[設計模式-5] 代理者模式

話三國~代理者模式

王司徒巧使連環計

話說董卓自從18路諸侯聯軍退卻後,愈加囂張跋扈,不但自稱尚父且出入更是用天子儀仗,幾乎是以天子自居。朝中當然不乏漢室忠臣,其中以王允為首,已是極度不滿。但豈奈手中無兵權自己又是文官,且董卓帳下呂布驍勇無敵,因此也無所作為。因此終日鬱鬱寡歡。

有一夜,王允在家中庭院散步,忽聞在牡丹亭畔有一女子長吁短嘆。允輕聲靠近想知道是誰,一看,居然是自己的義女貂蟬,王允大怒喝道,一個婦道人家在此長吁短嘆,莫非有私情也?貂蟬驚,但仍緩緩道:我深受義父大人厚恩,無以回報,但見義父終日眉頭深鎖,必定憂心國家大事,卻不知如何協助,因此在此嘆息,如有用得到小女之處,萬死不辭。王允突然靈光一閃,大喜道:殊不知天下掌握在你手中呀!快隨我來。貂蟬不明白,但仍隨之而去。一入室中,王允突然拜倒於地,貂蟬嚇得魂不附體,立刻也跪拜在地說:義父為何如此?王允淚流滿面道:百姓有倒懸之危,君臣有累卵之急,賊臣董卓想竄漢自立,朝中文武無計可施。董卓有一義子名叫呂布,乃天下第一勇將,但我發現他父子二人皆好色之徒,我想要使美人計,先將你下嫁呂布,再獻給董卓,你從中作梗讓他二人反目,唆使呂布殺死董卓,以絕大惡。若能重建漢室光我河山,皆是你的功勞!你意下如何?貂蟬聽罷俯身於地:義父大恩無以回報,我萬死不辭!王允拜謝。按下不表。

看完以上故事,突然覺得這不就是典型的代理者模式嗎?王允的計策就是要透過貂蟬代理呂布刺殺董卓。所以就用這個故事來看看代理模式是如何運作的八。

    interface  IAssassin : IKillRequest
    {
         void Kill();
    }

代理者模式的組成大致上可分為兩類,proxy(代理者)類別以及real object(被代理)的類別,在這裡定義一個共通的介面是要確保任何一個可以使用real object類別的地方都可以使用proxy類別(<=很重要)。依照故事情節,定義 了一個kill方法,這方法主要是要執行刺殺董卓的功能,因為是介面,所以並沒有實作。

    /// <summary>
    /// 呂布
    /// </summary>
    class Assassin :IAssassin
    {

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

接下來要定義real object(呂布),然後實作Kill方法。以本篇故事來說,呂布是唯一有能力刺殺董卓的男人,因此只有它具備kill方法的實作。

    /// <summary>
    /// 貂蟬
    /// </summary>
    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的方法之外,它還可以額外再擴充一些行為。除此之外,衍伸應用代理模式也可使用在權限控管上,下面就來寫一個示範的例子。

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

我們在這裡定義一個Person(唆使人)的類別,很簡單只具備一個name的屬性,來區別這個人到底是誰。

    /// <summary>
    /// 貂蟬
    /// </summary>
    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。

程式碼輸出:
我跟你很熟? 再不離開我就要叫囉!

成功!!確實達到了權限控管的效果。