Java設計模式之簡單工廠、工廠方法和抽象工廠

2016-04-05    分類:JAVA開發、程式設計開發、設計模式、首頁精華0人評論發表於2016-04-05

本文由碼農網 – 魯阿皓原創,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

在前面的學習中(參見前面的部落格),我們學到了很多OO原則:

  • 封裝變化
  • 多用組合,少用繼承
  • 針對介面/超類程式設計,不針對實現程式設計
  • 鬆耦合
  • 開閉原則

讓我們從一個簡單的類開始,看看如何將之改造成符合OO原則的類以及工廠模式在解耦中的威力。

class FoodStore {
	public Food  orderFood()   //通過此方法顧客從食物商店中得到食物
	{
		 Food food=new Food();
		 food.prepare();    // 準備食物
		 food.cut();        // 將食物切好
		 food.box();  // 包裝好
		 return food;
	}
}

這樣寫還不夠,食物店裡又不是隻有一種food,我們要讓食物店提供更多!

class FoodStore {
	public Food orderFood(String type) {
		Food food = null;
		if (type.equals("bread")) {
			food = new bread();
		}
		else if(type.endsWith("chicken"))
		{
			food=new chicken();
		}
		food.prepare();
		food.cut();
		food.box();
		return food;
	}
}

這樣我們讓食物店提供了麵包和雞肉,他們分別是Food的子類,那我們要在新增一種食物(pizza)呢?這就又需要修改FoodStore的程式碼!這可不符合開閉原則!但是,從上面兩個不同版本的類來看,可以分析出類中變化的部分:食物型別!

既然我們已經找到了變化的部分,根據封裝變化的原則,就把這部分(物件的建立)獨立開來。

class SimpleFactory {
	 public Food creatFood(String type)
	 {
	  Food food = null;
	  if (type.equals("bread")) {
		food = new bread();
	  }
	  else if(type.endsWith("chicken"))
	  {
		food=new chicken();
	  }
	  return food;
	 }
}

這樣,我們就完成了今天第一個“模式”——簡單工廠,在這個過程中,其實我們只做了一件事:將foodstore中建立物件的部分與其隔離開(其實工廠模式的目的就是封裝物件的建立),即:封裝變化

下面是封裝之後的FoodStore:

class FoodStore {
	SimpleFactory factory;
	public FoodStore(SimpleFactory factory)
	{
		this.factory=factory;
	}
	public Food orderFood(String type) {
		Food food = factory.creatFood(type);
		food.prepare();
		food.cut();
		food.box();
		return food;
	}
}

由於建立物件的過程交給了工廠,所以如果再新增食物類別,只需要修改SimpleFactory類,這樣FoodStore就符合了對修改關閉原則!
簡單工廠已經很厲害了!但是還不足以應付所有的情況,讓我們看看工廠方法和抽象工廠的表現。

問題1:假如有很多商店都向簡單工廠去取物件,那麼所有商店取得的麵包物件和雞肉物件都是一樣的!我們想讓不同商店裡取得不同口味的食物!

問題2:假如一個商店想要出售兩種風味的雞肉,簡單工廠並不能很好的實現。

對於這兩個問題,雖然可以在簡單工廠中加入多種xxxbread和xxxchicken 來解決,但是會產生很多問題!

分析: 對於問題一,假設第n個商店分別需要n_bread和n_chicken,那麼你需要在簡單工廠里加入2*n個條件語句,但是,這2*n個語句裡,其實只有2個是對其對應的商店有效的,其他的都是累贅!

對於問題二, 假如有n種食物,m種口味,那就是m*n個條件語句,對某個商店來說,有用的其實只有m1*n1個,仍然是有很多冗餘程式碼!

細心的讀者會發現,問題一可以看做問題二的子問題,並且都指向了程式碼的複用性!雖然簡單工廠可以複用,但效率太低了!

