設計引導---不要再盲目的new了!你要學著針對接口編程!(具體方法,Factory,Abstract Factory)

>>>  創業先鋒 眾人拾柴火焰高  >>> 簡體     傳統

   應園友提議,本篇博將幫助大家解決“針對接口編程”這一疑惑。而我所講的例子將從上一篇設計引導---一個鴨子游戲引發的設計(多態,繼承,抽象,接口,策略者模式)的案例中,延伸下來,讓大家更容易閱讀。

  上篇中有提到鴨子游戲。現在,假設那個鴨子游戲火了,火遍全球~~~公司大佬們因為這個游戲賺的盆滿缽滿,像憤怒的小鳥一樣:

                     

  現在公司下一步計劃!打造一個以游戲中鴨子個體為模型的玩具工廠!o(∩_∩)o

就像憤怒的小鳥毛絨玩具一樣~用這個比喻,大家應該會很簡單的想象模擬場景。

  下面來進入正題!漸進式描述,讓大家有個進階的梯度( ̄︶ ̄)↗ 。

  (oh fuck 下面這些內容 是我第二次寫!第一次寫的沒保存,IE死掉了!內牛滿面。。。。)

  看看玩具訂單:

  

復制代碼
 1        public DuckToy OrderToy()
 2        {
 3            DuckToy ducktoy = new DuckToy();
 4 
 5            ducktoy.Prepare();//準備
 6            ducktoy.Create();//開始制作
 7            ducktoy.Package();//包裝
 8            ducktoy.freight();//運送
 9 
10            return ducktoy;
11        }
復制代碼

這個方法很簡單,你僅僅只要關注new DuckToy(),針對一種鴨子玩具的生產!你還需要更多的鴨子模型!僅僅一種玩具,不足以打開市場!

下面改進一下:

復制代碼
 1        public DuckToy OrderToy(string type)
 2        {
 3            DuckToy ducktoy;
 4 
 5             if (type.Equals("MallarDuck"))
 6            {
 7                ducktoy = new MallarDuck();//綠頭鴨
 8            }
 9            else if (type.Equals("RedHaedDuck"))
10            {
11                ducktoy = new RedHaedDuck();//紅頭鴨
12            }
13            else if (type.Equals("DecoyDuck"))
14            {
15                ducktoy = new DecoyDuck();//誘餌鴨
16            }
17 
18            //還有更多的鴨子模型哦~    
19           //...
20 
21            ducktoy.Prepare();//準備
22            
23            ducktoy.Package();//包裝
24            ducktoy.freight();//運送
25 
26            return ducktoy;
27        }
復制代碼

 看到我最后實例化的注釋就知道,這根本不行,這種方式不是我們想要的。維護極度煩人。

把創建對象提取出來也許會不錯,可以把這個新對象稱作“工廠”呢:

復制代碼
 1    public class SimpleToyFactory
 2     {
 3        public DuckToy CreateToy(string type)
 4        {
 5            DuckToy ducktoy = null;
 6            if (type.Equals("MallarDuck"))
 7            {
 8                ducktoy = new MallarDuck();//綠頭鴨
 9            }
10            else if (type.Equals("RedHaedDuck"))
11            {
12                ducktoy = new RedHaedDuck();//紅頭鴨
13            }
14            else if (type.Equals("DecoyDuck"))
15            {
16                ducktoy = new DecoyDuck();//誘餌鴨
17            }
18 
19            return ducktoy;
20        }
21     }
復制代碼

就把代碼搬了一個地方,原來的問題還是沒有解決,繁瑣的問題依然存在。但這個移動也不能說完全沒好處,目前這個類只有一個OrderToy方法是它的客戶,以后還可以有很多的功能類,來用到這個“工廠”,比如獲取價錢或者描述,或者玩具列表....這個提取出來正是因為我們要把它從客戶的代碼中刪除。

  這個類也可以用靜態方法來定義,常稱為靜態工廠,方便是方便,就是不能再繼承來改變方法的行為了。

  好了,現在“工廠”方法做出來了,要重寫一下DuckToysStore了,OrderToy這個是DuckToysStore的,看你看完整的代碼,你能理解的:

