“在基類中定義創建對象的一個接口,讓子類決定實例化哪個類。工廠方法讓一個類的實例化延遲到子類中進行。”,這句話應該是工廠模式的高度概括。該模式用於封裝和管理對象的創建,是一種創建型模式。
一、工廠模式的分類:
根據產品是具體產品還是具體工廠可分爲簡單工廠模式和工廠方法模式,根據工廠抽象程度可分爲工廠方法模式和抽象工廠模式。
1.簡單工廠(Simple Factory)模式,又稱靜態工廠方法模式(Static Factory Method Pattern)。
2.工廠方法(Factory Method)模式,又稱多態性工廠(Polymorphic Factory)模式或虛擬構造子(Virtual Constructor)模式;
3.抽象工廠(Abstract Factory)模式,又稱工具箱(Kit 或Toolkit)模式。
二、簡單工廠模式
2.1 簡單工廠模式角色分配
- 抽象產品(Product)角色
- 具體產品(Concrete Product)角色
- 工廠(Factory)角色
2.2 簡單工廠實例
創建一個食品加工廠,能夠生產不同的食品。
1.抽象產品(Product)角色
public interface Food {
public void make();
}
2.具體產品(Concrete Product)角色
public class Tofu implements Food{
public Tofu() {
this.make(); ;
}
@Override
public void make() {
System.out.println("生產豆腐");
}
}
public class Juice implements Food{
public Juice() {
this.make();
}
@Override
public void make() {
System.out.println("生產果汁");
}
}
3.工廠(Factory)角色
public class FoodFactory {
public Food createFood(String foodType){
if("Tofu".equals(foodType)){
return new Tofu();
}else if ("Juice".equals(foodType)){
return new Juice();
}
return null;
}
}
測試與結果:
public class SimpleFactoryTest {
public static void main(String[] args) {
FoodFactory foodFactory = new FoodFactory();
Food tofu = foodFactory.createFood("Tofu");
Juice juice = (Juice) foodFactory.createFood("Juice");
}
}
生產豆腐
生產果汁
2.3 適用場景
簡單工廠模式是有一些缺陷的,比如我們生產新的產品,就需要同步改變createFood()方法中的邏輯,這就違背了“開放--封閉”原則(對擴展開放,意味着有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。對修改封閉,意味着類一旦設計完成,就可以獨立完成其工作,而不要對類進行任何修改。)
因此簡單工廠模式只適用於創建的對象較少的場景。
甚至嚴格的說,簡單工廠模式並不是23種常用的設計模式之一,它只算工廠模式的一個特殊情況。
2.4 利用反射優化簡單工廠模式
簡單工廠模式下,新增產品就要改變工廠方法中的判斷邏輯,違背開閉原則,使用反射可以規避這個缺陷。
其中抽象產品(Product)角色和具體產品(Concrete Product)角色不變,只要改變工廠角色中的方法。
public class FoodFactoryUpdate {
public static Object getClass(Class<? extends Food> clazz) {
Object result= null;
// 獲取類名(要創建哪種食品)
String name = clazz.getName();
try {
// 獲取該類的對象
result = Class.forName(name).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
}
測試類:
public class SimpleFactoryUpdateTest {
public static void main(String[] args) {
Tofu tofu = (Tofu) FoodFactoryUpdate.getClass(Tofu.class);
Juice juice = (Juice) FoodFactoryUpdate.getClass(Juice.class);
}
}
結果:
生產豆腐
生產果汁
這裏工廠類中使用了反射機制,即使你要新增新的產品也不需要改變工廠類中的方法,符合了“開閉”原則,這種方式很常見。
三、工廠模式
3.1 工廠模式和簡單工廠模式區別
“根據產品是具體產品還是具體工廠可分爲簡單工廠模式和工廠方法模式”,這句話是區別簡單工廠模式和工廠模式的關鍵。在簡單工廠模式中只會存在一個工廠類,所有的產品對象都有這唯一的工廠類創建。
工廠方法模式是簡單工廠的僅一步深化, 在工廠方法模式中,我們不再提供一個統一的工廠類來創建所有的對象,而是針對不同的對象提供不同的工廠。也就是說 每個對象都有一個與之對應的工廠。
3.2 適用場景
工廠方法模式應該是在工廠模式家族中是用的最多模式,一般項目中存在最多的就是這個模式。具體的:
- 一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
- 一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏
- 將創建對象的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無需關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中
3.3 工廠模式角色分配
- 抽象產品
- 具體產品
- 抽象工廠
- 具體工廠
3.4 工廠模式實例
和簡單工廠模式中唯一工廠負責生產所有產品相比,工廠方法模式將生成具體產品的任務分發給具體的產品工廠。
其中,Food類,Juice類、Tofu類不變,只是出現新的抽象工廠類和對應的具體工廠類。
抽象工廠類:
public interface AbstractFactory {
public Food createFood();
}
具體工廠類:
public class TofuFactory implements AbstractFactory {
@Override
public Food createFood() {
return new Tofu();
}
}
public class JuiceFactory implements AbstractFactory {
@Override
public Food createFood() {
return new Juice();
}
}
測試與結果:
public class factoryTest {
public static void main(String[] args) {
AbstractFactory tofuFactory = new TofuFactory();
AbstractFactory juiceFactory = new JuiceFactory();
Food tofu = tofuFactory.createFood();
Food juice = juiceFactory.createFood();
}
}
生產豆腐
生產果汁
上面就是增加了一個抽象工廠,定義了產品的生產接口,但不負責具體的產品。再通過定義多個具體實現工廠類來創建不同的對象。這樣不同類型的對象就由不同的工廠類來創建。
四、抽象工廠模式
4.1 抽象工廠模式與工廠模式區別
“根據工廠抽象程度可分爲工廠方法模式和抽象工廠模式”,這句話是區別抽象工廠模式和工廠模式的關鍵。工廠方法中,生產的是同一類產品,都是Food,生產的是單一產品。
抽象工廠模式是工廠方法的僅一步深化,在這個模式中的工廠類不單單可以創建一種產品,而是可以生產一整套產品(至少要生產兩個產品),這些產品必須相互是有關係或有依賴的,而工廠方法中的工廠是生產單一產品的工廠。
這裏“一整套產品”或者依賴關係怎麼理解?實際就像你生產了豆腐,順便生產裝豆腐的盤子;生產了果汁,順便生產裝果汁的瓶子。可能還是有些抽象吧?看下面的圖:
4.2 適用場景
- 和工廠方法一樣客戶端不需要知道它所創建的對象的類。
- 需要一組對象共同完成某種功能時,並且可能存在多組對象完成不同功能的情況。(同屬於同一個產品族的產品)
- 系統結構穩定,不會頻繁的增加對象。(因爲一旦增加就需要修改原有代碼,不符合開閉原則)
4.3 抽象工廠模式角色分配
- 抽象產品
- 具體產品
- 抽象工廠
- 具體工廠
4.4 抽象工廠模式實例
1.創建抽象產品接口(食品與裝它們的容器)
public interface Food {
public void make();
}
public interface Container {
public void load();
}
2.具體產品實現類
public class Tofu implements Food{
public Tofu() {
this.make(); ;
}
@Override
public void make() {
System.out.println("生產豆腐");
}
}
public class Juice implements Food{
public Juice() {
this.make();
}
@Override
public void make() {
System.out.println("生產果汁");
}
}
public class Bottle implements Container {
@Override
public void load() {
System.out.println("我是瓶子,裝果汁用的");
}
}
public class Panzi implements Container{
@Override
public void load() {
System.out.println("我是盤子,裝豆腐用的");
}
}
3.抽象工廠接口
public interface Factory {
public Food createFood();
public Container createContainer();
}
4.具體工廠實現類
public class TofuPanziFactory implements Factory{
@Override
public Food createFood() {
return new Tofu();
}
@Override
public Container createContainer() {
return new Panzi();
}
}
public class JuiceBottleFactory implements Factory {
@Override
public Food createFood() {
return new Juice();
}
@Override
public Container createContainer() {
return new Bottle();
}
}
測試與結果:
public class AbstractFactoryTest {
public static void main(String[] args) {
Factory factory = new TofuPanziFactory();
Tofu tofu = (Tofu) factory.createFood();
Panzi panzi = (Panzi) factory.createContainer();
panzi.load();
}
}
生產豆腐
我是盤子,裝豆腐用的