工廠方法模式定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法讓類把例項化推遲到子類。 我們依然接著簡單工廠模式提出的披薩店問題繼續探討
問題模擬
我們假設有多種不同的pizza店,比如紐約的pizza點,芝加哥的pizza店,他們都有自己製作的不同種類的pizza。 如果我們採用簡單模式方法,那麼我們就需要分別建立紐約pizzafactory和芝加哥的factory等等工廠,但這樣做沒有彈性。我們能不能將製作pizza的行為侷限在旁pizzaStore類中,但同時又能讓不同類的點去各自例項化自己的pizza類。 顯然,我們可以將pizzaStore由一個具體類,變為一個抽象的介面:
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza=createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);
}
複製程式碼
![Paste_Image.png](https://i.iter01.com/images/6ad8bdda234b0adedcf58d6af79eabeb2dd844887f292c7a88e18d5f56b62c26.png)
定義了一個抽象的基類,裡面有個抽象的create方法,我們讓其他的紐約地區,芝加哥地區等等不同的繼承自這個基類,讓子類自己決定怎麼建立pizza。
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals(“cheese”)) {
return new NYStyleCheesePizza();
} else if (item.equals(“veggie”)) {
return new NYStyleVeggiePizza();
} else if (item.equals(“clam”)) {
return new NYStyleClamPizza();
} else if (item.equals(“pepperoni”)) {
return new NYStylePepperoniPizza();
} else return null;
}
}
複製程式碼
以上是我們實現的一個具體的紐約pizzastore類。他繼承實現了基類的抽象方法。
然後我們繼承實現抽象的pizza類和具體的pizza類
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare() {
System.out.println(“Preparing “ + name);
System.out.println(“Tossing dough...”);
System.out.println(“Adding sauce...”);
System.out.println(“Adding toppings: “);
for (int i = 0; i < toppings.size(); i++) {
System.out.println(“ “ + toppings.get(i));
}
}
void bake() {
System.out.println(“Bake for 25 minutes at 350”);
}
void cut() {
System.out.println(“Cutting the pizza into diagonal slices”);
}
void box() {
System.out.println(“Place pizza in official PizzaStore box”);
}
public String getName() {
return name;
}
}
複製程式碼
具體的產品類
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = “NY Style Sauce and Cheese Pizza”;
dough = “Thin Crust Dough”;
sauce = “Marinara Sauce”;
toppings.add(“Grated Reggiano Cheese”);
}
}
複製程式碼
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = “NY Style Sauce and Cheese Pizza”;
dough = “Thin Crust Dough”;
sauce = “Marinara Sauce”;
toppings.add(“Grated Reggiano Cheese”);
}
}
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza() {
name = “Chicago Style Deep Dish Cheese Pizza”;
dough = “Extra Thick Crust Dough”;
sauce = “Plum Tomato Sauce”;
toppings.add(“Shredded Mozzarella Cheese”);
}
void cut() {
System.out.println(“Cutting the pizza into square slices”);
}
}
複製程式碼
最後測試我們的程式碼:
public class PizzaTestDrive {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza(“cheese”);
System.out.println(“Ethan ordered a “ + pizza.getName() + “\n”);
pizza = chicagoStore.orderPizza(“cheese”);
System.out.println(“Joel ordered a “ + pizza.getName() + “\n”);
}
}
複製程式碼
工廠方法模式分析
![Paste_Image.png](https://i.iter01.com/images/d14a654b2f9b95b5a99c73679b3eaf6a8891f0333b758e331e8ed79235b09c4f.png)
工廠方法模式常常分為兩大類:一個建立產品的建立類,一個產品類,其中建立類定義一個抽象的介面,外加其餘的繼承自他的具體實現。產品類也是類似,定義一個抽象的產品類介面,具體的產品類實現繼承自基類。
![Paste_Image.png](https://i.iter01.com/images/26b976e5bbfd32c761c70cc34a04c6b5c7042a19dc348da0f5153fd41bf9798f.png)
![Paste_Image.png](https://i.iter01.com/images/2fdd7328c0da6f388c2a323b64f27c4e159642e43a1d72956d712b0963588f8c.png)
把建立物件的程式碼集中在一個物件或者方法中,可以避免程式碼的重複,並且更方便的以後的維護,這意味著客戶在例項化物件的時候,依賴的是介面,而不是具體的物件,而正是我們之前提到的具體設計原則中的一種,針對介面程式設計。
依賴倒置原則
這是我們提出的又一設計原則:**要依賴抽象,不要依賴具體實現 ** 工廠方法模式中,就很好的應用這個原則: 如果我們採用簡單工廠模式,依賴關係是這樣的:
![Paste_Image.png](https://i.iter01.com/images/f05d4c3376160bb4b3c31f890820be326ae68d5bba96afd07b1faf0c58ed42cf.png)
而採用工廠方法模式,依賴關係如圖:
![Paste_Image.png](https://i.iter01.com/images/10d2a7589556afa8c50bdf41f81788b4e672e8ecd0d2489dcb1ed62eba413634.png)
這就是依賴倒置原則,高層元件依賴了底層的pizza元件!
小結
工廠方法模式對簡單工廠模式進行了抽象。有一個抽象的Factory類(可以是抽象類和介面),這個類將不再負責具體的產品生產,而是隻制定一些規範,具體的生產工作由其子類去完成。在這個模式中,工廠類和產品類往往可以依次對應。即一個抽象工廠對應一個抽象產品,一個具體工廠對應一個具體產品,這個具體的工廠就負責生產對應的產品。
工廠方法經常用在以下兩種情況中:
- 第一種情況是對於某個產品,呼叫者清楚地知道應該使用哪個具體工廠服務,例項化該具體工廠,生產出具體的產品來。Java Collection中的iterator() 方法即屬於這種情況。
- 第二種情況,只是需要一種產品,而不想知道也不需要知道究竟是哪個工廠為生產的,即最終選用哪個具體工廠的決定權在生產者一方,它們根據當前系統的情況來例項化一個具體的工廠返回給使用者,而這個決策過程這對於使用者來說是透明的。