復制代碼
 1  public class DuckToyStore
 2     {
 3        SimpleToyFactory factory;
 4        public DuckToyStore(SimpleToyFactory factory)
 5        {
 6            this.factory = factory;
 7        }
 8 
 9        public DuckToy OrderToy(string type)
10        {
11            DuckToy ducktoy;
12 
13            ducktoy = factory.CreateToy(type);
14          
15            ducktoy.Prepare();//準備
16            
17            ducktoy.Package();//包裝
18            ducktoy.freight();//運送
19 
20            return ducktoy;
21        }
22     }
復制代碼

當然,這只是OrderToy部分代碼,這看起來舒服多了~代碼中已經看不到new了,這從某種程度上來說,我們完成了一部分接口編程的知識了!

這個“工廠“已經做完了,就提取了一下代碼,我們可以把這個方法當作一種編程習慣,對,它不是工廠模式,它僅僅是一種較好的習慣。

  正因為有這個的存在,許多開發人員都會誤認為這就是工廠模式了。在下面我會介紹工廠模式并給予區別!

看個圖來輕松一下,理下剛才的關系:

通過簡單工廠的介紹,還沒有完哦,工廠繼續:

  玩具店,開的不錯,有很多加盟商了,隨之而來的是地域文化差異,不同的地方,玩具銷量也不同,比如在海邊,小鴨玩具就不能再做毛絨了, 那銷量肯定不好,小鴨游泳圈,小鴨船,會比毛絨玩具賣的好o(∩_∩)o ,更多想象只局限你思維啦~

  針對這個問題,玩具加盟商需要有自己的玩具類型,它們可以使用游戲公司的小鴨模型(專利化,一般沒有授權的,模仿小鴨模型會被追究的),但可以用不同的材質,一般的是毛絨,但是在海邊,塑料的才好賣,還可以有氣球樣式...。

繼續移植簡單工廠的做法:

1             BeiJingStyleFactory bjFactory = new BeiJingStyleFactory();
2             DuckToyStore bjStore = new DuckToyStore(bjFactory);
3             bjStore.OrderToy("MallarDuck");

這段代碼意思是:有一個北京樣式商店,它有自己的工廠,我要下訂單的話,只要讓DuckToyStore構造一個北京樣式商店,來處理我想要的”MallarDuck“,我就能得到一款,正在北京商店賣的“MallarDuck”。

當然,海南的樣式(HainanStyleFactory)商店,也可以這么做,代碼差不多

就是這代碼差不多!讓各地商家都捆綁在了你的創建模型上面了,你管不了他們做自己的特色,現在要做個框架。讓各地的玩具廠商,做自己的特色。

玩具變幻無窮~先來理一下關系。

  玩具廠商,就好比一個工廠,他生產各種各樣的玩具,但他想生產別人的專利玩具模型,就需要加盟,獲得持有專利公司的授權,這個很容易理解吧,然后他才可以名正言順的生產“鴨子玩具”。

  一個玩具工廠,他可以生產各種各樣的玩具,就意味著他可以自己制造自己的產品,他對于生產的玩具,有自己的理念,有自己的方法和創新:                     

 每種玩具,可以是相同的樣式,但需要不同的顏色去裝飾,以及配上什么裝飾品,這是玩具廠商的事,比如給專利“鴨子模型”配備一個小背包?裝飾一朵小花?小花的顏色設置,這些細節問題,專利公司他管理不了這么多,更多的制作權,還是在玩具工廠,玩具公司!

  公司需要一個自主的創建玩具的方法!現在重新設計一下,做個更有彈性的設計:

