Java 設計模式之工廠方法模式與抽象工廠模式

一、前期回顧
上一篇《 ofollow,noindex">Java設計模式之單例模式 》詳細介紹了單例模式,介紹了單例模式的使用場景,優缺點,同時也寫了兩種常見的單例模式寫法,懶漢式單例模式和餓漢氏單例模式,當然,單例模式的寫法還有很多,比如,列舉單例模式,靜態內部類單例模式等。有興趣的可以自行查詢資料。本篇開始介紹單例模式的第二篇,工廠方法模式,以及工廠模式的升級版,抽象工廠模式。
二、工廠方法模式的定義與實踐
定義:Define an interface for creating an object,but let subclasses decide which class to instantiation.Factory Mehod lets a class defer instantiation to subclasses.翻譯過來就是,定義一個用於建立物件的介面,讓其子類決定例項化哪個類。工廠方法讓一個類的例項化延遲到其子類。
上面的定義說明好像很繞口,如果不理解沒關係,我們用個例子來說明這個定義:
//定義產品介面 public interface Iproduct { public void doSomething(); } //產品實現類 public class ProductA implements Iproduct { @Override public void doSomething() { System.out.println("我是產品A,我可以搞事情"); } } //定義工廠類介面 public interface IFactory { //建立產品工廠方法 < T extends Iproduct> T creatProduct(Class<T> clz); } //工廠方法實現類 public class ProductFactory implements IFactory { @Override public <T extends Iproduct> T creatProduct(Class<T> clz) { Iproduct product=null; try { product= clz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return (T) product; } } //場景類 public class Client { public static void main(String[] args) { IFactory factory=new ProductFactory(); Iproduct product= factory.creatProduct(ProductA.class); product.doSomething(); } } 複製程式碼
上述例子,實現了一個簡單的工廠方法模式,定義了一個工廠介面類,然後具體的工廠方法實現了建立物件的邏輯。看到這裡,有人肯定會問,這裡要new一個工廠類的例項,和我new一個具體的物件有什麼區別呢?反正都要自己new,幹嘛要搞一個工廠類這麼繞呢?對,沒錯。這裡是要new一個工廠類,但是上面的例子建立產品的物件是比較簡單,所以感覺不出來,如果ProductA的例項建立是一個非常複雜的過程,那麼這個時候我通過工廠方法模式建立物件的例項就很方便了。
肯定還有人問,我覺得上面這個方式還有點囉嗦,有沒有更簡便的方式實現工廠模式呢?答案是,有。我們接下來看看簡單工廠模式。簡單工廠方法模式就是上述工廠方法模式的簡單版本。我們只要把上述工廠類的介面去掉,然後把工廠實現類改為靜態類就可以實現簡單工廠方法模式了。程式碼是這樣的:
//這裡只去掉了介面,改為靜態方法 public class ProductFactory{ public static <T extends Iproduct> T creatProduct(Class<T> clz) { Iproduct product=null; try { product= clz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return (T) product; } } public class Client { public static void main(String[] args) { Iproduct product= ProductFactory.creatProduct(ProductA.class); product.doSomething(); } } 複製程式碼
這裡就不用自己去new 工廠類啦,程式碼也簡潔了很多。但是去掉了介面也就意味著後面的擴充套件是不友好的,所以違反了開閉原則。
三、抽象工廠模式的定義與實踐
定義:Provide an interface for creating families of related or dependent objects without specifying their concrete classes.翻譯: 提供一個建立相關或者相互依賴的物件的介面,並且無需指定他們的具體實現類。
這裡理解可能有點困難,為了便於理解,我們先引入兩個概念。
產品等級結構:產品等級結構即產品的繼承結構,如一個抽象類是汽車,其子類就是各個品牌的汽車,比如賓士,寶馬汽車。汽車和賓士就構成了一個產品等級結構,抽象汽車是父類,而具體品牌的汽車是其子類。
產品族 :在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如賓士工廠生產的賓士跑車,寶馬公司生產的跑車,賓士跑車位於賓士產品等級結構中,寶馬跑車位於寶馬產品等級結構中。
所以,汽車和品牌是一個產品等級結構,而每個品牌下面有多個產品線,也就是說賓士牌下面的汽車有多個產品族,比如跑車族,SUV族。賓士跑車和寶馬跑車是一個產品族,賓士SUV和寶馬SUV是一個產品族。所以上面定義中“相關或者相互依賴的物件”就是場景中包含, 同一個等級結構中有不同的產品族。 ,我們來用一個實際場景來說明下。
假設,我需要一個汽車工廠,汽車可以生產跑車和SUV車型,那麼這個時候用之前的工廠方法模式就不滿足了,方法工廠模式只能滿足生產一個產品,現在需要生產兩個產品。所以我們在方法工廠模式的基礎上改造下來滿足這個場景。
//汽車介面 public interface ICar { void dirver(); } //抽象汽車類 public abstract class AbstractProduct implements ICar{ @Override public void dirver() { System.out.println("我是汽車,我可以開動"); } /*** * 展示不同車的亮點*/ public abstract void showSpecial(); } //寶馬SUV實現類 public class BmwSuv extends AbstractProduct { @Override public void showSpecial() { System.out.println("我是寶馬牌SUV,我的特點就是多功能用途"); } } //寶馬跑車實現類 public class BmwProductRoadster extends AbstractProduct { @Override public void showSpecial() { System.out.println("我是寶馬跑車,我的特點就是跑的快"); } } //賓士SUV public class BenzSuv extends AbstractProduct { @Override public void showSpecial() { System.out.println("我是賓士牌SUV,我的特點就是多功能用途"); } } //賓士跑車 public class BenzProductRoadster extends AbstractProduct { @Override public void showSpecial() { System.out.println("我是賓士跑車,我的特點就是跑的快"); } } //抽象工廠類 public abstract class AbstractFactory { //建立跑車 public abstract < T extends ICar> T createRoadster(); //建立SUV public abstract < T extends ICar> T createSuv(); } //賓士汽車工廠類 public class BenzFactory extends AbstractFactory { @Override public <T extends ICar> T createRoadster() { return (T) new BenzProductRoadster(); } @Override public <T extends ICar> T createSuv() { return (T) new BenzSuv(); } } //寶馬汽車工廠類 public class BmwFactory extends AbstractFactory { @Override public <T extends ICar> T createRoadster() { return (T) new BmwProductRoadster(); } @Override public <T extends ICar> T createSuv() { return (T) new BmwSuv(); } } //場景類 public class Client { public static void main(String[] args) { AbstractFactory bmwFactory=new BmwFactory(); AbstractFactory benzFactory=new BenzFactory(); AbstractProduct bmwRoadster= bmwFactory.createRoadster(); AbstractProduct bmwSuv=bmwFactory.createSuv(); AbstractProduct benzRoadster=benzFactory.createRoadster(); AbstractProduct benzSuv=benzFactory.createSuv(); bmwRoadster.showSpecial(); bmwSuv.showSpecial(); benzRoadster.showSpecial(); benzSuv.showSpecial(); } } 複製程式碼
細心的讀者應該發現,我們的改造相對於工廠方法模式而言,只是抽象工廠模式新增了多一個建立方法。這也是抽象工廠模式和工廠方法模式最明顯的區別,抽象工廠模式可以滿足多型別,多業務的場景,比如汽車工廠可以生產多種型別汽車,就適用於抽象工廠模式。而普通工廠方法模式就是針對生產某一種物件而言比較適用。總結來說就是,抽象工廠模式適用於多產品族的情況,普通工廠方法模式適用於單產品族情況。
四、總結
上面介紹了工廠方法模式與抽象工廠模式的定義與實踐,同時也衍生了一個簡單工廠模式。我們先來總結下工廠模式的優點與缺點。
優點:
1.良好的封裝性,程式碼結構清晰,不用關注具體物件的建立過程,符合迪米特法則。
2.良好的擴充套件性,我們擴充套件了產品,只要新建對應的工廠類,實現自己的實現邏輯即可。
缺點:
1.對於抽象工廠模式,產品族的擴充套件比較困難,需要修改頂部抽象工廠類,這會導致所有實現類都要修改,不符合開閉原則。
2.對於簡單工廠模式,由於沒有介面和抽象父類的約束,從而也會導致擴充套件只能修改程式碼,不符合開閉原則。
最後我們列一個表格來總結下工廠方法模式,簡單工廠模式,抽象工廠模式的區別。
簡單工廠模式 | 工廠方法模式 | 抽象工廠模式 | |
---|---|---|---|
有無介面或抽象類 | 無 | 有 | 有 |
適用場景 | 一般不涉及擴充套件,單產品族 | 涉及單產品族,便於擴充套件 | 多產品族場景,可以擴充套件等級結構,不便於擴充套件產品族 |
實現複雜度 | 最簡單 | 簡單 | 一般 |