昨天在搜抽象工廠模式時,發現有好幾篇博文講的實現方式和我所認知的有出入,而且還看到某某教程講的也是錯誤的還在搜索引擎排第一。
大家講的簡單工廠模式和工廠方法模式都是沒問題的,關鍵到抽象工廠模式就不對了。
先從簡單工廠模式和工廠方法模式復習一下,然后再看看錯的是咋講的。已經對抽象工廠模式爛熟於心的請忽略此文。
簡單工廠模式:
有個一公共的產品接口,定義一些方法,各個具體的產品類都實現這個接口,然后有一個專門生產產品類實例的類被稱作工廠類,專門為客戶端生產產品實例,這個工廠的內部實現就是使用switch或ifelse進行邏輯判斷實現的,根據客戶端傳遞的參數不同,來決定什么時候創建什么樣的具體產品,生產出不同的具體產品返回給客戶端。這樣,這個工廠類就集成了所有產品的創建邏輯,它變得無所不知,這也讓它變成了核心。但這也成為了他的缺點,因為它將所有的邏輯集中放在一個類里,當產品接口有了新的產品實現類,工廠類需要增加代碼,判斷在什么時候創建該產品,就需要加入新的判斷邏輯。

ShoesFactory工廠
public class ShoesFactory{ public static final String NIKE = "NIKE"; public static final String ADIDAS = "ADIDAS"; public static Shoes getShoes(String brand) { Shoes shoes = null; switch (brand) { case "NIKE": shoes = new NikeShoes(); break; case "ADIDAS": shoes = new AdidasShoes(); break; default: shoes = null; } return shoes; } }
測試類
public class SimpleFactoryTest{ public void main(String arg[]){ Shoes nikeShoes = ShoesFactory.getShoes(ShoesFactory.NIKE); nikeShoes.desc(); Shoes adidasShoes = ShoesFactory.getShoes(ShoesFactory.ADIDAS); adidasShoes.desc(); } }
缺點:每增加一個產品,都要新寫一個類,然后在工廠類里增加分支,不符合java的開閉原則。
優點:對外屏蔽產品實例的創建,客戶端只負責“消費”,通過這種方式實現了對職責的分割。
工廠方法模式:
有一個公共的產品接口和一個公共的產品工廠接口,任何具體工廠必須實現這個工廠接口。將具體的產品的實例創建任務交給具體的工廠類,工廠的職責單一化,每個產品實例從公共的工廠類內抽離出來,降低了耦合度。這樣,當增加新的產品實例時,只需要增加相應的工廠類,符合開閉原則。

public interface ShoesFactory{ Shoes getShoes(); }
NikeShoesFactory
public class NikeShoesFactory implements ShoesFactory{ @Override public Shoes getShoes() { return new NikeShoes(); } }
AdidasShoesFactory
public class AdidasShoesFactory implements ShoesFactory{ @Override public Shoes getShoes() { return new AdidasShoes(); } }
當增加李寧鞋子時只需增加LiningShoes使其實現Shoes接口,然后增加LiningShoesFactory工廠類實現ShoesFactory接口使其負責生產LiningShoes的實例。
缺點:沒增加一個具體產品都要增加相應的工廠類,增加代碼量。
優點:符合開閉原則,易於擴展。
抽象工廠模式:
當有多個類型的產品時,產品類型之間橫向發展,如增加褲子、上衣等產品。在工廠方法模式里,只有一個產品接口類鞋子,各種鞋子如耐克鞋、阿迪鞋實現鞋子接口,而工廠類只需要寫2個就行了,耐克鞋工廠、阿迪鞋工廠類,各個工廠類職責單一只生產對應的一種具體產品。
而此時若增加了新的產品類型如褲子,他的具體產品阿迪褲子、耐克褲子,這個時候工廠類怎么實現呢?還像工廠方法類那樣嗎?如果業務上要求單一工廠原則(一個工廠只生產一個類型的產品),繼續使用工廠方法沒有問題,但如果需要提供一些列產品顯然使用工廠方法模式是不行的。
此時隨着多個產品類型的增加,產品之間形成一個個的產品族(耐克鞋、阿迪鞋是一個產品族,耐克褲子、阿迪褲子是一個產品族),而不同品牌之間形成產品等級。
網文實現方式
當我在網上搜索【抽象工廠模式】時,發現有不少文章給出的實現方式是如下圖這樣的。
抽象工廠模式結構:

