本程序改編自《Head First Design Patterns》中的Pizza例子,我本學期早上經常吃包子。
總共有11個類:
- 一個工廠父類 ( Creator)
- 兩個工廠子類 ( Concrete Creator)
- 一個包子父類 ( Product )
- 六個包子子類 ( Concrete Product )
- 一個Main類 ( 程序的入口 )

圖:ConcreteProduct 繼承 Product,ConcreteCreator 繼承 Creator,ConcreateProduct 依賴 ConcreateProduct。其實,有一條線沒有顯示出來:Creator 依賴 Product。
*Head First* 說,這體現了依賴倒置原則:因為高層模塊(Creator)依賴抽象類(Product),底層模塊(ConcreteProduct)也依賴抽象類(Product)。
補充說明:我個人感覺,嚴格的說:ConcreteProduct 和Product 之間不是依賴,而是繼承關系。勉強說的話,算作一種”非常非常強的依賴“吧。*Head First* : The ConcreteProduct class depends on the Product abstraction too, because they implement the Product interface (we’re using the “interface” in the general sense) in the Product abstraction class.
### 一個工廠父類
~~~
package factoryMethod;
public abstract class BaoZiFactory {
// 把具體的 new操作 “下放”到子類中。
abstract BaoZi createBaoZi(String baoZiName);
// 這個方法不改變,即無論包子里面的餡是什么,制作包子的流程是一樣的(都是準備餡,然后蒸30分鐘)
final public BaoZi makeBaoZi(String baoZiName)
{
BaoZi baoZi = createBaoZi(baoZiName);
baoZi.prepare();
baoZi.steam();
return baoZi;
}
}
~~~
### 兩個工廠子類
~~~
package factoryMethod;
public class BaoZiFactoryChangsha extends BaoZiFactory {
public BaoZiFactoryChangsha()
{
System.out.println("Constructor of BaoZiFactory in Changsha\n");
}
@Override // 依據傳進來的參數,決定new什么包子
BaoZi createBaoZi(String baoZiName)
{
BaoZi baoZi = null;
if(baoZiName.equals("醬肉"))
{
baoZi = new ChangshaJiangRouBaoZi();
}
else if(baoZiName.equals("青菜"))
{
baoZi = new ChangshaQingCaiBaoZi();
}
else if(baoZiName.equals("鮮肉"))
{
baoZi = new ChangshaXianRouBaoZi();
}
return baoZi;
}
}
~~~
~~~
package factoryMethod;
public class BaoZiFactoryWuhan extends BaoZiFactory {
public BaoZiFactoryWuhan()
{
System.out.println("Constructor of BaoZiFactory in Wuhan\n");
}
@Override // 依據傳進來的參數,決定new什么包子
BaoZi createBaoZi(String baoZiName)
{
BaoZi baoZi = null;
if(baoZiName.equals("醬肉"))
{
baoZi = new WuhanJiangRouBaoZi();
}
else if(baoZiName.equals("青菜"))
{
baoZi = new WuhanQingCaiBaoZi();
}
else if(baoZiName.equals("鮮肉"))
{
baoZi = new WuhanXianRouBaoZi();
}
return baoZi;
}
}
~~~
### 一個包子父類
~~~
package factoryMethod;
import java.util.ArrayList;
// 父類:包子,抽象出包子共有的特性
// 有道詞典:steamed stuffed bun (蒸的,填充的,小圓面包)
// 由于這三個單詞加起來過長,我命名放棄采納英文命名法,直接使用漢語拼音命名法BaoZi
public abstract class BaoZi {
private String name;
ArrayList<String> stuffings = new ArrayList<String> ();
public void setName(String n)
{
this.name = n;
}
public String getName()
{
return name;
}
void prepare()
{
System.out.println("Prepare " + name);
System.out.println("Stuffings are:");
for(String stuff: stuffings)
{
System.out.println(stuff);
}
}
void steam()
{
System.out.println("Steam for 30 minutes");
}
//覆蓋toString (這個方法繼承自java.lang.Object)
public String toString()
{
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
for(String stuff : stuffings)
{
display.append(stuff + "\n");
}
return display.toString();
}
}
~~~
### 六個包子子類
~~~
package factoryMethod;
public class ChangshaJiangRouBaoZi extends BaoZi {
public ChangshaJiangRouBaoZi()
{
setName("長沙醬肉包子");
stuffings.add("辣椒");
stuffings.add("炸醬");
stuffings.add("肉末");
stuffings.add("干子");
}
}
~~~
~~~
package factoryMethod;
public class ChangshaQingCaiBaoZi extends BaoZi {
public ChangshaQingCaiBaoZi()
{
setName("長沙青菜包子");
stuffings.add("辣椒");
stuffings.add("包菜");
stuffings.add("茄子");
}
}
~~~
~~~
package factoryMethod;
public class ChangshaXianRouBaoZi extends BaoZi {
public ChangshaXianRouBaoZi()
{
setName("長沙鮮肉包子");
stuffings.add("辣椒");
stuffings.add("鮮肉");
}
}
~~~
~~~
package factoryMethod;
public class WuhanJiangRouBaoZi extends BaoZi {
public WuhanJiangRouBaoZi()
{
setName("武漢醬肉包子");
stuffings.add("炸醬");
stuffings.add("肉末");
stuffings.add("干子");
}
}
~~~
~~~
package factoryMethod;
public class WuhanQingCaiBaoZi extends BaoZi {
public WuhanQingCaiBaoZi()
{
setName("武漢青菜包子");
stuffings.add("包菜");
stuffings.add("茄子");
}
}
~~~
~~~
package factoryMethod;
public class WuhanXianRouBaoZi extends BaoZi {
public WuhanXianRouBaoZi()
{
setName("武漢鮮肉包子");
stuffings.add("鮮肉");
}
}
~~~
### 一個Main類
~~~
package factoryMethod;
public class Main {
public static void main(String[] args)
{
BaoZiFactory wuhanFactory = new BaoZiFactoryWuhan();
BaoZiFactory changshaFactory = new BaoZiFactoryChangsha();
BaoZi baoZi = null;
baoZi = wuhanFactory.makeBaoZi("醬肉");
System.out.println("Caitao made a " + baoZi.getName() + "\n");
baoZi = wuhanFactory.makeBaoZi("青菜");
System.out.println("Caitao made a " + baoZi.getName() + "\n");
baoZi = changshaFactory.makeBaoZi("鮮肉");
System.out.println("Lucy made a " + baoZi.getName() + "\n");
baoZi = changshaFactory.makeBaoZi("青菜");
System.out.println("Lucy made a " + baoZi.getName() + "\n");
}
}
~~~
### 運行結果
直接從eclipse復制過來的
~~~
Constructor of BaoZiFactory in Wuhan
Constructor of BaoZiFactory in Changsha
Prepare 武漢醬肉包子
Stuffings are:
炸醬
肉末
干子
Steam for 30 minutes
Caitao made a 武漢醬肉包子
Prepare 武漢青菜包子
Stuffings are:
包菜
茄子
Steam for 30 minutes
Caitao made a 武漢青菜包子
Prepare 長沙鮮肉包子
Stuffings are:
辣椒
鮮肉
Steam for 30 minutes
Lucy made a 長沙鮮肉包子
Prepare 長沙青菜包子
Stuffings are:
辣椒
包菜
茄子
Steam for 30 minutes
Lucy made a 長沙青菜包子
~~~
### 如果沒有工廠方法模式
又要實現同樣的功能怎么破?代碼如下(理想輸入條件,沒有異常處理)
~~~
public BaoZi makeBaoZi(String place, String type)
{
BaoZi baoZi = null;
if (place.equals("武漢"))
{
if (type.equals("醬肉"))
{
baoZi = new WuhanJiangRouBaoZi();
}
else if (type.equals("青菜"))
{
baoZi = new WuhanQingCaiBaoZi();
}
else if (type.equals("鮮肉"))
{
baoZi = new WuhanXianRouBaoZi();
}
}
else if (place.equals("長沙"))
{
if (type.equals("醬肉"))
{
baoZi = new ChangshaJiangRouBaoZi();
}
else if (type.equals("青菜"))
{
baoZi = new ChangshaQingCaiBaoZi();
}
else if (type.equals("鮮肉"))
{
baoZi = new ChangshaXianRouBaoZi();
}
}
baoZi.prepare();
baoZi.steam();
return baoZi;
}
~~~
~~~
優劣之分立馬體現出來了!我們可以看到代碼變短了(當然了,需要增加一些類作為“額外工作”,這是值得的)
創建包子的new操作“隱藏了”,取而代之的是一個factory對象調用createBaoZi方法
public BaoZi makeBaoZi(String place, String type)
{
BaoZiFactory factory = null;
if(place.equals("武漢"))
{
factory = new BaoZiFactoryWuhan();
}
else if(place.equals("長沙"))
{
factory = new BaoZiFactoryChangsha();
}
BaoZi baoZi = factory.createBaoZi(type);
baoZi.prepare();
baoZi.steam();
return baoZi;
}
~~~
**1. 工廠方法模式的優勢**:如果位于武漢的工廠需要增加一種口味的包子(這種類似的事情經常發生),比如”熱干包子“,那么工廠方法模式只用增加一個包子子類,然后修改武漢工廠子類就行了。
~~~
關鍵是下面的代碼不變,放到哪里都一樣,以不變應萬變!
BaoZiFactory factory = null;
if(place.equals("武漢"))
{
factory = new BaoZiFactoryWuhan();
}
else if(place.equals("長沙"))
{
factory = new BaoZiFactoryChangsha();
}
BaoZi baoZi = factory.createBaoZi(type);
~~~
**2. 沒有工廠方法模式的劣勢**:如果位于武漢的工廠需要增加一種口味的包子(這種類似的事情經常發生),比如”熱干包子“,那么所有用于在武漢創建包子的
if( … ) { new … }
else( ) { … }
的代碼都在后面增加一個
if( 熱干 ) {new ReGanBaoZiWuhan() }
else( ) { … }
~~~
BaoZi baoZi = null;
if (place.equals("武漢"))
{
if (type.equals("醬肉"))
{
baoZi = new WuhanJiangRouBaoZi();
}
else if (type.equals("青菜"))
{
baoZi = new WuhanQingCaiBaoZi();
}
else if (type.equals("鮮肉"))
{
baoZi = new WuhanXianRouBaoZi();
}
*********增加的代碼*****************
else if(type.equals("熱干"))
{
baoZi = new WuhanReGanBaoZi();
}
************************************
}
else if (place.equals("長沙"))
{
if (type.equals("醬肉"))
{
baoZi = new ChangshaJiangRouBaoZi();
}
else if (type.equals("青菜"))
{
baoZi = new ChangshaQingCaiBaoZi();
}
else if (type.equals("鮮肉"))
{
baoZi = new ChangshaXianRouBaoZi();
}
}
~~~
改一個地方還比較輕松,**但是關鍵在于很有可能在很多地方都要用到包子**。假設100個地方都要創建包子,那么100個地方的if( … )else( )“的代碼都要修改!這完全是反人類,違反了開閉原則。
[更多深入分析,查看我的這一篇博客,進入目錄->第三個標題](http://blog.csdn.net/u013390476/article/details/50333763)