標籤:
這篇來介紹一下Factory 方法模式(Factory Method Pattern),在實際開發過程中我們都習慣於直接使用 new 關鍵字用來建立一個對象,可是有時候對象的創造需要一系列的步驟:你可能需要計算或取得對象的初始設定;選擇產生哪個子物件執行個體;或在產生你需要的對象之前必須先產生一些協助工具功能的對象,這個時候就需要瞭解該對象建立的細節,也就是說使用的地方與該對象的實現耦合在了一起,不利於擴充,為瞭解決這個問題就需要用到我們的Factory 方法模式,它適合那些建立複雜的對象的情境,Factory 方法模式也是一個使用頻率很高的設計模式。
PS:對技術感興趣的同鞋加群544645972一起交流。
設計模式總目錄
java/android 設計模式學習筆記目錄
特點
Factory 方法模式(Factory Method Pattern)定義了一個建立對象的介面,但由子類決定要執行個體化的類是哪一個,Factory 方法讓類把執行個體化延遲到子類,這樣的設計將對象的建立封裝其來,以便於得到更松耦合,更有彈性的設計。
Factory 方法模式是建立型設計模式之一,是結構較為簡單的一種模式,在我們平時的開發過程中應用也是非常的廣泛,比如 ArrayList,HashSet,與 Iterator 之間就能算是一種Factory 方法。
簡單原廠模式(Simple Factory)是Factory 方法模式的一種,Factory 方法模式的特點總結一下:
- 簡單原廠模式從某種意義上來說不算是真正的設計模式,但仍不失為一個簡單的方法,可以將客戶程式從具體類中解耦;
- Factory 方法模式使用繼承,把對象的建立委託給子類,子類實現Factory 方法來建立對象,也就是說允許將執行個體化延遲到子類進行;;
- Factory 方法模式是一個非常典型的“針對抽象編程,而不是具體類編程”例子。
UML類圖
為Factory 方法模式的uml類圖,幾個角色的分工也很明確,主要分為四大模組:
- 一是抽象工廠介面,其為Factory 方法模式的核心,它定義了一個工廠類所具備的基本行為;
- 二是具體工廠,其實現了具體的商務邏輯;
- 三是抽象產品介面,它定義了所有產品的公用行為;
- 四是具體產品,為實現抽象產品的某個具體產品的對象。
簡單原廠模式和Factory 方法模式的區別就在於簡單原廠模式將抽象工廠介面這個角色給精簡掉了,是Factory 方法模式的一個弱化版本。
從這種設計的角度來思考,Factory 方法模式是完全符合設計原則的,它將對象的建立封裝起來,以便於得到更松耦合,更有彈性的設計,而且Factory 方法模式依賴於抽象的介面,將執行個體化的任務交給子類去完成,有非常好的可擴充性。
樣本與源碼
我們以一個簡單的玩具工廠為例,工廠中生產小孩的玩具,女生的玩具和男生的玩具,先寫一個 IToy 的抽象產品介面用來定義玩具的基本行為模式,然後實現該介面產生幾個玩具的具體產品類 ChildrenToy,MenToy 和 WomenToy 類:
IToy.class
public interface IToy { /** * 名字 */ String getName(); /** * 價格 */ float price(); /** * 玩 */ void play();}
ChildrenToy.class
public class ChildrenToy implements IToy{ @Override public String getName() { return "toy car"; } @Override public float price() { return 10.5f; } @Override public void play() { Log.e("play", "a child is playing a toy car"); }}
MenToy.class
public class MenToy implements IToy{ @Override public String getName() { return "PS4"; } @Override public float price() { return 2300; } @Override public void play() { Log.e("play", "a man is playing GTA5 on ps4"); }}
WomenToy.class
public class WomenToy implements IToy{ @Override public String getName() { return "plush toy"; } @Override public float price() { return 200; } @Override public void play() { Log.e("play", "a woman is playing with a plush toy"); }}
完成產品的兩個角色之後,接下來要定義工廠類的兩個角色,根據Factory 方法模式和簡單原廠模式的不同,可以有兩種不同的寫法:
Factory 方法
Factory 方法模式需要先寫出一個工廠類的抽象介面來定義行為,這個時候根據實際情況我們可以分為兩種實現方式,第一種寫法會有多個 ConcreteFactory 的角色;第二種寫法只會有一個 ConcreteFactory 的角色,根據傳入的參數不同而返回不同的產品對象:
multi ConcreateFactory
IToyCreator.class
public interface IToyCreator { /** * 生產玩具 */ IToy createToy();}
ChildrenToyCreator.class
public class ChildrenToyCreator implements IToyCreator { private static final String TAG = "ChildrenToyCreator"; @Override public IToy createToy() { IToy toy = new ChildrenToy(); Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---"); toy.play(); return toy; }}
MenToyCreator.class
public class MenToyCreator implements IToyCreator { private static final String TAG = "MenToyCreator"; @Override public IToy createToy() { IToy toy = new MenToy(); Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---"); toy.play(); return toy; }}
WomenToyCreator.class
public class WomenToyCreator implements IToyCreator { private static final String TAG = "WomenToyCreator"; @Override public IToy createToy() { IToy toy = new WomenToy(); Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---"); toy.play(); return toy; }}
最後直接可以根據需要建立不同的 IToy 對象了,測試代碼如下:
IToyCreator toyCreator;switch (v.getId()) { case R.id.btn_child: toyCreator = new ChildrenToyCreator(); toyCreator.createToy(); break; case R.id.btn_men: toyCreator = new MenToyCreator(); toyCreator.createToy(); break; case R.id.btn_women: toyCreator = new WomenToyCreator(); toyCreator.createToy(); break;}
single ConcreteFactory
IToyCreator.class
public interface IToyCreator { /** * 生產玩具 */ <T extends IToy> IToy createToy(Class<T> clazz);}
ConcreteToyCreator.class
public class ConcreteToyCreator implements IToyCreator{ private static final String TAG = "ConcreteToyCreator"; @Override public <T extends IToy> IToy createToy(Class<T> clazz) { if (clazz == null){ throw new IllegalArgumentException("argument must not be null"); } try { IToy toy = clazz.newInstance(); Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---"); toy.play(); return toy; } catch (Exception e) { throw new UnknownError(e.getMessage()); } }}
這種寫法直接傳入一個 Class 對象,接著利用反射的方式進行對象的建立,可以說從某種意義上精簡了很多的工廠實作類別,不用一個具體產品類就對應需要一個具體工廠類了,下面為測試代碼:
IToyCreator toyCreator;switch (v.getId()) { case R.id.btn_child: toyCreator.createToy(ChildrenToy.class); break; case R.id.btn_men: toyCreator.createToy(MenToy.class); break; case R.id.btn_women: toyCreator.createToy(WomenToy.class); break;}
總結對比
以上的兩種方式當然最後都能夠成功列印出正確的結果:
單個工廠實作類別的方法對比前面的多個工廠實作類別的方法來說更加的簡潔和動態,而且對於以後新增的產品類來說也能夠不用修改原來的代碼,符合開閉原則,但是這種寫法在某些情況下是不適用的,比如不同的 IToy 對象設定了不同的建構函式,參數都不一樣,用反射來實現就不適用了,這個時候就只能為每一個具體產品類都定義一個對應的具體工廠類了。
簡單工廠
同樣是上面的代碼,具體工廠實作類別只有一個的時候,我們還是為工廠提供了一個抽象類別,那麼,如果將 IToyCreator 這個角色精簡掉,只留下 ConcreteToyCreator 的這個角色,將其中的產品產生方法設定為靜態應該也是沒問題的:
public class ToyCreator{ private static final String TAG = "ToyCreator"; public static <T extends IToy> IToy createToy(Class<T> clazz) { if (clazz == null){ throw new IllegalArgumentException("argument must not be null"); } try { IToy toy = clazz.newInstance(); Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---"); toy.play(); return toy; } catch (Exception e) { throw new UnknownError(e.getMessage()); } }}
像這樣的方式就稱為簡單原廠模式,上面也說過,是Factory 方法模式的一個弱化版本,缺點就是失去了被子類繼承的特性,所有的壓力都集中在工廠類中,不利於維護。
總結
總的來說,Factory 方法模式是一個很好的設計模式,它遵循了一個“儘可能讓事情保持抽象”的原則,松耦合的設計原則也能夠很好的符合開閉原則,將類的執行個體化延遲到子類,同時也擯棄了簡單原廠模式的缺點。
但是同時Factory 方法模式也有一些缺點,每次我們為Factory 方法添加新的產品時就要編寫一個新的產品類,同時還要引入抽象層,當產品種類非常多時,會出現大量的與之對應的工廠對象,這必然會導致類結構的複雜化,所以對於簡單的情況下,使用Factory 方法模式就需要考慮是不是有些“重”了。
源碼下載
https://github.com/zhaozepeng/Design-Patterns/tree/master/FactoryMethodPattern
引用
http://blog.csdn.net/jason0539/article/details/23020989
https://en.wikipedia.org/wiki/Factory_method_pattern
http://news.tuxi.com.cn/to/kf/satmaj/namtst.html
java/android 設計模式學習筆記(3)---Factory 方法模式