設計模式系列-策略模式


      今天下班回家,吃完晚飯后在網上閑逛,看了看電視劇《男人幫》的經典台詞,感覺很經典,雖然這個電視劇我早已經在上個月就看完了,但是看了看里面的經典開場白和台詞,還是不由自主的伸出大拇指,贊啊!

       列舉部分台詞如下:

      (1)自從我們睜開眼睛看這個世界的第一天起, 我們看到的就只有兩種人,男人和女人。 他們分屬於兩大不同的陣營,為了徹底收服對方,大家互相往死里掐。
      (2)根據一個遙遠的傳說,說有一種東西叫愛情,可以徹底終結這場戰爭。 於是大家紛紛的趕緊去尋找,趕緊去幻想, 找到頭破血流才發現,原來這個感情也是另一些人在書房里炮制出來的。 於是大家更加絕望,更加痛苦,更加互相的往死里掐。 

        1、場景案例
       尤其是上面這幾句,讓我想起了我一同事(這里用“某某哥”代替)和他女朋友的故事(這里用“某某嫂”代替),一次活動中,某某哥認識了某某嫂,那個一見鍾情呀,簡直是看不見某某嫂就吃不下飯、寫不下代碼呀,在追求中也沒少費工夫。比如:送小禮物,請客吃飯,搞浪漫等等,我們就把這幾個階段分別用代碼模擬一下把!

           ①第一階段贈送小禮物代碼如下:

        // 第一階段 送小禮物
         public  static  void SendGift()
        {
            Console.WriteLine( " 送給女方精美小禮物! ");
        }

         static  void Main( string[] args)
        {
             // 第一階段 
            SendGift();
        }

           ② 此時、通過送精美小禮物女方已經願意與男方接觸,這時就可以一起吃飯了,代碼如下:

         // 第一階段 送小禮物
         public  static  void SendGift()
        {
            Console.WriteLine( " 送給女方精美小禮物! ");
        }

         // 添加 第二階段 請客吃飯
         public  static  void Eat()
        {
            Console.WriteLine( " 請女生吃牛排! ");
        }

         static  void Main( string[] args)
        {
             // 第一階段 
            
// SendGift();  // 此時第一階段已經不需要了所以注銷掉進入第二階段

            
// 第二階段
            Eat();
        }

         ③ 經過之前階段的接觸,兩人已經在一起了,在一起就免不了在熱戀中經歷浪漫情節,第三階段:制造浪漫、代碼如下:

       // 第一階段 送小禮物
         public  static  void SendGift()
        {
            Console.WriteLine( " 送給女方精美小禮物! ");
        }

         // 添加 第二階段 請客吃飯
         public  static  void Eat()
        {
            Console.WriteLine( " 請女生吃牛排! ");
        }

         // 添加 第三階段 浪漫:燭光晚餐等待女友
         public  static  void Romantic()
        {
            Console.WriteLine( " 精心制作了紅酒、晚餐與蠟燭,等待女友回家! ");
        }

         static  void Main( string[] args)
        {
             // 第一階段 
            
// SendGift();  // 此時第一階段已經不需要了所以注銷掉進入第二階段

            
// 第二階段
            
// Eat();  // 此時第二階段也已經經過,所以注銷掉、進入第三階段

            
// 第三階段
            Romantic();
        }

         看了某某哥追某某嫂的經過后,我們會發現,每當進入一個新階段的時候,我們就需要去修改我們的代碼,加入新的追女策略,並且將main函數中的調用修改成進入新的階段,那么我們怎么樣能避免,在進入新階段后不去修改調用的代碼呢?當然這個肯定難不倒你,代碼如下:

        // 第一階段 送小禮物
         public  static  void SendGift()
        {
            Console.WriteLine( " 送給女方精美小禮物! ");
        }

         // 添加 第二階段 請客吃飯
         public  static  void Eat()
        {
            Console.WriteLine( " 請女生吃牛排! ");
        }

         // 添加 第三階段 浪漫:燭光晚餐等待女友
         public  static  void Romantic()
        {
            Console.WriteLine( " 精心制作了紅酒、晚餐與蠟燭,等待女友回家! ");
        }

         static  void Main( string[] args)
        {
            Console.Write( " 現在是第幾階段?: ");
             string level = Console.ReadLine();  // 代表當前是哪個階段
             switch (level)
            {
                 case  " one ": // 第一階段 
                    SendGift();
                     break;
                 case  " two ": // 第二階段
                    Eat();
                     break;
                 case  " three "// 第三階段
                    Romantic();
                     break;
                 default:
                    Console.WriteLine( " 沒這個打算湊什么熱鬧? ");
                     break;
            }
        }

          這樣我們就解決了在兩人進入新的階段后,無需修改main方法中的代碼!輸出結果如下:

  

         那么如果這時兩人已經到了談婚論嫁的時候,該怎么辦呢?也就是要在新加入一個階段叫做結婚呢?

          2、引入策略模式

         普通的辦法還是要去新增方法,在修改調用代碼!那我們有沒有辦法呢?別着急,先看看策略模式、定義如下:“它定義了算法家族,分別封裝起來,讓他們之間可以互相替換,次模式讓算法的變化,不會影響到使用算法的客戶”。

          策略模式類圖如下:

         那么使用策略模式改裝,我們上面場景的類圖如下:

          模擬出了類型那么我們寫代碼當然也就不難啦。

          ① 首先呢,我們需要抽象出來追女孩策略,這個策略里面有一個公共的接口就是去做(也就是行動的意思),例如:送禮物、吃飯、浪漫,都是需要人去做去行動的。

     // 追女孩策略抽象類
     public  abstract  class PursueaGirlStrategy
    {
         // 追女孩策略中都有一個統一的接口,就是行動。
         public  abstract  void Justdoit();
    }

         ② 接下來,就是追女孩策略中,各個環節策略的實現啦,代碼如下:

  // 第一階段 送小禮物
     public  class SendGiftStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 送給女方精美小禮物! ");
        }
    }

     // 第二階段 請客吃飯
     public  class EatStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 請女生吃牛排! ");
        }
    }

     // 第三階段 浪漫:燭光晚餐等待女友
     public  class RomanticStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 精心制作了紅酒、晚餐與蠟燭,等待女友回家! ");
        }
    }

         ③ 最后就是,承載策略實例,提供統一調用的策略上下文啦。代碼如下:

  // 策略的宿主 承載當前需要執行的策略
     public  class PursueaGirlContext
    {
        PursueaGirlStrategy staregy;
         public PursueaGirlContext(PursueaGirlStrategy strategy)
        {
             this.staregy = strategy;
        }

         // 調用策略的公共接口方法
         public  void ContextJustdoit()
        {
             if ( this.staregy !=  null)
            {
                 this.staregy.Justdoit();
            }
        }

    }

       main方法將來調用如下:

   static  void Main( string[] args)
        {
             // 定義追女孩策略上下文對象
            PursueaGirlContext context =  null;
            Console.Write( " 現在是第幾階段?: ");
             string level = Console.ReadLine();  // 代表當前是哪個階段
             switch (level)
            {
                 case  " one ": // 第一階段 
                    context =  new PursueaGirlContext( new SendGiftStrategy());
                     break;
                 case  " two ": // 第二階段
                    context =  new PursueaGirlContext( new EatStrategy());
                     break;
                 case  " three "// 第三階段
                    context =  new PursueaGirlContext( new RomanticStrategy());
                     break;
                 default:
                    Console.WriteLine( " 沒這個打算湊什么熱鬧? ");
                     break;
            }

             // 統一的策略調用模式
            context.ContextJustdoit();
        }

          好的,策略模式與我們的場景先告一段落把。這個時候可能有的人就會問啦,策略模式跟工廠模式很想啊,都是提供一個統一的接口,有不同的實例去實例化。那么我們來看看策略模式與工廠模式的區別吧。

          3、策略模式與工廠模式的區別

          概念上理解:

                     ① 策略模式:是告訴上下文,我需要去做什么,至於使用怎么實現,需要上下文去使用當前對應的策略去實現。例如:項目經理(客戶端)說:“我要實現即時消息功能”,那么我們程序員(上下文)就需要去尋找對應的策略(開發即時消息模塊的策略)去實現。

                     ②工廠模式:是告訴工廠,此時我需要什么部件你去給我制造,例如:開發及時消息模塊,我需要JS的彈出框,我就會告訴UI組(工廠),給我做一個JS彈出框,要求模仿EXT的。

          4、策略與工廠談戀愛

           那么了解了他們之間的區別后,我們再來看上面策略模式實現的一個問題,例如:需要加入結婚策略時,還是需要增加結婚策略類,並且修改客戶端(MAIN方法)中的調用代碼。那么我們把提供使用策略的代碼放到工廠中呢?這樣我們以后就只需要增加新策略修改工廠就行啦!客戶端就不用修改了。

           ①工廠代碼代碼如下:

  // 提供策略的工廠
     public  class FactoryStrategy
    {
         // 根據客戶端參數創建策略
         public  static PursueaGirlContext GetStrategy( string level)
        {
             // 定義追女孩策略上下文對象
            PursueaGirlContext context =  null;
             switch (level)
            {
                 case  " one ": // 第一階段  禮物
                    context =  new PursueaGirlContext( new SendGiftStrategy());
                     break;
                 case  " two ": // 第二階段 吃飯
                    context =  new PursueaGirlContext( new EatStrategy());
                     break;
                 case  " three "// 第三階段 浪漫
                    context =  new PursueaGirlContext( new RomanticStrategy());
                     break;
                 case  " four ": // 第四階段 結婚
                    context =  new PursueaGirlContext( new MarriedStrategy());
                     break;
                 default:
                    context =  null;
                     break;
            }
             return context;
        }
    }

          ② 有了工廠后的客戶端(main)代碼如下:

    static  void Main( string[] args)
        {
           
            Console.Write( " 現在是第幾階段?: ");
             string level = Console.ReadLine();  // 代表當前是哪個階段

            
// 定義追女孩策略上下文對象
            PursueaGirlContext context = FactoryStrategy.GetStrategy(level);

             // 統一的策略調用模式
            context.ContextJustdoit();
        }

         ③ 完整代碼如下:

