菜鳥成長系列-工廠模式

glmapper發表於2017-12-24

菜鳥成長系列-概述
菜鳥成長系列-物件導向的四大基礎特性
菜鳥成長系列-多型、介面和抽象類
菜鳥成長系列-物件導向的6種設計原則
菜鳥成長系列-單例模式

上一篇我們已經對建立型模式中的單例模式進行了學習,今天來學習另外一個比較重要並且經常使用的模式-工廠模式;工廠模式專門負責將大量有共同介面的類例項化。其可以動態的決定將哪一個類例項化,不必事先知道每次要例項化哪一個類。

工廠模式具有以下三種形態:

  • 簡單工廠模式:又稱靜態工廠模式
  • 工廠方法模式:又稱多型性工廠模式或者虛擬構造子模式
  • 抽象工廠模式:又稱工具箱模式

本篇文章將對這三種形態的工廠模式進行一些基本的學習,並通過例子來直觀的感受下不同形態的具體實現方式。最後再分析下JAVA以及Spring中是如何使用的。

1、簡單工廠模式

菜鳥成長系列-工廠模式
從上圖可以看出,簡單工廠模式涉及到工廠角色、抽象產品角色以及具體產品角色等三個角色。各角色職能如下:

  • 工廠類:擔任這個角色的是工廠方法模式的核心,含有與應用緊密相關的具體業務邏輯。工廠類在客戶端的直接呼叫下建立產品物件,它往往由一個具體的java類實現
  • 抽象產品:擔任這個角色的類是工廠方法模式所建立的物件的父類,或它們共同擁有的介面。抽象產品角色可以用一個java介面或者抽象類來實現
  • 具體產品:工廠方法模式所建立的任何物件都是這個角色的例項,具體產品角色由一個java類實現

菜鳥成長系列-工廠模式
來看例子,考慮到今天有小夥伴來我這做客,本demo將以做菜來實現一波。首先工廠就是廚房,抽象類就是籠統的菜,實現類就是具體哪個菜。

  • 抽象產品
package com.glmapper.design.factory;
/**
 * 抽象類角色:food介面,約束型別
 * @author glmapper
 * @date 2017年12月24日上午10:38:36
 *
 */
public interface IFood {
	/**
	 * 提供一個展示食物細節的方法
	 * @param foodName 食物名稱
	 */
	public void showFood();
}

複製程式碼
  • 具體產品-魚
package com.glmapper.design.factory;
/**
 * 具體產品-食物魚
 * @author glmapper
 * @date 2017年12月24日上午10:51:29
 *
 */
public class FishFood implements IFood
{
	@Override
	public void showFood() {
		System.out.println("一盤魚");
	}
}
複製程式碼
  • 具體產品-土豆絲
package com.glmapper.design.factory;
/**
 * 
 * 具體食物:土豆絲
 * @author glmapper
 * @date 2017年12月24日上午10:47:17
 *
 */
public class ShreddedPotatoesFood implements IFood{
	@Override
	public void showFood() {
		System.out.println("一盤土豆絲");
		
	}
}
複製程式碼
  • 工廠角色 - 食物工廠
package com.glmapper.design.factory;
/**
 * 工廠角色-食物工廠
 * 
 * @author glmapper
 * @date 2017年12月24日上午10:41:10
 *
 */
public class SimpleFoodFactory {
	/**
	 * 提供一個靜態方法,用於獲取食物
	 * @param foodType 食物型別
	 * @return 具體食物
	 */
	public static IFood getFood(String foodType){
		IFood food = null;
		if (foodType.equals("fish")) {
			food = new FishFood();
		}
		if (foodType.equals("potatoes")) {
			food = new ShreddedPotatoesFood();
		}
		return food;
	}
}
複製程式碼
  • 客戶端
package com.glmapper.design.factory;
/**
 * 客戶端
 * @author glmapper
 * @date 2017年12月24日上午10:45:17
 *
 */
public class MainTest {
	public static void main(String[] args) {
		IFood fishfood = SimpleFoodFactory.getFood("fish");
		fishfood.showFood();
		
		IFood potatoesfood = SimpleFoodFactory.getFood("potatoes");
		potatoesfood.showFood();
	}
}
複製程式碼
  • 結果