復制代碼
 1    public abstract class DuckToyStore
 2     {
 3 
 4        public DuckToy OrderToy(string type)
 5        {
 6            DuckToy ducktoy;
 7 
 8            //ducktoy = factory.CreateToy(type);
 9            ducktoy = CreateToy(type);//這里沒有new哦
10 
11            ducktoy.Prepare();//準備
12            ducktoy.Create();//開始制作
13            ducktoy.Package();//包裝
14            ducktoy.freight();//運送
15 
16            return ducktoy;
17        }
18 
19        public abstract DuckToy CreateToy(string type);//授予子類去new
20     }
復制代碼

把這個做成一個抽象類,讓繼承的子類去實現CreateToy(制造玩具),看個圖:

OrderToy()現在已經被分離出來了,它不知道哪些實際的具體類參與進來了,專業點,這就是解耦(decouple)

來具體實現一下:

實現之前,先做好商店內的具體模型:

復制代碼
//為了方便,放一起  
  class BeiJingStyleMallarDuck:DuckToy
    {
        public BeiJingStyleMallarDuck() {
            Console.WriteLine("初始化..");//傳統毛絨玩具
            name = "北京綠頭鴨";
            material = "傳統毛絨玩具";
            color = "白色";
        }
        
    }

    class BeiJingStyleRedHeadDuck : DuckToy
    {
        public BeiJingStyleRedHeadDuck() {
            Console.WriteLine("初始化...");
            name = "北京紅頭鴨";
            material = "傳統毛絨玩具";
            color = "綠色";
        }
    }

    class HainanStyleMallarDuck : DuckToy
    {
        public HainanStyleMallarDuck() {
            Console.WriteLine("初始化...");//靠近海邊的用塑料防水材質
            name = "海南綠頭鴨";
            material = "防水橡膠材質";
            color = "白色";
        }
    }

    class HainanStyleRedHeadDuck:DuckToy{
        public HainanStyleRedHeadDuck() {
            Console.WriteLine("初始化...");
            name = "海南紅頭鴨";
            material = "防水橡膠材質";
            color = "綠色";
        }
    }
復制代碼

 

現在可以做商店啦,雖然代碼有點多,但是都是相同的概念,在看代碼的時候,要構造好這些相互之間的關系:

復制代碼
 1         class BeiJingStyleStore: DuckToyStore 
 2         {//北京
 3           public override DuckToy  CreateToy(string item)
 4             {
 5                 if (item.Equals("MallarDuck"))
 6                 {
 7                     return new BeiJingStyleMallarDuck();
 8                 }
 9                 else if (item.Equals("RedHeadDuck"))
10                 {
11                     return new BeiJingStyleRedHeadDuck();
12                 }
13                 else
14                 {
15                     return null;
16                 }
17             }
18         }
19         class HaiNanStyleStore:DuckToyStore
20         {//海南
21             public override DuckToy CreateToy(string item)
22             {
23                 if (item.Equals("MallarDuck"))
24                 {
25                     return new HainanStyleMallarDuck();
26                 }
27                 else if (item.Equals("RedHeadDuck"))
28                 {
29                     return new HainanStyleRedHeadDuck();
30                 }
31                 else
32                 {
33                     return null;
34                 }
35             }
36         }
復制代碼


是不是很期待程序的結果??Main登場┈━═☆:

復制代碼
1 //親測可用哦o(∩_∩) 
2        static void Main(string[] args)
3         {
4             DuckToyStore bjStyleStore = new BeiJingStyleStore();//創建商店
5             bjStyleStore.OrderToy("MallarDuck");//下單
6 
7         }
復制代碼

代碼有沒有覺得很是簡單呢?

bjStyleStore.OrderToy("MallarDuck");  --- 這段代碼的參數也許會引起不好的爭論,為了方便,我就寫個String了,用個Enum來集合玩具樣式是個不錯的方法。

結果:

有點感觸嗎?例子都講完了~ 看下詳細的定義,現在你的頭腦里應該會有兩個結構關系,像這樣:

一個創建類,一個產品類,工廠模式通過子類決定該創建的對象是什么,來達到將對象創建的過程封裝的目的。

正式定義,工廠方法模式:

 

  定義了一個創建對象的接口,但由子類決定要實列化的類是哪一個。

  工廠方法讓類把實例化推遲到子類。

