定義
定義一個創建對象的接口,但由子類決定要實例的類是哪一個。工廠方法讓類把實例推遲到子類。
設計原則
要依賴抽象,不要依賴具體的類:不能讓高層組件依賴於底層組件,並且兩者都應該依賴於抽象。
指導方針
- 變量不可以持有具體類的引用:如果使用new,就會持有具體類的引用。可以用工廠來避開這樣的做法。
- 不要讓類派生自具體類:如果派生自具體類,你就會依賴具體類。派生自一個抽象。
- 不要覆蓋基類中已實現的方法:如果覆蓋基類已實現的方法,那么你的基類就不是一個真正適合被繼承的抽象。基類中所有實現的方法,應該由所有子類共享。
抽象工廠模式
提供一個接口,用於創建相關或依賴對象的家族,而不需要明確指定的具體類。
第一次設計(識別變化)
假設我們有一個披薩店,我們需要寫代碼來制造一些不同類型的披薩。
下面來看看我們最開始的設計:
Pizza orderPizza(string type)
{
Pizza pizza;
if (type.Equals("cheese"))
{
pizza = new CheesePizza();
}
else if (type.Equals("greek"))
{
pizza = new GreekPizza();
}
else if (type.Equals("pepperoni"))
{
pizza = new PepperoniPizza();
}
else
{
pizza = new Pizza();
}
pizza.perpare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
再這里我們根據type傳入的類型來創建對應的pizza,但這里有一個問題,當我們需要修改一些pizza種類的時候,我們就必須要修改里面的代碼。比如:不在提供GreekPizza的pizza,我們就必須刪除創建GreekPizza的代碼。這樣就不符合我們的開放-關閉原則。
封裝創建對象代碼
下面,我們將創建pizza對象代碼封裝到一個單獨的對象中,由這個對象專職創建pizza。
class SimplePizzaFactory
{
public Pizza createPizza(string type)
{
Pizza pizza = null;
if (type.Equals("cheese"))
{
pizza = new CheesePizza();
}
else if (type.Equals("greek"))
{
pizza = new GreekPizza();
}
else if (type.Equals("pepperoni"))
{
pizza = new PepperoniPizza();
}
else
{
pizza = new Pizza();
}
}
}
重做PizzaStore類
class PizzaStore
{
private SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory)
{
this.factory = factory;
}
Pizza orderPizza(string type)
{
Pizza pizza=null;
pizza=factory.createPizza(type);
pizza.perpare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
類圖
這種簡單的工廠其實並不是一個設計模式,反而比較像是一種編程習慣。
使用框架
下面我們把createPizza的方法放回PizzaStore中,但是要把它設置成"抽象方法",然后為每個區域創建不同的PizzaStore子類。讓每個子類各自決定如何制造披薩。
public abstract class PizzaStore
{
//private SimplePizzaFactory factory;
//public PizzaStore(SimplePizzaFactory factory)
//{
// this.factory = factory;
//}
Pizza orderPizza(string type)
{
Pizza pizza = null;
// factory.createPizza(type);
//從工廠對象中移回PizzaStore中
pizza = createPizza(type);
pizza.perpare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
//工廠對象移到這里
public abstract Pizza createPizza(string type);
}
}
在這里,orderPizza會對Pizza對象做許多的事情,但並不知道究竟是哪一種披薩,這里其實就是解耦。
開一家披薩店
public override Pizza createPizza(string type)
{
if (type.Equals("cheese"))
{
return new CheesePizza();
}
//根據類型不同,創建其他對象
return null;
}
工廠模式
所有工廠模式都用來封裝對象創建的。
創建者類
Creator類是一個抽象類,它定義了一個抽象的工廠方法,讓子類實現此方法制造產品。
子類可以利用createPrizza創建自己的產品。
產品類
對於上面的PizzaStore來說,Pizza就是產品。
定義工廠方法模式
工廠方法模式:定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推出到子類。
依賴倒置原則
當你直接實例化一個對象時,就是在依賴它的具體類。
在代碼里減少對於具體類依賴是件"好事"。這個原則就是依賴倒置原則——要依賴抽象,不要依賴具體類。
不能讓高層組件(由其他底層組件定義其行為的類)賴低層組件,不管高層還是底層,兩者都應該依賴與抽象。
上面示例應用工廠模式后類圖:
可以看到,高層組件(PizzaStore)和底層組件都依賴了Pizza抽象。
幾個指導方針幫助你遵循此原則
- 變量不可以持有具體的引用
- 不要讓類派生自具體類
- 不要覆蓋基類中已經實現的方法