更多最新文章歡迎大家訪問我的個人博客😄:豆腐別館
上文介紹的簡單工廠模式提到了當所需生產的產品逐漸增多時,其違反了單一職責原則及開閉原則,而工廠方法模式,即是對簡單工廠模式的進一步抽象化。
一、模式的定義
- 定義創建產品對象的工廠接口,將產品對象的實際創建工作推遲到具體子工廠類當中。
二、模式的實現
接着上文的栗子,我們已經知道了男人都愛喫豆腐,還愛喫大豬蹄子。在簡單工廠方法模式中,我們是通過一個食物工廠類,再依據傳入的參數來確認需要生產哪種食物。同時我們也指出,當產品變多後這樣處理違反了我們的單一職責原則以及開閉原則。那我們要怎麼辦呢?
在解決問題前我們需要再明確遍問題出現的原因,不就是因爲人類變心喜歡喫上了越來越多的產品,導致工廠嚴重堵車嘛。知道原因了那可不就好辦了,打死變心的,呸, 一個工廠處理不過來我給你兩個(N)個工廠幫忙分擔行不行?emm,感覺好有道理,說幹就幹:
首先同樣的,能喫到哪樣東西的前提是要有這麼個東西,同時爲了方便用戶自定義具體食品,我們也需要新建一個食物父類,如下:
package com.doufuplus.patterns.factory.method;
/**
* 食物類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public abstract class Food {
public abstract void eat();
}
具體食物繼承自食物類:
package com.doufuplus.patterns.factory.method;
/**
* 豆腐類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public class Doufu extends Food {
@Override
public void eat() {
System.out.println("軟軟的豆腐...");
}
}
package com.doufuplus.patterns.factory.method;
/**
* 豬蹄類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public class Trotter extends Food {
@Override
public void eat() {
System.out.println("大大的豬蹄...");
}
}
- Round 1 簡單工廠拆分
- 新建豆腐工廠類:
package com.doufuplus.patterns.factory.method;
/**
* 豆腐工廠類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public class DoufuFactory {
/**
* 獲取豆腐
*/
public static Food getDoufu() {
return new Doufu();
}
}
- 新建豬蹄工具類:
package com.doufuplus.patterns.factory.method;
/**
* 豬蹄工廠類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public class TrotterFactory {
/**
* 獲取豬蹄
*/
public static Food getTrotter() {
return new Trotter();
}
}
- 現在工廠拆分也拆分了,想喫豆腐了就是這樣的:
好嘛,這下清晰多了,想喫什麼就從什麼工廠裏面拿,如果這時候我不想喫豆腐了,只想喫大豬蹄子補補要怎麼辦?那好辦,改改代碼唄,把上面的DoufuFactory.getDoufu();
改爲TrotterFactory.getTrotter()
就好了。這樣當然是可以的,只是這樣的改動就變成不止需要替換掉工廠類,還需要去找到更改後相應的方法,那麼有什麼更好的方式可以使代碼改動更少呢?
- Round 2 抽象/多態工廠拆分
身爲一個有志向的青年,本着能躺着就不站着的原則。我們需要針對上面提出的問題對代碼做出如下改進:
- 再次新建食物工廠接口,提供抽象方法,讓具體子類去決定生成何種食物:
package com.doufuplus.patterns.factory.method;
/**
* 食物工廠類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public interface FoodFactory {
/**
* 獲取食物
*/
Food getFood();
}
- 將原來的
DoufuFactory
以及FoodFactory
都實現上面的FoodFactory
,如:
package com.doufuplus.patterns.factory.method;
/**
* 豆腐工廠類
* 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
*
* @author 丶doufu
* @date 2019/11/27
*/
public class DoufuFactory implements FoodFactory {
/**
* 獲取豆腐
*/
@Override
public Food getFood() {
// ...
// 放蔥花撒鹽
// ...
return new Doufu();
}
}
- 那麼這時候我們的調用方式就是這樣的:
- 當豆腐喫膩了,只想喫豬蹄時,就是這樣的:
可以看到這時候的代碼改動就變少了,且不需要費心去找相應的方法,因爲方法名始終是一致的。
ps:當需要生成的產品不多且不會增加,一個具體工廠類就可以完成任務時,可刪除抽象工廠類。這時工廠方法模式將退化到上文的簡單工廠模式。
三、模式的結構
回顧下上面的代碼我們來捋一下工廠方法模式的主要類結構,如下:
- 抽象工廠(Abstract Factory):提供了創建產品的接口,調用者通過它訪問具體工廠的工廠方法 newProduct() 來創建產品。如代碼中的
FoodFactory
。 - 具體工廠(ConcreteFactory):主要是實現抽象工廠中的抽象方法,完成具體產品的創建。如代碼中的
DoufuFactory
、TrotterFactory
。 - 抽象產品(Product):定義了產品的規範,描述了產品的主要特性和功能。如代碼中的
Food
類。 - 具體產品(ConcreteProduct):實現了抽象產品角色所定義的接口,由具體工廠來創建,它同具體工廠之間一一對應。如代碼中的
Doufu
、Trotter
。
四、模式的優缺點
1. 優點
- 用戶只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體創建過程。(與簡單工廠模式類似)
- 在系統增加新的產品時只需要添加具體產品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則。
2. 缺點
- 每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,當工廠類越來越多時,易造成工廠氾濫,增加系統複雜度。
五、模式的應用場景
- 客戶只知道創建產品的工廠名,而不知道具體的產品名。如 TCL 電視工廠、海信電視工廠等。
- 創建對象的任務由多個具體子工廠中的某一個完成,而抽象工廠只提供創建產品的接口。
- 客戶不關心創建產品的細節,只關心產品的品牌。
參考:C語言中文網-設計模式