工廠方法讓子類決定實列化哪一個類,不需要知道實際創建的產品是哪一個。

選擇了使用哪個子類,自然就決定了實際創建的產品是什么。現在可以隨意的擴展你的工廠了,比如加一個上海的商店,自定義自己的玩具模型~都很輕松啦。

現在說說簡單工廠工廠方法,大家現在應該覺得,子類看起來很像簡單工廠。

簡單工廠把全部的事情,在一個地方都處理完了,而工廠方法卻是創建一個框架,讓子類決定要如何實現。

簡單工廠可以將對象封裝起來,但是簡單工廠不具備工廠方法的彈性,因為簡單工廠不能改變正在創建的產品。

例子的前部分從依賴具體對象,講到依賴接口實例化對象,具體設計原則如下:

要依賴抽象,不要依賴具體

對于這一原則,怎么去判斷,提及一個正式的名稱去區別:依賴倒置原則

什么是依賴?比如簡單工廠~典型的依賴。

看看倒置:

倒置指的是和一般面向對象設計的思考方式完全相反。不好理解?嘿,絕對讓你懂。

問:“現在你需要實現一個上海玩具店,你第一件事情想到什么?”

小花:“我要自己創建自己的風格樣式玩具!比如:咧嘴的綠頭鴨,憤怒的綠頭鴨...”

問:“現在你的思維正在順勢從頂端思考,現在倒置一下,先考慮具體的模型玩具!”

小花:“我的小鴨玩具們,都要共享一個鴨子接口。”

問:“Yes,你想到一個抽象的鴨子接口,那么你再想想,如何設計玩具店?”

小花:“噢!我現在要關心的是玩具店的樣式,而不是咧嘴的綠頭鴨(具體的模型)。”

---總結:

  你要做具體的模型,就必須靠一個工廠將這些具體模型抽象出來,然后各種具體的模型都會依賴一個抽象(鴨子接口),而你的玩具商店也會依賴這個抽象接口。

現在不僅僅倒置了一個商店依賴具體類的設計,而且也倒置了你的思考方式,下面有幾點指導意見,幫助你避免在面向對象設計中違反依賴倒置原則:

  • 變量不能持有具體類的引用,就像訂單方法代碼中,你看不到new一樣。
  • 不要讓派生自具體類,要派生就派生抽象類abstract
  • 不要覆蓋基類中已實現的方法,除非你要覆蓋的是比較特殊的一部分代碼。

 抽象工廠模式:

  提供一個接口,用于創建相關或依賴對象的家族,而不需要明確置頂具體類。

注意這個抽象工廠模式工廠方法不是一個概念,他們有不同的定義,再次提醒,他們不是同一個模式,但是又有很多交織在一起的枝節。

抽象工廠的方法經常以工廠方法的方式實現。抽象工廠的任務是定義一個負責創建一組產品的接口。

這個接口內的每個方法都負責創建一個具體的產品,還沒完,然后再利用實現抽象工廠的子類來提供這些具體的做法。

也就是說,抽象工廠中利用工廠方法實現生產的具體做法。

來看看抽象工廠的代碼吧,畢竟看了這么多知識了,比較比較代碼也是能促進學習的。

我偷懶了,只完成了一個實例,目的很簡單,想讓大家看到抽象工廠的實現:

先看具體DuckToy,修改版:

復制代碼
 1    public abstract class DuckToy
 2     {
 3        //都是封裝好的類型
 4        public FactroyName name;
 5        public FactroyMaterial material;
 6        public FactroyColor color;
 7 
 8        public abstract void Prepare(); //準備工作 現在是抽象方法
 9 
10        public void Create()
11         {
12             Console.WriteLine("正在生產...(預計30分鐘)");
13         }
14 
15        public void Package()
16         {
17             Console.WriteLine("正在包裝...(預計2分鐘)");
18         }
19        public void Freight()
20         {
21             Console.WriteLine("正在運送...");
22         }
23 
24     }
復制代碼

