常說的工廠模式一般認為有三種:簡單工廠、工廠方法模式、抽象工廠模式。其中簡單工廠嚴格上來說不是一種設計模式,而被認為是一種好的編碼習慣/風格。 簡單工廠 簡單工廠的本質就是封裝變化的代碼,使客戶代碼將要面臨的改變變少。而且被封裝的代碼也有了更好的復用性,比如服務多個客戶端或者被繼承/包裝等工具來擴展
常說的工廠模式一般認為有三種:簡單工廠、工廠方法模式、抽象工廠模式。其中簡單工廠嚴格上來說不是一種設計模式,而被認為是一種好的編碼習慣/風格。
簡單工廠
簡單工廠的本質就是封裝變化的代碼,使客戶代碼將要面臨的改變變少。而且被封裝的代碼也有了更好的復用性,比如服務多個客戶端或者被繼承/包裝等工具來擴展。
下麵以腎5和腎6為對象來說明
//define product(iphone) interface public interface IPhone{ public void model(); } //iphone5 public class Phone5 implements IPhone{ public void model(){ System.out.printf("it is ipone5"); } } //iphone6 public class Phone6 implements IPhone{ public void model(){ System.out.printf("it is ipone6"); } } //Client: An apple store public class AppleStore{ public IPhone sellPhone(String model){ if(model.equals("5")){ return new Phone5(); } else if(model.equals("6")){ return new Phone6(); } } } //change it with SimpleFactory //Client: An apple store public class AppleStore{ private PhoneFactory factory; public IPhone sellPhone(String model){ return factory.createPhone(model); } } public class PhoneFactory{ public IPhone createPhone(String model){ if(model.equals("5")){ return new Phone5(); } else if(model.equals("6")){ return new Phone6(); } } }
從上面使用簡單工廠的前後我們可以看出,簡單工廠僅僅就是把if else那部分易變的代碼,因為會不斷的有新產品退出或者有舊的產品不再出售,進行了封裝。但是這樣的改變帶來的優點也是很明顯的。比如前面提到的服務多個客戶端,假如還有一個倉庫管理系統也可以利用這個PhoneFactory來創建iphone,而不用重新寫一份if else的代碼;再比如,假設在美國賣的是64GB版本,在中國賣的都是128GB版本,那麼可以保持客戶端的代碼不變,只要修改一下我們提供的介面類PhoneFactory即可達到相同信號但是不同產品的目的。也可以給AppleStore添加setFactory方法來設置不同版本的Factory以達到動態設定的效果。
從"對修改關閉"的原則上來看,簡單工廠也能使代碼更貼近這種原則。採用簡單工廠之後,需要修改的只是自己控制的工廠代碼,而對於客戶代碼則不需要修改,遵循了“對修改關閉”原則。
也有把createPhone寫成靜態方法的,這樣的做法就可以不用創建工廠實例,但缺點就是不能通過繼承來改變工廠。
工廠模式
工廠方法模式:定義一個創建對象的介面,但由子類決定要實例化的類是哪一個,把類的實例化推遲到工廠子類中。
下麵我們看一個具體的實例,使用工廠方法模式來重寫上面的賣手機模型。
1 //change it with FactoryMode 2 //Client: An apple store 3 public class AppleStore{ 4 private PhoneFactory factory; 5 6 public AppleStore(PhoneFactory factoy){ 7 this.factory = factory; 8 } 9 10 public IPhone sellPhone(String model){ 11 return factory.createPhone(model); 12 } 13 } 14 15 //factory interface 16 public abstract class PhoneFactory{ 17 public final IPhone createPhone(String model){ 18 return wrap(assemble(model)); 19 } 20 21 public final void wrap(IPhone phone){ 22 System.out.printf("wrap this phone"); 23 return phone; 24 } 25 26 abstract IPhone assemble(String model); 27 } 28 29 // fatory of USA 30 public class PhoneFactoryA extends PhoneFactory{ 31 public IPhone assemble(String model){ 32 if(model.equals("5")){ 33 return new Phone5ForA(); 34 } else if(model.equals("6")){ 35 return new Phone6ForA(); 36 } 37 } 38 } 39 40 //factory of CHN 41 public class PhoneFactoryC extends PhoneFactory{ 42 public IPhone assemble(String model){ 43 if(model.equals("5")){ 44 return new Phone5ForC(); 45 } else if(model.equals("6")){ 46 return new Phone6ForC(); 47 } 48 } 49 } 50 51 52 //applestore in usa 53 AppleStore usaStore = new AppleStore(new PhoneFactoryA()); 54 //applestore in CHN 55 AppleStore chnStore = new AppleStore(new PhoneFactoryC());
上面的工廠方法模型中,我們首先創建了一個工廠介面,然後分別實現了其子類USA工廠和CHN工廠,以分別製造適應不同國家的版本。Phone5ForA等對應的是不同國家的不同版本的類,代碼中沒有寫出來。如果又要開一個HK店,那麼只要實現一個HKPhoneFactoy子類即可。
上面比較重要的一點是:寫了兩個final方法:創建手機和包裝手機,然後從代碼可以看出來,手機組裝好後,必須經過wrap(包裝)才可以從store中售出。我們知道final方法不可以被重寫,也就是說,上面的流程:“組裝-包裝-出售”是不可以在子類中被更改的,這樣的話,就可以防止不良工廠賣出沒有包裝盒的手機,即就對子類的一些行為進行了控制,這也是工廠方法模式中很重要的一點。
工廠方法最重要的特點的就是創建工廠介面,使得能夠針對介面編程,而不是針對實現編程,簡單工廠就是針對實現編程。使用針對介面編程可以帶來更大的彈性。工廠方法介面的子類與簡單工廠中的工廠十分相似,從功能上看也是一致的,都實現了封裝變化部分的效果。但是從大局上看,簡單工廠僅僅就是實現封裝變化部分的效果,而工廠方法模式則是搭建了一個很有彈性的框架。
抽象工廠模式
抽象工廠定義:提供一個介面,用於創建相關或依賴對象的家族,而不需要明確指定具體類。
繼續上面的例子。我們知道手機是由多個零件組裝起來的,比如CPU,屏幕等,對於上面的Phone5和Phone6,組裝他們的時候都需要CPU和屏幕,但是各自的具體零件型號又不相同,這時,就該我們的抽象工廠方法上場了。
//define product(iphone) interface public interface IPhone{ protected IComponentFactory componentFactory; public void model(); } //iphone5 public class Phone5 implements IPhone{ public Phone5(){ componentFactory = new ComponentFactory5(); } public void model(){ System.out.printf("it is ipone5"); } } //iphone6 public class Phone6 implements IPhone{ public Phone6(){ componentFactory = new ComponentFactory6(); } public void model(){ System.out.printf("it is ipone6"); } } //ComponentFactory interface public interface IComponentFactory{ protected CPU cpu; protected Screen screen; } //ComponentFactory5 public class ComponentFactory5 implements IComponentFactory{ public ComponentFactory5(){ cpu = CPUFactory.createCPU(5); screen = ScreenFactory.createScreen(5); } //ComponentFactory6 public class ComponentFactory6 implements IComponentFactory{ public ComponentFactory6(){ cpu = CPUFactory.createCPU(6); screen = ScreenFactory.createScreen(6); } //cpu public interface CPU{ } public class CPU5 implements CPU{ } public class CPU6 implements CPU{ } public class CPUFactory{ public static CPU createCPU(int model){ if(model == 5){ return new CPU5(); } else if (model == 6){ return new CPU6(); } } //screen public interface Screen{ } public class Screen5 implements Screen{ } public class Screen6 implements Screen{ } public class ScreenFactory{ public static Screen createScreen(int model){ if(model == 5){ return new Screen5(); } else if (model == 6){ return new Screen6(); } }
在上面的代碼中,組裝Phone5和Phone6都需要CPU和Screen零件,於是他們都有一個零件工廠IComponentFactory(抽象工廠介面),各自的零件工廠都指定了自己需要什麼樣子的零件。而零件工廠創建零件的時候,則都是分別調用的具體零件工廠CPUFactory和ScreenFactory來創建具體的零件,也即本抽象工廠的實現中也應用到了前面提到的工廠方法模型。從這裡也可以發現:抽象工廠他們不真正的製造具體的對象,他們僅僅指定我們需要什麼要的對象,就好比抽象工廠方法就是一張裝配清單(比如大部分手機廠乾的就是這個活),而工廠方法則是負責製造這個裝配清單上的具體零件(比如高通、聯發科等就是乾的製造CPU的活),這也許是抽象工廠之所以被叫做抽象的原因。更進一步的我們可以發現,抽象工廠其實可以理解為組裝工廠,它的模型就是通過組合不同的Component來實現的;而工廠方法模式則是通過繼承,把具體實現交給子類來實現的。
在上面的抽象工廠代碼中,Phone類不需要關心CPU和Screen具體是怎麼創建的(這些具體的創建都是CPUFactory和ScreenFactory),而只知道我需要哪種CPU和Screen,這樣就把Phone從具體的CPU和Screen類解耦了。