一盤魚
一盤土豆絲
複製程式碼

OK,菜做完了,可以吃了。。。

我們來討論下簡單工廠模式的優缺點:

優點:模式的核心是工廠類,這個類含有必要的判斷邏輯,可以決定在什麼時候建立哪一個產品類的例項。而客戶端則可以免除直接建立產品物件的責任,而僅僅負責消費產品即可。用一句話來說就是:簡單工廠模式這個做法實現了對責任的分割。
缺點:集中了所有產品的建立邏輯,形成了一個無所不能的全職類,但是之前我們在討論設計原則的時候說過,我們要儘量避免這種情況的發生,這種就很明顯破壞了單一職責這條原則,另外也不滿足開閉原則的約束。當我們需要進行品類擴充套件時,我們需要不斷的去修改我們的工廠的業務邏輯,一方面是工廠類會急速的膨脹,另一方面因為囊括了不同的產品對於我們後期的維護造成一定的影響。

2、工廠方法模式

這個時候一個同事說他是南方人,另外一個同事說他是北方人,吃不慣今天的菜。

菜鳥成長系列-工廠模式
好吧,既然這樣,那我就只能點外賣了。。。但是為了防止他們變卦自己的家鄉,我需要做一個計劃,下面就是計劃圖:
菜鳥成長系列-工廠模式

從上圖中我們可以看出,工廠方法模式的角色包括以下幾種:

  • 抽象工廠:是工廠方法模式的核心,與應用程式無關。任何在模式中建立的物件的工廠類必須實現這個介面。
  • 具體工廠:這是實現抽象工廠介面的具體工廠類,包含與應用程式密切相關的邏輯,並且受到應用程式呼叫以建立產品物件。
  • 抽象產品:工廠方法模式所建立的物件的超型別,也就是產品物件的共同父類或共同擁有的介面
  • 具體產品:這個角色實現了抽象產品角色所定義的介面。某具體產品有專門的具體工廠建立,它們之間往往一一對應。

因為我的同事都是來自不同地方的,他們的口味也都不一樣,但是呢同事都是第一次來我家吃飯,所以為了招待周全,根據同事不同的口味叫不同口味的魚。

  • 抽象工廠角色:獲取食物
package com.glmapper.design.factory;
/**
 * 
 * 角色1:抽象工廠 - 負責獲取食物
 * @author glmapper
 * @date 2017年12月24日下午1:59:28
 */
public interface MethodFoodFactory {
	//獲取食物的方法
	public IFishFood getFood();
}
複製程式碼
  • 具體工廠1:獲取南方食物-魚
package com.glmapper.design.factory;
/**
 * 南方口味外賣 - 魚
 * @author glmapper
 * @date 2017年12月24日下午2:03:36
 */
public class SouthFishFactory implements MethodFoodFactory{
	@Override
	public IFishFood getFood() {
		return new SouthFishFood();
	}
}

複製程式碼
  • 具體工廠2:獲取北方食物-魚
package com.glmapper.design.factory;
/**
 * 北方口味外賣 - 魚
 * @author glmapper
 * @date 2017年12月24日下午2:03:36
 */
public class NorthFishFactory implements MethodFoodFactory{

	@Override
	public IFishFood getFood() {
		// TODO Auto-generated method stub
		return new NorthFishFood();
	}
}
複製程式碼
  • 具體產品1:南方食物- 魚
package com.glmapper.design.factory;
/**
 * 南方口味-魚
 * @author glmapper
 * @date 2017年12月24日下午2:16:17
 */
public class SouthFishFood implements IFishFood{
	@Override
	public void showFood() {
		System.out.println("來自南方廚師做的魚");
	}
}
複製程式碼
  • 具體產品2:北方食物-魚
package com.glmapper.design.factory;
/**
 * 北方口味  - 魚
 * @author glmapper
 * @date 2017年12月24日下午2:12:55
 */
public class NorthFishFood implements IFishFood {
	@Override
	public void showFood() {
		System.out.println("來自北方廚師做的魚");
	}
}
複製程式碼
  • 客戶端
package com.glmapper.design.factory;
/**
 * 客戶端
 * @author glmapper
 * @date 2017年12月24日上午10:45:17
 */
public class MainTest {
	public static void main(String[] args) {
		//點一個南方口味外賣
		MethodFoodFactory southFoodFactory = new SouthFishFactory();
		//點一個北方口味外賣
		MethodFoodFactory northFoodFactory = new NorthFishFactory();
		//拿到南方口味外賣魚
		southFoodFactory.getFood().showFood();
		//拿到北方口味外賣魚
		northFoodFactory.getFood().showFood();
	}
}
複製程式碼
  • 結果:
來自南方廚師做的魚
來自北方廚師做的魚
複製程式碼

OK,這樣我們就滿足了不同區域同時關於魚口味的需求了,以後升值加薪就指望他們了。。。

關於工廠方法模式的優缺點:

優點:
1、 在工廠方法中,使用者只需要知道所要產品的具體工廠,無須關係具體的建立過程,甚至不需要具體產品類的類名。
2、 在系統增加新的產品時,我們只需要新增一個具體產品類和對應的實現工廠,無需對原工廠進行任何修改,很好地符合了“開閉原則”
缺點:
每次增加一個產品時,都需要增加一個具體類和物件實現工廠,是的系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

3、抽象工廠模式

準備吃飯的時候突然又來了幾位同事,而且他們有的喜歡吃酸菜魚,有的喜歡吃紅燒魚,這就很頭疼。於是,只能根據他們的需要繼續點外賣。(這個就給出一個結構圖,並且將每種角色都用具體的場景來說明了,具體的程式碼可以參考這個圖例自己嘗試一下。)

菜鳥成長系列-工廠模式
OK,終於可以吃飯了!

4、三種形態的工廠模式在java中的使用

4.1、簡單工廠模式在java中的使用

java.text.DateFormat (一個抽象類)這個類相信很多小夥伴都用到過,在java API中,這個類算是一個簡單工廠模式的典型應用了(此處還是與上篇一樣,不考慮期原始碼細節,也不介紹基本用法)。來看它的幾個方法:

  • public final static DateFormat getDateInstance()
  • public final static DateFormat getDateInstance(int style)
  • public final static DateFormat getDateInstance(int style, Locale aLocale)

作為一個抽象類,卻提供了很多的靜態工廠方法,就像上面列舉的那三個一樣。有小夥伴可能會疑惑,為啥子一個抽象類闊以有自己的例項,並通過幾個方法提供自己的例項。我們知道,抽象類是不可以有自己的例項物件的,但是需要注意的是,DateFormat的工廠方法是靜態的,並不是普通的方法,也就是說,不需要通過建立例項物件的方式去呼叫。

public final static DateFormat getDateInstance()
{
    return get(0, DEFAULT, 2, Locale.getDefault(Locale.Category.FORMAT));
}
複製程式碼

getDateInstance方法並沒有通過呼叫DateFormat的構造方法來建立物件。

4.2、工廠方法模式在java中的應用

java.net.URL類,類圖如下,

菜鳥成長系列-工廠模式
URL物件通過一個工廠方法openConnection()返回一個URLConnection型別的物件。URLConnection是一個抽象類,因此所返還的不可能是這個抽象類的例項,而必然是其具體子類的例項。

4.3、抽象工廠模式在java中的應用

根據java與模式一書的介紹,在java中使用抽象工廠模式的是 JAVA awt的peer架構,通過抽象工廠模式來構建分屬於不同作業系統的peer構件。這個我也沒用過,瞭解即可。

關於Spring中工廠模式的使用會在後續Spring原始碼分析系列中給大家詳細分析,這裡就不重複了。

今天的學習就到此結束了,祝大家週末愉快。話說今天平安夜,大家都是在家寫程式碼嗎?

如果您對系列文章有任何意見,可以給我留言,感謝大家。

下面是一個接蘋果的姿勢,呼呼呼.....

菜鳥成長系列-工廠模式

相關文章