代碼實現
抽象工廠類
public abstract class AbstractFactory2{ public abstract Shoes getShows(String brand); public abstract Pants getPants(String brand); }
PantsFactory 褲子工廠
public class PantsFactory extends AbstractFactory2{ //褲子工廠不能生產鞋子,空實現 返回null @Override public Shoes getShows(String brand) { return null; } @Override public Pants getPants(String brand) { Pants pants = null; switch (brand) { case "NIKE": pants = new NikePants(); break; case "ADIDAS": pants = new AdidasPants(); break; default: pants = null; } return pants; } }
public class ShoesFactory extends AbstractFactory2{ @Override public Shoes getShows(String brand) { Shoes shoes = null; switch (brand) { case "NIKE": shoes = new NikeShoes(); break; case "ADIDAS": shoes = new AdidasShoes(); break; default: shoes = null; } return shoes; } //鞋子工廠不能生產鞋子,空實現 返回null @Override public Pants getPants(String brand) { return null; } }
FactoryProductor2工廠生產者,通過不同的產品名稱創建具體工廠實例:(這里貌似簡單工廠模式)
public class FactoryProductor2{ public static final String SHOES = "SHOES"; public static final String PANTS = "PANTS"; public static AbstractFactory2 getFactory(String productType) { if (SHOES.equalsIgnoreCase(productType)) { return new ShoesFactory(); } else if (PANTS.equalsIgnoreCase(productType)) { return new PantsFactory(); } return null; } }
測試類
public class AbstractFactoryTest2{ public static void main(String[] args) { AbstractFactory2 factory = FactoryProductor2.getFactory(FactoryProductor2.PANTS); Pants nikePants = factory.getPants("NIKE"); Pants adidasPants = factory.getPants("ADIDAS"); nikePants.desc(); adidasPants.desc(); factory = FactoryProductor2.getFactory(FactoryProductor2.SHOES); Shoes nikeShoes = factory.getShows("NIKE"); Shoes adidasShows = factory.getShows("ADIDAS"); nikeShoes.desc(); adidasShows.desc(); } }
NikePants
AdidasPants
NikeShoes
AdidasShoes
分析正確性
大家看這樣的抽象工廠,每次創建產品都要知道產品的品牌,而且工廠類的另外一個方法被廢棄。
還美名其曰的說產品族難擴展,產品等級易擴展。那我們就來看看在這種方式下是怎么個難易法。
產品族難擴展: 當我們想增加上衣Coat產品時,按現在的方式擴展一下看看。首先我們需要新建接口Coat,新建實現類NikeCoat和AdidasCoat實現Coat接口。然后我們需要在抽象工廠類增加創建Coat產品的方法,同時修改現有的2個工廠完成Coat相關實現類的實例創建邏輯,其次我們需要在FactoryProductor2增加分支邏輯創建CoatFactory。
撇開FactoryProductor2類不說,總結下我們都做了什么:
- 新增產品接口Cost
- 修改抽象工廠,新增getCoat方法,修改原有工廠增加getCoat的空實現
- 新增具體工廠CoatFactory
產品等級易擴展: 當我們想增加李寧品牌時,我們需要做什么?pants包下創建LiNingPants實現pants接口,shoes包下創建LiNingShoes實現Shoes接口,然后修改factory包下的ShoesFactory工廠和PantsFactory工廠,各自增加分支判斷創建李寧牌的鞋子和褲子。
總結下我們都做了什么:
-
新增具體產品 LiNingPants 和 LiNingShoes
-
修改現有具體工廠類 ShoesFactory 工廠和 PantsFactory 工廠,增加創建李寧工廠分支
這樣看來產品族擴展起來確實難,而且不符合開閉原則,但產品等級擴展起來卻也不容易,也不符合開閉原則。
大家發現沒有,現在的抽象工廠的具體工廠內部是怎么實現的?還是使用switch或ifesle分支,不論擴展產品族還是產品等級都需要改具體工廠類,增加分支,都不符合開閉原則。這是正確的抽象工廠??
NO!!
這樣實現抽象工廠模式的不僅僅是搜到的幾篇博文,而且還有*鳥教程,搜索排第一:)

正確的抽象工廠
看下這個圖,發現具體工廠的名字變了,分別為AdidasFactory和NikeFactory,可想而知,以品牌命名具體的工廠只創建本品牌下的所有產品。
代碼實現
抽象工廠類
public abstract class AbstractFactory{ public abstract Shoes getShows(); public abstract Pants getPants(); }
這樣,每一個具體的工廠類,實現具體方法時,每個方法都能生成對應的產品,方法沒有空實現。而且每一個具體工廠內部沒有判斷分支。
NikeFactory耐克工廠
public class NikeFactory extends AbstractFactory{ @Override public Shoes getShows() { return new NikeShoes(); } @Override public Pants getPants() { return new NikePants(); } }
AdidasFactory阿迪工廠
public class AdidasFactory extends AbstractFactory{ @Override public Shoes getShows() { return new AdidasShoes(); } @Override public Pants getPants() { return new AdidasPants(); } }
而對於工廠生產者來說,通過不同的品牌名稱創建對應的工廠實例,
public class FactoryProductor{ public static final String NIKE = "NIKE"; public static final String ADIDAS = "ADIDAS"; public static AbstractFactory getFactory(String brand) { if (NIKE.equalsIgnoreCase(brand)) { return new NikeFactory(); } else if (ADIDAS.equalsIgnoreCase(brand)) { return new AdidasFactory(); } return null; } }
測試類
public class AbstractFactoryTest{ public static void main(String[] args) { AbstractFactory nikeFactory = FactoryProductor.getFactory(FactoryProductor.NIKE); Pants nikePants = nikeFactory.getPants(); Shoes nikeShows = nikeFactory.getShows(); nikePants.desc(); nikeShows.desc(); AbstractFactory adidasFactory = FactoryProductor.getFactory(FactoryProductor.ADIDAS); Shoes adidasShows = adidasFactory.getShows(); Pants adidasPants = adidasFactory.getPants(); adidasPants.desc(); adidasShows.desc(); } }
NikePants
NikeShoes
AdidasPants
AdidasShoes
分析
現在我們再來看一下,所謂的產品族難擴展,產品等級易擴展。
產品族難擴展: 當增加cost上衣產品時,首先需要新建包coat,新建接口Coat,新建實現類NikeCoat和AdidasCoat,然后在抽象工廠類新增抽象方法getCoat,ShoesFactory工廠和PantsFactory工廠分別實現該方法。
都做了什么:新建產品接口Coat,並創建實現類,抽象工廠新增抽象方法,原具體工廠類重寫新增的抽象方法。
產品等級易擴展: 當增加新的品牌時,比如增加李寧牌的鞋子和褲子,怎么擴展?首先創建LiningShoes和LiningPants類分別實現Shoes和Pants接口,然后新建LiningFactory工廠類使其繼承AbstractFactory抽象工廠類,重寫getShoes和getPants方法。
都做了什么:新增產品實現類,新增具體工廠類。
類圖

增加產品族
增加后,這個系統中有三個產品族,兩個產品等級

增加產品等級
增加后,這個系統中2個產品族,3個產品等級

作者:walkinger
鏈接:https://juejin.im/post/5d4b8de1e51d453b1f37eac4