// 追女孩策略抽象類
     public  abstract  class PursueaGirlStrategy
    {
         // 追女孩策略中都有一個統一的接口,就是行動。
         public  abstract  void Justdoit();
    }

     // 第一階段 送小禮物
     public  class SendGiftStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 送給女方精美小禮物! ");
        }
    }

     // 第二階段 請客吃飯
     public  class EatStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 請女生吃牛排! ");
        }
    }

     // 第三階段 浪漫:燭光晚餐等待女友
     public  class RomanticStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 精心制作了紅酒、晚餐與蠟燭,等待女友回家! ");
        }
    }

     // 添加第四階段 結婚
     public  class MarriedStrategy : PursueaGirlStrategy
    {
         public  override  void Justdoit()
        {
            Console.WriteLine( " 兩人民政局領證! ");
        }
    }

     // 提供策略的工廠
     public  class FactoryStrategy
    {
         // 根據客戶端參數創建策略
         public  static PursueaGirlContext GetStrategy( string level)
        {
             // 定義追女孩策略上下文對象
            PursueaGirlContext context =  null;
             switch (level)
            {
                 case  " one ": // 第一階段  禮物
                    context =  new PursueaGirlContext( new SendGiftStrategy());
                     break;
                 case  " two ": // 第二階段 吃飯
                    context =  new PursueaGirlContext( new EatStrategy());
                     break;
                 case  " three "// 第三階段 浪漫
                    context =  new PursueaGirlContext( new RomanticStrategy());
                     break;
                 case  " four ": // 第四階段 結婚
                    context =  new PursueaGirlContext( new MarriedStrategy());
                     break;
                 default:
                    context =  null;
                     break;
            }
             return context;
        }
    }

     // 策略的宿主 承載當前需要執行的策略
     public  class PursueaGirlContext
    {
        PursueaGirlStrategy staregy;
         public PursueaGirlContext(PursueaGirlStrategy strategy)
        {
             this.staregy = strategy;
        }

         // 調用策略的公共接口方法
         public  void ContextJustdoit()
        {
             if ( this.staregy !=  null)
            {
                 this.staregy.Justdoit();
            }
        }

    }
     static  void Main( string[] args)
        {
           
            Console.Write( " 現在是第幾階段?: ");
             string level = Console.ReadLine();  // 代表當前是哪個階段

            
// 定義追女孩策略上下文對象
            PursueaGirlContext context = FactoryStrategy.GetStrategy(level);

             // 統一的策略調用模式
            context.ContextJustdoit();
        }

        這樣我們以后如果有新的策略只需要增加新的策略類,修改工廠即可,那么細心的朋友問了,這樣只不過是把修改客戶端移到工廠里面了,將來還是需要工廠,沒關系徹底解決調用修改的話,我們將來可以利用反射!以后不管修改還是新增我們都能很好的去應對了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM