設計模式之工廠模式(二)

Dimple91發表於2019-04-20

之前已經帶大家稍微入門了工廠模式(即簡單工廠模式)的方法,沒看過的朋友可以移步去檢視一番。設計模式之工廠模式(一)。今天我們繼續吃著披薩,學習著工廠模式的接下來部分吧。

加盟披薩店

我們先前的披薩店已經經營有成,擊敗了部分競爭者,接下來的計劃就是開加盟店。作為經營者,你肯定希望確保加盟店運營的質量,所以希望這些店都是用你那些經過時間考驗的程式碼。

但是每個地方可能需要不同口味的披薩(比如A地區、B地區、C地區等),這就是開店地點以及該地區披薩美食家口味的影像。

如果利用先前的簡單工廠,那麼就是需要建立多個不同的工廠可以使用。程式碼如下:

NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza("Veggie");

ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore nyStore = new PizzaStore(chicagoFactory);
nyStore.orderPizza("Veggie");
複製程式碼

給披薩店使用的框架

在使用簡單工廠的時候,我們發現加盟店的確是採用工廠建立披薩,但是其他部分,卻開始採用自己的方式。有個做法可以讓披薩製作活動侷限於PizzaStore類,而同時又能讓這些加盟店可以自由地製作該地區的風味。

所要做的事情呢,就是把之前createPizza()方法放回到PizzaStore中,不過不是單純的放回來,而是把它設定成抽象方法,然後每個區域建立一個PizzaStore的子類即可。.放出部分程式碼來看看

public abstract class PizzaStore {
    public Pizza orderPizza(String type) {
        // 現在createPizza()方法從工廠物件中移回PizzaStore
        Pizza pizza = createPizza(type);
        ...
        return pizza;
    }
    
    // 現在把工廠物件移動到這個方法中
    abstract Pizza createPizza(String type);
}
複製程式碼

現在上面這個就作為超類;讓每個域型別都繼承這個PizzaStore,每個子類各自決定如何製造披薩。

允許子類做決定

我們現在要讓createPizza()能夠應對各種變化建立正確種類的披薩。做法是讓PizzaStore的各個子類負責定義自己的createPizza()方法。所以,我們會得到一些PizzaStore具體的子類,每個子類都有自己的披薩變體,而仍然適合PizzaStore框架,並使用除錯好的orderPizza()方法。

設計模式之工廠模式(二)
;

這時候會有人肯定納悶了,PizzaStore的子類終究是子類,如何能做決定呢?關於這個方面,要從PizzaStore的orderPizza()方法觀點來看,此方法在抽象的PizzaStore內定義,但是隻在子類中實現具體型別。

現在,更進一步地,orderPizza()方法對Pizza物件做了許多事情,但由於Pizza物件是抽象的,orderPizza()並不知道哪些實際的具體類參與進來了。換句話說,這就是解耦(decopule)。

當orderPizza()呼叫createPizza()時,就會由具體的披薩店來決定做哪一種披薩。那麼,子類是實時做出這樣的決定嗎?不是,但從orderPizza()角度來看,如果選擇在NYStylePizzaStore訂購披薩,就是由這個子類決定。

讓我們開一家披薩店吧

現在我們把加盟店開了。其實我們只需要繼承PizzaStore,然後提供createPizza()方法實現自己的披薩風味即可。比如紐約風味:

// NYPizzaStore擴充套件PizzaStore,所以擁有orderPizza方法(以及其他方法)
public class NYPizzaStore extends PizzaStore {

// createPizza()返回一個Pizza物件,由子類全權負責該例項化哪一個具體的Pizza
	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;
	}
}
複製程式碼

工廠方法用來處理物件的建立,並將這樣的行為封裝在子類中。這樣,客戶程式中關於超類的程式碼就和子類物件建立程式碼解耦了。

如何利用披薩工廠方法訂購披薩

  1. 首先,需要取得披薩店的例項。A需要例項化一個ChicagoPizzaStore,而B需要一個NYPizzaStore
  2. 有了各自的PizzaStore,A和B分別呼叫orderPizza()方法,並傳入他們所喜愛的披薩型別
  3. orderPizza()呼叫createPizza()建立披薩。其中NYPizzaStore例項化的是紐約風味披薩,而ChicagoPizzaStore例項化的是芝加哥風味披薩。createPizza()會建立好的披薩當做返回值。
  4. orderPizza()並不知道真正建立的是哪一個披薩,只知道這是一個披薩,能夠被準備、被烘烤、被切片、被裝盒、然後提供給A和B

看看如何根據訂單生產這些披薩

  1. 先看看A的訂單,首先我們需要一個紐約披薩店:
// 建立一個NYPizzaStore的例項
PizzaStore nyPizzaStore = new NYPizzaStore();
複製程式碼
  1. 現在有了一個店,可以下訂單了:
// 呼叫nyPizzaStore例項的orderPizza()方法
nyPizzaStore.orderPizza("cheese");
複製程式碼
  1. orderPizza()方法於是呼叫cratePizza()方法
// 別忘了,工廠方法create-Pizza()是在子類中實現的。在這個例子中,他會返回紐約芝士披薩
Pizza pizza = createPizza("cheese");
複製程式碼
  1. 最後,披薩必須經過下列的處理才算成功orderPizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
複製程式碼

吃披薩咯

我們需要先有一個比薩,不然披薩店開起來了,結果沒有產品,豈不是很尷尬。

// 從一個抽象披薩類開始,所有的具體披薩都必須派生自這個類
public abstract class Pizza {
	String name;
	String dough;
	String sauce;
	ArrayList<String> toppings = new ArrayList<String>();
 
	void prepare() {
		System.out.println("Prepare " + name);
		System.out.println("Tossing dough...");
		System.out.println("Adding sauce...");
		System.out.println("Adding toppings: ");
		for (String topping : toppings) {
			System.out.println("   " + topping);
		}
	}
  
	void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}
 
	void cut() {
		System.out.println("Cut the pizza into diagonal slices");
	}
  
	void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}
 
	public String getName() {
		return name;
	}

	public String toString() {
		StringBuffer display = new StringBuffer();
		display.append("---- " + name + " ----\n");
		display.append(dough + "\n");
		display.append(sauce + "\n");
		for (String topping : toppings) {
			display.append(topping + "\n");
		}
		return display.toString();
	}
}
複製程式碼

我們來定義一些具體的子類,在這裡,其實就是定義紐約和芝加哥風味的芝士披薩

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 ChicagoStyleClamPizza extends Pizza {
	public ChicagoStyleClamPizza() {
		name = "Chicago Style Clam Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
		toppings.add("Frozen Clams from Chesapeake Bay");
	}
 
	void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}
複製程式碼

好了等久了吧,馬上來吃披薩了,這個時候剛好是下午4點左右,小編感覺已經餓的不行。

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");

		pizza = nyStore.orderPizza("clam");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("clam");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyStore.orderPizza("pepperoni");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("pepperoni");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

		pizza = nyStore.orderPizza("veggie");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("veggie");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");
	}
}
複製程式碼

好了,至此我們已經開了紐約和芝加哥披薩店,並已經愉快的製作和吃上了披薩,而且這是通過我們的工廠方法模式建立並得到的。

關於認識工廠方法模式,因為這篇我們已經通過程式碼來了解了下,我將在下一篇進行解釋並進一步認識這個模式,請大家敬請期待吧。

PS:因為工廠模式涉及的篇幅較大,幾篇文章可能存在不合理的銜接,小編會盡快輸出全部文章,讓大家能一次瞭解,在此給大家道個歉。

GitHub地址 HeadFirstDesign

愛生活,愛學習,愛感悟,愛挨踢

設計模式之工廠模式(二)

相關文章