[设计模式-5] 代理者模式

话三国~代理者模式


王司徒巧使连环计

话说董卓自从18路诸侯联军退却后,愈加嚣张跋扈,不但自称尚父且出入更是用天子仪仗,几乎是以天子自居。朝中当然不乏汉室忠臣,其中以王允为首,已是极度不满。但岂奈手中无兵权自己又是文官,且董卓帐下吕布骁勇无敌,因此也无所作为。因此终日郁郁寡欢。

有一夜,王允在家中庭院散步,忽闻在牡丹亭畔有一女子长吁短叹。允轻声靠近想知道是谁,一看,居然是自己的义女貂蝉,王允大怒喝道,一个妇道人家在此长吁短叹,莫非有私情也?貂蝉惊,但仍缓缓道:我深受义父大人厚恩,无以回报,但见义父终日眉头深锁,必定忧心国家大事,却不知如何协助,因此在此叹息,如有用得到小女之处,万死不辞。王允突然灵光一闪,大喜道:殊不知天下掌握在你手中呀!快随我来。貂蝉不明白,但仍随之而去。一入室中,王允突然拜倒于地,貂蝉吓得魂不附体,立刻也跪拜在地说:义父为何如此?王允泪流满面道:百姓有倒悬之危,君臣有累卵之急,贼臣董卓想窜汉自立,朝中文武无计可施。董卓有一义子名叫吕布,乃天下第一勇将,但我发现他父子二人皆好色之徒,我想要使美人计,先将你下嫁吕布,再献给董卓,你从中作梗让他二人反目,唆使吕布杀死董卓,以绝大恶。若能重建汉室光我河山,皆是你的功劳!你意下如何?貂蝉听罢俯身于地:义父大恩无以回报,我万死不辞!王允拜谢。按下不表。

看完以上故事,突然觉得这不就是典型的代理者模式吗?王允的计策就是要透过貂蝉代理吕布刺杀董卓。所以就用这个故事来看看代理模式是如何运行的八。

    interface  IAssassin : IKillRequest
    {
         void Kill();
    }

代理者模式的组成大致上可分为两类,proxy(代理者)类以及real object(被代理)的类,在这里定义一个共通的界面是要确保任何一个可以使用real object类的地方都可以使用proxy类(<=很重要)。依照故事情节,定义 了一个kill方法,这方法主要是要执行刺杀董卓的功能,因为是界面,所以并没有实践。

    /// 
    /// 吕布
    /// 
    class Assassin :IAssassin
    {

        public override void Kill()
        {
            Console.WriteLine("吕布刺杀董卓");
        }
    }

接下来要定义real object(吕布),然后实践Kill方法。以本篇故事来说,吕布是唯一有能力刺杀董卓的男人,因此只有它具备kill方法的实践。

    /// 
    /// 貂蝉
    /// 
    class AssassinProxy : IAssassin
    {
        protected IAssassin _assassin;

        public AssassinProxy(IAssassin assassin)
        {
            _assassin = assassin;
        }

        public override void Kill()
        {
            _assassin.Kill();
            break;
         }
    }

再来定义貂蝉,貂蝉需要拥有real object(被代理者)类的参考,不然貂蝉会不知道她要叫谁杀董卓,这个参考的限制便是要具备"刺杀董卓"方法的男人,在本例中,当然是三国战神吕布啰。

        static void Main(string[] args)
        {
            var 吕布 = new Assassin();
            AssassinProxy 貂蝉 = new AssassinProxy(吕布);
            貂蝉.Kill();

            Console.WriteLine();
            Console.Read();
        }

完成了,来实际跑跑看吧,我们声明了吕布(被代理者),接着声明貂蝉(proxy),把吕布(被代理者)传入当参数,接着执行kill命令。

程序输出:
吕布刺杀董卓

定义

代理者模式=>为一个对象提供代理以控制这个对象的访问

结束了吗?当然还没,如果只是这样应用,那proxy类似乎没啥意义,直接调用吕布(被代理者)的类,不是更省事吗?因为如果仔细看故事的话,这段程序有两件事不能阐述,第一件事就是王允请貂蝉代理吕布刺杀董卓(这里注意一下,实际刺杀董卓的还是吕布不是貂蝉唷),那如果曹操请貂蝉做同样的事,貂蝉会答应吗?恩,权限问题这里没有表达出来;再来第二件事,貂蝉有实施美人计,这个情节也在程序看不到。

由此可见proxy类除了执行原本real object的方法之外,它还可以额外再扩充一些行为。除此之外,衍伸应用代理模式也可使用在权限控管上,下面就来写一个示范的例子。

    /// 
    /// 唆使人
    /// 
    class Person
    {
        public string name="";
        public Person(string strName)
        {
            name=strName;
        }
    }

我们在这里定义一个Person(唆使人)的类,很简单只具备一个name的属性,来区别这个人到底是谁。

    /// 
    /// 貂蝉
    /// 
    class AssassinProxy : IAssassin
    {
        protected IAssassin _assassin;
        protected Person _man;


        public AssassinProxy(IAssassin assassin,Person man )
        {
            _assassin = assassin;
            _man=man;
        }

        public override void Kill()
        {
            switch (_man.name)
            {
                case "曹操":
                    Console.WriteLine("我跟你很熟? 再不离开我就要叫啰!");
                    break;
                case "王允":
                    Console.WriteLine("义父大恩,无以回报!貂蝉施展美人计");
                    _assassin.Kill();
                    break;
            }
        }
    }

貂蝉类新增了一个_man的属性来存放person(唆使人),并在构造函数多一个person(唆使人)的参数。以故事情节来说 ,王允想要执行吕布刺杀董卓的方法 ,透过貂蝉 ,貂蝉会判断 王允是他的义父,所以他有权限执行此要求,所以在执行吕布的刺杀方法时,还会额外使出美人计诱惑吕布;相反的,如果是曹操跟貂蝉说"执行吕布刺杀董卓的方法",貂蝉就会判断曹操没权限 而大声尖叫,因为貂蝉并不认识曹操。所以,除非person(唆使人)的name是王允,否则,貂蝉不执行吕布(被代理者)的kill方法,借此来达到权限控管的目的。

        static void Main(string[] args)
        {
            var 王允 = new Person("王允");
            var 吕布 = new Assassin();
            AssassinProxy 貂蝉 = new AssassinProxy(吕布,王允);
            貂蝉.Kill();

            Console.WriteLine();
            Console.Read();
        }

最后再看看程序执行的情况,我们声明了吕布(被代理者),王允(唆使人)两个类,接着声明貂蝉(proxy),把吕布(被代理者)和王允(唆使人)传入当参数,接着执行kill命令。

程序输出:
义父大恩,无以回报!貂蝉施展美人计
吕布刺杀董卓

        static void Main(string[] args)
        {
            var 曹操 = new Person("曹操");
            var 吕布 = new Assassin();
            AssassinProxy 貂蝉 = new AssassinProxy(吕布,曹操);
            貂蝉.Kill();

            Console.WriteLine();
            Console.Read();
        }

接着依样画葫芦,声明吕布(被代理者),曹操(唆使人)两个类,再声明貂蝉(proxy),把吕布(被代理者)和曹操(唆使人)传入当参数,接着执行kill。

程序输出:
我跟你很熟? 再不离开我就要叫啰!

成功!!确实达到了权限控管的效果。