一.概述
平時作項目跟使用第三方類庫的時候常常會用到工廠模式.什麼是工廠模式,簡單來講就是他的字面意思.給外部批量提供相同或者不一樣的產品,而外部不須要關心工廠是如何建立一個複雜產品的過程.因此工廠模式能夠下降模塊間的耦合,同時能夠提升擴展性(當有新的產品出現時,只須要擴展工廠就好了,上層模塊不敏感).html
工廠模式根據抽象的角度和層級的不一樣能夠分爲兩種模式:android
1.工廠方法模式 (Factory Method)ios
2.抽象工廠模式 (Abstract Factory)windows
二.實現
1.工廠方法模式緩存
工廠方法模式的特色是:iphone
一個抽象產品類(或接口),派生(或實現)多個真實產品類ide
一個抽象工廠類(或接口),派生(或實現)多個真實工廠類spa
通常來講標準的工廠方法模式須要一個工廠只生產一種產品,那麼當產品種類特別多的時候工廠的數量就特別多,因此一般會使用一些工廠方法模式的變種.設計
1.)標準工廠方法模式code
首先先介紹一下標準的工廠方法模式,不帶任何的變種.以工廠生產不一樣操做系統的手機爲例.
創建一個產品接口,提供一個獲取系統信息的方法.
?
1 2 3 4 5 6 |
/** * Created by jesse on 15-8-17. */ public interface IPhone { public void getOS(); } |
再根據IPhone接口實現Android,IOS,Blackberry三種手機.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class AndroidPhone implements IPhone { private final String TAG = AndroidPhone. class .getSimpleName(); @Override public void getOS() { Log.i(TAG, im Android); } } public class IosPhone implements IPhone { private final String TAG = IosPhone. class .getSimpleName(); @Override public void getOS() { Log.i(TAG, im IOS); } } public class BlackBerryPhone implements IPhone { private final String TAG = BlackBerryPhone. class .getSimpleName(); @Override public void getOS() { Log.i(TAG, im BlackBerry); } } |
標準的工廠方法模式都須要有抽象的工廠接口或者基類.
?
1 2 3 |
public abstract class IGenerator { public abstract IPhone generatePhone(String flag) throws Exception; } |
經過基類或者接口來實現真實的工廠類,這裏須要注意跟簡單工廠模式的不一樣,標準的工廠方法模式裏面一個工廠只生產一個產品,因此這裏要根據產品的種類劃分出來三個工廠,分別生產三種不一樣的產品.這種設計思想很是契合單一職責原則.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class AndroidGenerator extends IGenerator { @Override public IPhone generatePhone() { return new AndroidPhone(); } } public class IOSGenerator extends IGenerator { @Override public IPhone generatePhone() { return new IosPhone(); } } public class BlackberryGenerator extends IGenerator { @Override public IPhone generatePhone() { return new BlackBerryPhone(); } } |
在客戶端從工廠中得到產品並使用的過程當中都是經過接口進行訪問的,在建立產品的階段有效得下降了使用者和產品之間的耦合度.
?
1 2 3 4 5 6 7 8 9 10 11 |
IPhone android, ios, bb; IGenerator androidGenerator, iosGenerator, bbGenerator; androidGenerator = new AndroidGenerator(); iosGenerator = new IOSGenerator(); bbGenerator = new BlackberryGenerator(); android = androidGenerator.generatePhone(); ios = iosGenerator.generatePhone(); bb = bbGenerator.generatePhone(); android.getOS(); ios.getOS(); bb.getOS(); |
最終的運行效果顯而易見.