再看封裝類型的字段:

復制代碼
//開始會難以理解。 - -。既然是工廠,那么工廠就有統一的標準,不允許用戶自行修改  
  public class FactroyMaterial
    {
        public FactroyMaterial()//為了簡單,我在這直接用構造函數,其實你還可以自己定義自己的方法,下面的幾個類都可以自己擴展  
      {
            Console.WriteLine("材料:毛絨");
        }
    }
    public class FactroyName
    {
        public FactroyName()
        {
            Console.WriteLine("名字:北京綠頭鴨");
        }
    }
    public class FactroyColor
    {
        public FactroyColor()
        {
            Console.WriteLine("顏色:白色");
        }
    }
復制代碼

上面的代碼是北京商店特有的,為什么這么說? 看看這個:

復制代碼
 1     public class BJToyStroeFactory: ToysFactory
 2     {//北京商店特有的材料 名字 顏色
 3         public FactroyMaterial ReturnMaterial() 
 4         {
 5             return new FactroyMaterial();
 6         }
 7         public FactroyName ReturnName()
 8         {
 9             return new FactroyName();
10         }
11         public FactroyColor ReturnColor()
12         {
13             return new FactroyColor();
14         }
15     }
復制代碼

現在又出來一個ToysFactory,接著就是抽象工廠的接口啦:

   public interface ToysFactory//通用接口方法
    {
        FactroyMaterial ReturnMaterial();
        FactroyName ReturnName();
        FactroyColor ReturnColor();
    }

底層都寫好了,好好好,現在要看具體的鴨子模型!

復制代碼
 1     class BeiJingStyleMallarDuck:DuckToy
 2     {
 3         ToysFactory toysfactory;
 4         public BeiJingStyleMallarDuck(ToysFactory tosyfactory) 
 5         {
 6             this.toysfactory = tosyfactory;
 7         }
 8        public override void  Prepare()//實現
 9         {
10             Console.WriteLine("開始準備...");
11             material = toysfactory.ReturnMaterial();
12             name = toysfactory.ReturnName();
13             color = toysfactory.ReturnColor();
14         }  
15     }
復制代碼

繼承下來的封裝類型,通過Prepare,從工廠獲取實物的信息。別著急,我們再向上一層。

復制代碼
 1 //重寫了 BeiJingStyleStore   
 2      class BeiJingStyleStore: DuckToyStore 
 3         {
 4             DuckToy toy = null;
 5             ToysFactory toysfactory = new BJToyStroeFactory();
 6           public override DuckToy  CreateToy(string item)
 7             {
 8                 if (item.Equals("MallarDuck"))
 9                 {
10                     toy = new BeiJingStyleMallarDuck(toysfactory);
11                     return toy;
12                 }//偷懶了~~~~就寫一個toy
13                 else
14                 {
15                     return null;
16                 }
17             }
18         }
復制代碼

都改了這么多了,看看Main要改什么嗎?

1         static void Main(string[] args)
2         {
3             DuckToyStore bjStyleStore = new BeiJingStyleStore();
4             bjStyleStore.OrderToy("MallarDuck");
5         }
6 //什么都沒改^_^

 改了這么多,Main都不需要改,OrderToy所在的DuckToyStore也不用改,但是它的流程已經脫胎換股了。

結果差點忘記貼:

我寫了這么多不僅僅是一個工廠模式,還有一個抽象工廠模式。

看看下面兩張圖比較一下吧,這兩種模式的結構

 

-----------------------------------------------------------------------------------------------------------

下面是工廠方法:

工廠是很有威力的技巧,幫助我們針對抽象編程,而不需要針對具體編程~

原例子---《Head First 設計模式》--工廠模式:披薩店。

對設計有興趣的朋友,強烈推薦拿下此書。磨刀不誤砍柴工~~~^_^


肅 2013-08-31 20:16:18

[新一篇] 三國殺Online

[舊一篇] Tawkon.com:測量手機輻射
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表