設計模式學習之Factory Method模式和Abstract Factory模式
終于到了工廠模式了,說起工廠模式,不得不把工廠方法模式和抽象工廠模式結合起來說,這兩種模式都有工廠,乍聽起來還真容易混淆,但它們卻是不相同的兩種模式,但又互相有聯系。那么這兩者之間各有什么用途,互相之間又有什么聯系呢?一個一個來吧。
既然說起了工廠模式,那么首先有一點是需要搞清的,那就是,所有的工廠模式都是為了將對象的創建過程封裝起來,要么 將創建過程隔離出來 ,要么 將創建過程抽象成接口。
那么,在說這兩種模式之前,先說一下簡單工廠,所謂的簡單工廠,其實就是一種最簡單的將創建過程隔離的設計方法。我們通常在創建一個對象時,都會用到如下語句:
Object o=new Object();
即new一個對象,但這種做法是最最基本最最原始的創建對象的方法,在面向對象的設計中,我們必須要遵循一個原則: 要依賴抽象,不要依賴具體類 。所以,這種做法因為太過具體,所以我們得想辦法把它抽象出來,怎么抽象呢,用簡單工廠可以這樣實現:
1 public class Client 2 { 3 public void do() 4 { 5 Object o=SimpleFactory.createObject(); 6 } 7 } 8 public class SimpleFactory 9 { 10 public static Object createObject() 11 { 12 Object o=new Object(); 13 return o; 14 } 15 }
咋看起來變化不大,不就是吧new Object改為一個類的靜態方法了嗎(其實也可以用非靜態方法,不過需要先實例化對象),但這樣做卻可以適應需求的改變,比如說,我現在想要根據不同的類型創建不同的對象,如果直接用new方法,則會在do方法里面寫上很多歌new Object()的語句,而且還有很多的判斷,每當新增加一種類型,就得改動Client類代碼。而如果將創建對象的過程用簡單工廠封裝起來,則只需要改動簡單工廠里面的代碼:
1 //直接用new方法 2 public class Client 3 { 4 public void do(String type) 5 { 6 Object o; 7 switch(type) 8 { 9 case "type1":o=new Object1();break;//Object1、Object2都是Object的子類 10 case "type2":o=new Object2();break; 11 //... 12 } 13 } 14 } 15 16 //采用簡單工廠封裝創建過程 17 public class Client 18 { 19 public void do(String type) 20 { 21 Object o=SimpleFactory.createObject(type); 22 } 23 } 24 public class SimpleFactory 25 { 26 public static Object createObject(String type) 27 { 28 Object o; 29 switch(type) 30 { 31 case "type1":o=new Object1();break;//Object1、Object2都是Object的子類 32 case "type2":o=new Object2();break; 33 //... 34 } 35 return o; 36 } 37 }
但這樣做還是會帶來不便,因為還是要改動代碼,增加新的判斷分支,怎樣才能更好的封裝以增強可維護性和可擴展性呢,工廠方法模式和抽象工廠模式就可以大大增強簡單工廠的作用。
那么首先說說工廠方法模式,先說基本概念吧
- 工廠方法模式:
定義一個用于創建對象的接口,讓子類決定實例化哪一個類。Factory Method 使一個類的實例化延遲到其子類。
- 適用性:
- 當一個類不知道它所必須創建的對象的類的時候。
- 當一個類希望由它的子類來指定它所創建的對象的時候。
- 當類將創建對象的職責委托給多個幫助子類中的某一個,并且你希望將哪一個幫助子類是代理者這一信息局部化的時候。
工廠方法模式中,最重要的一點是“讓子類決定實例化那一個類”,這一點怎么才能做到呢,而且這樣做有什么好處呢?
還是舉例子最好了,對于工廠模式,能舉的最好的例子當然是與生產產品有關的了,現在,我們假定生產一種手機,那么我們知道,每種手機都需要很多零部件,然后進行組裝,打磨,包裝等等,對于手機最主要的零件cpu來說,就有不同類型的cpu,比如高通,intel,蘋果,德州儀器,或者mtk等等,那么我們在生產手機時,就需要使用某種芯片,就相當于創建這些對象。但不同手機可以使用不同的芯片,如果采用簡單工廠來生產這些芯片,就像下面一樣:
1 public class Cellphone 2 { 3 Cpu cpu; 4 Memory memory; 5 Screen screen; 6 public void getCpu(); 7 public void makePhone(String phoneType) 8 { 9 switch(phoneType) 10 { 11 case "iphone4s":cpu=CpuFactory.createCpu("apple A5");break; 12 case "三星i9100":cpu=CpuFatory.createCpu("高通");break; 13 case "摩托羅拉me525":cpu=CpuFactory.createCpu("高通");break; 14 case "華為8500":cpu=CpuFactory.createCpu("德州儀器");break; 15 case "諾基亞":cpu=CpuFactory.createCpu("intel");break; 16 //... 17 } 18 } 19 public void package(); 20 } 21 22 public class CpuFactory 23 { 24 public static Cpu createCpu(String cpuType) 25 { 26 Cpu cpu; 27 switch(cpuType) 28 { 29 case "apple A5":cpu=new AppleCpu("A5");break; 30 case "apple A5X":cpu=new AppleCpu("A5X");break; 31 case "高通1代":cpu=new QualcommCpu("1");break; 32 case "高通2代":cpu=new QualcommCpu("2");break; 33 case "德州儀器":cpu=new TiCpu();break; 34 case "intel":cpu=new IntelCpu();break; 35 //... 36 } 37 } 38 }
那么,如果增加新的cpu,就要在CpuFactory里面再增加新的判斷分支。而如果使用工廠方法模式,就可以這樣:
1 public class Cellphone 2 { 3 protected String type; 4 protected Cpu cpu; 5 protected Memory memory; 6 protected Screen screen; 7 protected abstract Cpu getCpu(); 8 public Cellphone(String type) 9 { 10 this.type=type; 11 } 12 public void makePhone() 13 { 14 cpu=getCpu(); 15 //.. 16 } 17 public void package(); 18 } 19 20 public class Iphone extends Cellphone 21 { 22 public Cpu getCpu() 23 { 24 switch(type) 25 { 26 case "3gs":cpu=CpuFactory.createCpu("apple A3"); 27 case "4":cpu=CpuFactory.createCpu("apple A5"); 28 case "4s":cpu=CpuFactory.createCpu("apple A5X"); 29 case "5":cpu=CpuFactory.createCpu("apple A6"); 30 //... 31 } 32 } 33 } 34 public class Motorola extends Cellphone 35 { 36 public Cpu getCpu() 37 { 38 switch(type) 39 { 40 case "millstone":cpu=CpuFactory.createCpu("高通1代");break; 41 case "me525":cpu=CpuFactory.createCpu("高通2代");break; 42 case "millstone2":cpu=CpuFactory.createCpu("高通2代");break; 43 case "xt860":cpu=CpuFactory.createCpu("高通3代");break; 44 //... 45 } 46 } 47 } 48 //... 49 public class CpuFactory 50 { 51 public static Cpu createCpu(String cpuType) 52 { 53 Cpu cpu; 54 switch(cpuType) 55 { 56 case "apple A5":cpu=new AppleCpu("A5");break; 57 case "apple A5X":cpu=new AppleCpu("A5X");break; 58 case "高通1代":cpu=new QualcommCpu("1");break; 59 case "高通2代":cpu=new QualcommCpu("2");break; 60 case "德州儀器":cpu=new TiCpu();break; 61 case "intel":cpu=new IntelCpu();break; 62 //... 63 } 64 } 65 }
對于上例來說,我們將不同類型的手機分離了出來,讓它們共同繼承Cellphone基類,顯得結構清晰,便于擴展,比如現在如果有一款三星的手機,就只需要繼承新建一個Samsung類然后繼承Cellphone類,然后重寫getCpu方法即可實現三星手機的cpu采購過程。也就是說,getCpu作為一個抽象的創建cpu的方法,讓子類去具體實現。
像這種結構,就體現了“具體的手機產品需要具體的cpu類型”的設計。也就是“定義一個用于創建對象的接口(getCpu),讓子類決定實例化哪一個類”,在Iphone類和Motorola類中分別實現了getCpu方法,使用簡單工廠產生了各種的cpu。在這里,并不是采用工廠方法模式取代簡單工廠,而是將簡單工廠也應用到了工廠方法模式當中,因為這兩者是不同的設計思想,也即 工廠方法模式是將創建過程抽象成接口 ,而 簡單工廠是將創建過程隔離出來 。而抽象工廠模式則是這兩種思想的進一步結合和升華。
- 抽象工廠模式:
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。 - 適用性:
- 一個系統要獨立于它的產品的創建、組合和表示時。
- 一個系統要由多個產品系列中的一個來配置時。
- 當你要強調一系列相關的產品對象的設計以便進行聯合使用時。
- 當你提供一個產品類庫,而只想顯示它們的接口而不是實現時。
前面的例子雖然將不同的手機種類從判斷分支中解脫了出來,但對于cpu的制作仍然顯得比較亂,也就是說CpuFactory承擔了所有的cpu制作任務(搞壟斷啊:>),而cpu的型號五花八門,同一種型號的cpu也有很多的版本,如果放到一個工廠生產,實在不是一個聰明的做法,所以,我們自然考慮將不同的cpu從一個簡單工廠解脫出來,分別由不同的工廠來生產(這樣才有競爭嘛:>),因此,我們將簡單工廠改造成抽象工廠,怎么改造?看下面:
1 //抽象工廠類 2 public class CpuFactory 3 { 4 Cpu cpu; 5 public abstract Cpu createCpu(String type); 6 } 7 //蘋果cpu工廠類 8 public class AppleCpuFactory extends CpuFactory 9 { 10 public Cpu createCpu(String type) 11 { 12 switch(type) 13 { 14 case "A3":this.cpu=new AppleCpu("A3");break; 15 case "A5":this.cpu=new AppleCpu("A5");break; 16 case "A5X":this.cpu=new AppleCpu("A5X");break; 17 case "A6":this.cpu=new AppleCpu("A6");break; 18 //... 19 } 20 } 21 } 22 //高通cpu工廠類 23 public class QualcommCpuFactory extends CpuFactory 24 { 25 public Cpu createCpu(String type) 26 { 27 switch(type) 28 { 29 case "1代":this.cpu=new QualcommCpu("1");break; 30 case "2代":this.cpu=new QualcommCpu("2");break; 31 case "3代":this.cpu=new QualcommCpu("3");break; 32 //... 33 } 34 } 35 } 36 //德州儀器工廠類 37 public class TiCpuFactory extends CpuFactory 38 { 39 public Cpu createCpu(String type) 40 { 41 switch(type) 42 { 43 case "1代":this.cpu=new TiCpu("1");break; 44 case "2代":this.cpu=new TiCpu("2");break; 45 case "3代":this.cpu=new TiCpu("3");break; 46 //... 47 } 48 } 49 } 50 //cpu產品抽象類 51 public class Cpu 52 { 53 protected float frequency; 54 protected String type; 55 public Cpu(String type) 56 { 57 this.type=type; 58 } 59 } 60 //蘋果cpu產品類 61 public class AppleCpu extends Cpu 62 { 63 } 64 //高通cpu產品類 65 public class QualcommCpu extends Cpu 66 { 67 } 68 //德州儀器cpu產品類 69 public class TiCpu extends Cpu 70 { 71 }
使用這種模式,就將各種具體的工廠分出了清晰的結構,也就是將工廠也抽象成接口了,這就是為什么叫“抽象工廠”的原因。
既然我們升級簡單工廠為抽象工廠,那么創造產品自然就要用新的接口方法了:
1 public class Cellphone 2 { 3 protected String type; 4 protected Cpu cpu; 5 protected Memory memory; 6 protected Screen screen; 7 protected CpuFactory cpuFactory; 8 protected abstract Cpu getCpu(); 9 public Cellphone(String type) 10 { 11 this.type=type; 12 } 13 public void makePhone() 14 { 15 cpu=getCpu(); 16 //.. 17 } 18 public void package(){}; 19 } 20 public class Iphone extends Cellphone 21 { 22 public Cpu getCpu() 23 { 24 this.cpuFactory=new AppleCpuFactory();//由于創建產品的方法不再是靜態方法,所以需要創建實例 25 switch(type) 26 { 27 case "3gs":cpu=this.cpuFactory.createCpu("A3"); 28 case "4":cpu=this.cpuFactory.createCpu("A5"); 29 case "4s":cpu=this.cpuFactory.createCpu("A5X"); 30 case "5":cpu=this.cpuFactory.createCpu("A6"); 31 //... 32 } 33 } 34 } 35 public class Motorola extends Cellphone 36 { 37 public Cpu getCpu() 38 { 39 this.cpuFactory=new QualcommCpuFactory(); 40 switch(type) 41 { 42 case "millstone":cpu=this.cpuFactory.createCpu("1");break; 43 case "me525":cpu=this.cpuFactory.createCpu("2");break; 44 case "millstone2":cpu=this.cpuFactory.createCpu("2");break; 45 case "xt860":cpu=this.cpuFactory.createCpu("3");break; 46 //... 47 } 48 } 49 }
這樣一來,整個結構就清晰多了,用一句話概括就是:由具體的手機使用具體的工廠生產具體的cpu,但手機、工廠和cpu都通過接口進行交互,較好的封裝了各自的實現,從而達到了較好的可擴展性,大大降低了各自的耦合性。現在,如果要添加新的手機、新的cpu,就可以建立新類型的工廠來實現對新的cpu的創建過程。
作者:everdom
出處:http://everdom.cnblogs.com/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。

浙公網安備 33010602011771號