2).簡單工廠模式
接着分析一下簡單工廠模式,這是最簡單的變種,也叫作靜態工廠方法模式,從這個名字就能夠看出工廠的方法是靜態的.既然工廠方法是靜態的,那麼工廠就不能經過繼承進行擴展,若是有新增的產品,就只能在靜態方法裏面作修改因此從這個角度來講簡單工廠模式是不符合開閉原則的.
由於這是靜態工廠方法模式,因此工廠類就沒有接口或者虛基類來提供抽象.經過不一樣的Flag來初始化不一樣的產品.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class PhoneGenerator{ public final static String GENERATE_IOS = generate_ios; public final static String GENERATE_ANDROID = generate_android; public final static String GENERATE_BLACKBERRY = generate_blackberry; public static IPhone generatePhone(String flag) throws Exception { IPhone iPhone = null ; switch (flag){ case GENERATE_ANDROID: iPhone = new AndroidPhone(); break ; case GENERATE_IOS: iPhone = new IosPhone(); break ; case GENERATE_BLACKBERRY: iPhone = new BlackBerryPhone(); break ; default : throw new Exception(UNDEFINED FLAG); } return iPhone; } } |
對外部來講要使用工廠只須要把目標產品類傳過去就好了.運行結果跟1)中的是同樣的.
?
1 2 3 4 5 6 7 |
IPhone android, ios, bb; android = PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_ANDROID); ios = PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_IOS); bb = PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_BLACKBERRY); android.getOS(); ios.getOS(); bb.getOS(); |
3)結合反射的應用
假設須要加入一種搭載win10系統的手機,標準的工廠方法模式須要從新派生出來一個新的工廠來給客戶使用,簡單工廠模式也須要新增新的flag和case判斷去構造新的手機.有沒有什麼方法能夠儘可能避免這些修改呢?固然是有的,這裏能夠經過使用Class.forName 反射的方式來達到目的.
首先經過泛型來約束輸入輸出的參數類型,把異常拋到上層去處理並實現具體的工廠.
?
1 2 3 |
public abstract class IGenerator { public abstract <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception; }</t></t> |
?
1 2 3 4 5 6 7 |
public class PhoneGenerator extends IGenerator { public <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception { IPhone iPhone = null ; iPhone = (IPhone) Class.forName(clazz.getName()).newInstance(); return (T)iPhone; } }</t></t> |
經過這種裝載的方式去初始化產品就能夠達到上面描述的需求,能夠根據需求直接添加一個實現了IPhone接口的WindowsPhone產品而不須要修改工廠,客戶就能夠直接從工廠拿到WindowsPhone的手機去使用了.
4)產品類私有構造應用
產品類私有構造應用其實更偏向與一種規範.既然使用工廠模式了,那就是這些手機所有都要在工廠內部建立出來.這種應用就作了限制,使用私有構造就不容許外部經過new的方式來建立,而工廠則經過反射和更改訪問權限來建立產品.固然這個時候外部也能夠經過一樣的方式來建立對象,因此說這個應用更偏向於一種團隊規範.
?
1 2 3 4 5 6 7 8 9 |
public class PhoneGenerator extends IGenerator { public <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception { IPhone iPhone = null ; Class phone = Class.forName(clazz.getName()); phone.getDeclaredConstructor().setAccessible( true ); iPhone = (IPhone) phone.newInstance(); return (T)iPhone; } }</t></t> |
5)緩存對象
對於那些建立起來特別消耗資源或者特別複雜的對象,可使用下面的方式來進行一個長期的緩存.對於那些有訪問數量需求的對象也能夠創建緩存List,經過設置最大建立數來控制對象量級的峯值.例如JDBC的最大鏈接數等.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class PhoneGenerator extends IGenerator{ private Map<string, iphone= "" > map = new HashMap<>(); @Override public <t extends = "" iphone= "" > T generatePhone(Class<t> clazz) throws Exception{ IPhone iPhone = null ; if (map.containsKey(clazz.getName())) iPhone = map.get(clazz.getName()); else { iPhone = (IPhone) Class.forName(clazz.getName()).newInstance(); map.put(clazz.getName(), iPhone); } return (T) iPhone; } }</t></t></string,> |
2.抽象工廠模式
抽象工廠模式的特色:
多個抽象產品類(或接口),派生(或實現)多個真實產品類
一個抽象工廠類(或接口),派生(或實現)多個真實工廠類
抽象工廠模式其實也算是工廠方法模式的一種延伸,在工廠方法模式中全部的產品都是一個系列的,都是從IPhone那裏實現出的不一樣真實產品,因此對於外部來講他們都是手機產品,只須要關心手機的抽象接口就好了.然而又多個業務種類,而且這些業務有些依賴關係的時候,這種狀況下使用的工廠模式就是抽象工廠模式.
接着在抽象方法模式裏面的例子,在抽象工廠模式中咱們須要再多開一種產品,那就平板吧,而平板又分爲Android平板和IOS平板(這裏偷懶黑莓平板就不寫了),而且平板跟手機有不少共同的地方,例如相同的OS硬件設計等.既然是一個新的產品線了,那麼仍是先抽象出來平板的接口來.仍是老樣子,打印一下本身是什麼系統的.
?
1 2 3 |
public interface IPad { public void getBrand(); } |
接着經過IPad接口來實現兩個不一樣的平板.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class AndroidPad implements IPad { private final String TAG = AndroidPad. class .getSimpleName(); @Override public void getBrand() { Log.i(TAG, im Android pad); } } public class IOSPad implements IPad { private final String TAG = IOSPad. class .getSimpleName(); @Override public void getBrand() { Log.i(TAG, im IOS phone pad); } } |
在抽象工廠的接口的時候仍是繼續使用泛型來建立了,這樣也省的派生出來幾個不一樣的工廠.
?
1 2 3 4 |
public abstract class IGenerator { public abstract <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception; public abstract <t extends = "" ipad= "" >T generatePad(Class<t> clazz) throws Exception; }</t></t></t></t> |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class ProductGenerator extends IGenerator{ @Override public <t extends = "" iphone= "" > T generatePhone(Class<t> clazz) throws Exception{ IPhone iPhone = (IPhone) Class.forName(clazz.getName()).newInstance(); return (T) iPhone; } @Override public <t extends = "" ipad= "" > T generatePad(Class<t> clazz) throws Exception { IPad iPad = (IPad) Class.forName(clazz.getName()).newInstance(); return (T) iPad; } }</t></t></t></t> |
假設有一個客戶須要來我廠定製移動產品,A套餐中包含一個IOS手機和一個Android的平板,B套餐中包含一個Android手機和一個IOS平板.而這個套餐規則能夠經過工廠進行約束.這樣工廠就能勝任完成這個需求的任務了.
然而偏偏由於抽象工廠模式支持多種產品線,結果致使須要擴展一條新的產品的時候就會比較麻煩.假設須要新增一個屏幕貼膜產品,而且給每一個出廠的帶屏幕的產品都配一個.那麼要作的修改不只僅是要添加貼膜這個產品,還要修改從工廠的抽象到工廠的實現,還要修改工廠的約束.這是不符合開閉原則的.可是若是隻是擴展一個產品的子系列,例如要新增一個windows平板,抽象工廠模式和工廠方法模式同樣根本不須要修改工廠抽象和工廠實現,只須要新增產品就好了.