工廠模式(Factory Pattern)定義: 定義了一個創建對象的介面,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類。 針對實現編程,但是當我們每次使用new時候,不正是在針對實現編程嗎?是的,當使用“new”時,你的確實在實例化一個具體類,所以用的確實是實現,而不是介面。代 ...
工廠模式(Factory Pattern)定義:
定義了一個創建對象的介面,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類。
針對實現編程,但是當我們每次使用new時候,不正是在針對實現編程嗎?是的,當使用“new”時,你的確實在實例化一個具體類,所以用的確實是實現,而不是介面。代碼綁定這具體類會導致代碼更脆弱,更缺乏彈性。
Duck duck = new GreenDuck();
使用介面讓代碼具有彈性 但還是得建立具體類的實例
但我們總是要用new來創建對象吧。是的,在技術上,new沒有錯,畢竟這是Java基礎部分。真正的犯人是我們的老朋友 “改變”,以及它是如何影響new的使用的。
看下麵的代碼:
1 /*當有一群相關的具體類時,通常會寫出這樣的代碼*/ 2 Duck duck; 3 4 if(picnic){ 5 duck = new GreenDuck(); 6 }else if(hunting){ 7 duck = new DecoyDuck(); 8 }else if(inBathTub){ 9 duck = new RubberDuck(); 10 }...//還有更多的實現類,在運行程式時,要通過判斷,來確定創建哪個對象View Code
針對介面編程,可以隔離掉以後系統可能發生的一大堆改變。如果代碼是針對介面編程而寫,那麼通過多態,它可以與任何新類實現該介面。但是,當代碼使用大量的具體類時,等於是自找麻煩,因為一旦加入新的具體類,就必須改變代碼。也就是說,你的代碼並非 “對修改關閉”。想用新的具體類型來擴展代碼,必須重新打開它。所以,遇到問題時候,就應該回到OO設計原則去尋找線索。別忘了,我們的第一個原則用來處理改變,並幫助我們 “找出會變化的方面,把它們從不變的部分分離出來”
舉例:
有一家比薩店,定製比薩的方法如下:
1 Pizza orderPizza(String type){//這隻是類中一個方法,將想要定製的比薩的type類型傳入,就會製作出相應的比薩 2 Pizza pizza; 3 4 if(type.equals("cheese")){//根據比薩的type類型,實例化正確的具體類,然後將其賦值給pizza實例變數。 5 pizza = new CheesePizza();//請註意,這裡的任何比薩都必須實現Pizza介面 6 }else if(type.equals("greek"){ 7 pizza = new GreekPizza(); 8 }else if(type.equals("pepperoni"){ 9 pizza = new PepperoniPizza(); 10 } 11 12 13 pizza.prepare();//一旦知道要製作什麼類型的比薩,那麼我們將要進行加工 14 pizza.bake();//這四個方法,分別是製作比薩的四個步驟 15 pizza.cut(); 16 pizza.box(); 17 18 return pizza; 19 }orderPizza
正如上面所說,我們要製作新的比薩具體類或者刪除現有的比薩具體類,都需要打開orderPizza()方法所在的類,來添加或刪除裡面的實例化。
我們要做的是,找出會變化的方面,把它們從不變的部分分離出來,現在我們將判斷製作比薩類型部分從orderPizza()方法中分離出來:
1 Pizza orderPizza(String type){//這隻是類中一個方法,將想要定製的比薩的type類型傳入,就會製作出相應的比薩 2 Pizza pizza; 3 4 //原先實例化比薩的部分被分離出去 5 6 pizza.prepare();//一旦知道要製作什麼類型的比薩,那麼我們將要進行加工 7 pizza.bake();//這四個方法,分別是製作比薩的四個步驟 8 pizza.cut(); 9 pizza.box(); 10 11 return pizza; 12 }orderPizza
1 public class SimplePizzaFactory{ 2 public Pizza createPizza(String type){ 3 Pizza pizza = null; 4 5 if(type.equals("cheese")){//根據比薩的type類型,實例化正確的具體類,然後將其賦值給pizza實例變數。 6 pizza = new CheesePizza();//請註意,這裡的任何比薩都必須實現Pizza介面 7 }else if(type.equals("pepperoni"){//刪除了一些比薩,又增加了一些比薩 8 pizza = new PepperoniPizza(); 9 }else if(type.equals("clam"){ 10 pizza = new ClamPizza(); 11 }else if(type.equals("veggie"){ 12 pizza = new VeggiePizza(); 13 } 14 15 return pizza; 16 } 17 }SimplePizzaFactory
從上面代碼可以看出,我們將orderPizza()方法中實例化比薩的部分分離出來,單獨放入了SimplePizzaFactory類中。
那麼,關於new的問題還是沒有解決,我們還是針對實現編程,只是把針對實現編程的部分搬到了另一個對象中。問題還是存在,沒錯,確實存在。但是我們已經做到最小化改變代碼(以我們現在所學的知識,還不足以脫離new來實例化對象,所以肯定要針對實現編程。若不能避免,那就做到最好!),若以後要修改創建比薩實例部分,就只要打開SimplePizzaFactory類就可以修改。
現在我們將PizzaStore(比薩店)類實現:
1 public class PizzaStore{ 2 SimplePizzaFactory factory;//比薩工廠對象 3 4 public PizzaStore(SimplePizzaFactory factory){//在構造器中,需要一個工廠對象作為參數 5 this.factory = factory; 6 } 7 8 public Pizza orderPizza(String type){ 9 Pizza pizza; 10 11 pizza = factory.createPizza(type);//將實例化比薩對象的責任交給工廠對象 12 13 pizza.prepare();//一旦知道要製作什麼類型的比薩,那麼我們將要進行加工 14 pizza.bake();//這四個方法,分別是製作比薩的四個步驟 15 pizza.cut(); 16 pizza.box(); 17 18 return pizza; 19 } 20 }PizzaStore
上面的就是簡單工廠模式,這隻是小本生意的開始。我們只開了一家比薩店,現在我們的比薩店出名了,要開跨地區連鎖店,規定每個地區開一家。我們做的比薩要符合當地人的口味。於是各個地區的比薩口味又不一樣。
不多說先把Pizza抽象類實現了:
1 import java.util.ArrayList; 2 3 public abstract class Pizza{ 4 String name;//比薩名字 5 String dough;//麵團 6 String sauce;//醬 7 ArrayList toppings = new ArrayList();//配料列表 8 9 void prepare(){//準備做比薩方法 10 System.out.println("Preparing " + name); 11 System.out.println("Tossing dough..."); 12 System.out.println("Adding sauce..."); 13 System.out.println("Adding toppings: "); 14 15 for(int i = 0; i < toppings.size(); i++){ 16 System.out.println(" " + toppings.get(i)); 17 } 18 } 19 20 void bake(){//烘焙方法 21 System.out.println("Bake for 25 minutes at 350"); 22 } 23 24 void cut(){//切塊方法 25 System.out.println("Cutting the pizza into diagonal slices"); 26 } 27 28 void box(){//裝盒方法 29 System.out.println("Place pizza in official PizzaStore box"); 30 } 31 32 public String getName(){//獲取名稱方法 33 return name; 34 } 35 }Pizza
然後根據各地區做出不同的Pizza:
1 public class NYStyleCheesePizza extends Pizza{//NewYork比薩 2 public NYStyleCheesePizza(){ 3 name = "NY Style Sauce and Cheese Pizza"; 4 dough = "Thin Crust Dough"; 5 sauce = "Marinara Sauce"; 6 7 toppings.add("Grated Reggiano Cheese"); 8 } 9 }NYStyleCheesePizza
1 public class ChicagoStyleCheesePizza extends Pizza{//Chicago比薩 2 public ChicagoStyleCheesePizza(){ 3 name = "Chicago Style Deep Dish Cheese Pizza"; 4 dough = "Extra Thick Crust Dough"; 5 sauce = "Plum Tomato Sauce"; 6 7 toppings.add("Shredded Mozzarella Cheese"); 8 } 9 10 void cut(){ 11 System.out.println("Cutting the pizza into square slices"); 12 } 13 }ChicagoStyleCheesePizza
實現PizzaStore抽象類:
1 public abstract class PizzaStore{//所有地區的PizzaStore都要繼承該抽象類 2 public Pizza orderPizza(String type){//定製Pizza方法 3 Pizza pizza; 4 5 pizza = createPizza(type);//創建Pizza實例 6 7 pizza.prepare(); 8 pizza.bake(); 9 pizza.cut(); 10 pizza.box(); 11 12 return pizza; 13 } 14 15 abstract Pizza createPizza(String type);//創建Pizza實例的方法由每個地區的PizzaStore子類來實現 16 }PizzaStore
每個地區開一個PizzaStore:
1 public class NYPizzaStore extends PizzaStore{//NewYork店 2 Pizza createPizza(String item){ 3 if(item.equals("cheese")){//可以製作出cheese口味的Pizza 4 return new NYStyleCheesePizza(); 5 }else return null;//也可以製作出其他口味的Pizza,我就不實現了。 6 } 7 }NYPizzaStore
1 public class ChicagoPizzaStore extends PizzaStore{//Chicago店 2 Pizza createPizza(String item){ 3 if(item.equals("cheese")){//可以製作出Cheese口味的Pizza 4 return new ChicagoStyleCheesePizza(); 5 }else return null;//也可以製作出其他口味的Pizza,我就不實現了。 6 } 7 }ChicagoPizzaStore
測試類:
1 public class PizzaTestDrive{ 2 3 public static void main(String args[]){ 4 //先開店 5 PizzaStore nyStore = new NYPizzaStore(); 6 PizzaStore chicagoStore = new ChicagoPizzaStore(); 7 8 //NYPizzaStore店製作出cheese口味的Pizza 9 Pizza pizza = nyStore.orderPizza("cheese"); 10 System.out.println("Ethan ordered a " + pizza.getName() + "\n"); 11 //ChicagoPizzaStore店製作出cheese口味的Pizza 12 pizza = chicagoStore.orderPizza("cheese"); 13 System.out.println("Joel ordered a " + pizza.getName() + "\n"); 14 } 15 }PizzaTestDrive
編譯運行結果:
上面的就是工廠模式。
總結一下,有兩個抽象類:
1.Pizza抽象類:這個抽象類是產品抽象類,換句話說就是工廠要製作出的產品。所有的Pizza子類都要繼承自該Pizza抽象類。
2.PizzaStore抽象類:這個抽象類是工廠抽象類,這個抽象類中有一個createPizza()抽象方法,這個方法就是為了讓子類去實現自己的產品類。這樣一來,子類可以根據自己的情況,隨心所欲的生產符合自己口味的產品。
再細細地品味一下工廠模式的定義:
工廠模式定義了一個創建對象的介面(工廠抽象類),但由工廠子類決定要實例化哪一個類。工廠方法讓類把實例化推遲到了工廠子類。
由工廠子類決定要實例化哪一個類,不要理解錯誤。並不是指模式允許子類本身在運行時做決定,而是指在編寫工廠抽象類時,不需要知道實際創建的產品是哪一個。選擇使用了哪個子類,自然就決定了實際創建的產品是什麼。
這裡溫馨提醒一下:在設計模式中,所謂的 “實現一個介面” 並 “不一定” 表示 “寫一個介面,並利用implements關鍵字來實現這個介面”。“實現一個介面” 泛指 “實現某個超類型(可以是類或介面)的某個方法” 。