針對問題一,我們可以將那個具有2*n個條件語句的簡單工廠分成n個工廠,與商店一一對應,這樣解決了效率問題(不同的商店呼叫不同的工廠),但是,每個工廠類其實只為對應的商店服務,也就是說,每個工廠類都不需要複用卻被我們獨立開來!在前文提到過,工廠模式的目的是將物件的建立進行封裝,那麼能不能將這種封裝放在“特殊”的地方呢?當然能!工廠方法模式就是將其放到子類上!

工廠方法模式:定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法讓類把例項化推遲到子類。

方法就是將物件建立部分宣告為抽象方法,交給子類去實現,如下:

abstract class Store {
	public Food orderFood(String type) {
		Food food = creatFood(type);
		food.prepare();
		food.cut();
		food.box();
		return food;
	}
	public abstract Food creatFood(String type); //抽象方法,父類並不知道具體的物件型別,這將由子類決定
}

工廠方法模式相比簡單工廠更具有彈性,可以變更正在建立的產品,但是它也放棄了複用性!那怎麼能夠既保有彈性又能實現複用呢(這正是問題二所面臨的問題)?

不用多說,這個自然就是我們的抽象工廠模式,先看看定義吧!

抽象工廠模式:提供一個介面,用於建立相關或依賴物件的家族,而不需要明確指定具體類。

如下:

interface abstract_Factory {
  public Food creatBread();
  public Food creatChicken();
}

做兩個實現類,如河北工廠和河南工廠,分別提供河北風味的麵包,雞肉和河南風味的麵包,雞肉,如下:

河北工廠
——————————————

class Hebei_Factory implements abstract_Factory {

	@Override
	public Food creatBread() {
		// TODO Auto-generated method stub

		return new Hebei_bread();
	}

	@Override
	public Food creatChicken() {
		// TODO Auto-generated method stub
		return new Hebei_chicken();
	}
}

河南工廠
——————————————

class Henan_Factory implements abstract_Factory {

	@Override
	public Food creatBread() {
		// TODO Auto-generated method stub
		return new Henan_bread();
	}

	@Override
	public Food creatChicken() {
		// TODO Auto-generated method stub
		return new Henan_chicken();
	}

}

相應的商店及執行程式碼
——————————————

class abstract_Factory_Story {

	Food food;

	public Food orderFood(String type,abstract_Factory factory) {
		if("bread".equals(type)){
			food = factory.creatBread();
		}
		else if("chicken".equals(type))
		{
			food = factory.creatChicken();
		}
		food.prepare();
		food.cut();
		food.box();
		return food;
	}
	public static void main(String[] args)
	{
		abstract_Factory_Story afs=new abstract_Factory_Story();
		afs.orderFood("bread", new Hebei_Factory());
		afs.orderFood("chicken", new Henan_Factory());
	}
}

執行結果
——————————————

Hebei_bread:prepare............
Hebei_bread:cutting............
Hebei_bread:boxed............
Henan_chicken:prepare............
Henan_chicken:cutting............
Henan_chicken:boxed............

可以看出,執行時我們只要傳入不同的工廠,就能得到不同風味的食物,各個工廠子類也能複用,在商店類中並不依賴具體型別(food是所有食物的超類),能夠輕易的進行擴充套件而不用修改程式碼(type的判斷放在工廠中會更好)。

總結

縱覽三種設計模式,可以發現簡單工廠和工廠方法都可以看做抽象工廠的子模式,抽象工廠本身就是工廠方法組合而成(將物件的建立延遲到子工廠),而相應的每個子工廠,也都可以看做是一個簡單工廠,只不過在抽象工廠裡,運用了面對介面/超類程式設計的方法將商店類與子工廠具體型別解耦,使之更具有彈性。在很多情況下,簡單工廠和工廠方法都能很好的替代抽象工廠,例如,在不需要複用的物件建立封裝時,工廠方法比抽象工廠更合適,在建立物件的型別相對確定時(不需要彈性),用簡單工廠也能勝任。

本文連結:http://www.codeceo.com/article/java-factory-pattern.html
本文作者:碼農網 – 魯阿皓
原創作品,轉載必須在正文中標註並保留原文連結和作者等資訊。]

相關文章