设计模式(1):简单工厂模式(Simple Factory Pattern)
1. 從一個(gè)簡單的例子開始
在眾多的設(shè)計(jì)原則中,有一條原則是這么說的:要針對接口編程,不要針對實(shí)現(xiàn)編程。
針對接口編程的話,可以使用不同的實(shí)現(xiàn)類來創(chuàng)建這個(gè)對象。比如需要一個(gè)List:
而當(dāng)我們需要使用LinkedList 的時(shí)候,我們需要做的只是將上面的new關(guān)鍵字后面的類型變?yōu)長inkedList 即可:
List<String> list=new LinkedList<>();而使用list 的其它地方的代碼都不需要修改,因?yàn)槲覀兪褂胠ist 的地方,使用的都是List 接口聲明的方法,而不管是ArrayList 還是LinkedList ,都實(shí)現(xiàn)了那些方法。
這么做的好處就是降低了對象之間的耦合,在需求發(fā)生修改的時(shí)候?qū)ΡM可能少的代碼進(jìn)行修改。
那么問題來了,對于需要使用List 對象的客戶(我們可以這么說,因?yàn)長ist 是我們程序員通過new 關(guān)鍵字創(chuàng)造出來的,那么使用它的人就是客戶),如果想通過一種方式控制需要的具體實(shí)現(xiàn)類型,比如提供一個(gè)String 類型的變量which,來告訴我們需要new 一個(gè)哪種類型的List 的話,應(yīng)該怎么做呢?
簡單的辦法就像下面這樣檢查這個(gè)which 是什么:
這樣的話,就通過一個(gè)變量來控制我們創(chuàng)建對象的實(shí)現(xiàn)類了。進(jìn)一步,可以把這個(gè)過程封裝成一個(gè)類的靜態(tài)方法,比如:
//這里僅僅演示這個(gè)方法,對于泛型什么的細(xì)節(jié)就不考慮了 public class ListFactory {public static List createList(String which) {if(which.equals("ArrayList")) return new ArrayList();else if(which.equals("LinkedList")) return new LinkedList();else //oops, no Concrete List you want} }這個(gè)靜態(tài)的方法,由于具有根據(jù)需求產(chǎn)生一個(gè)對象的功能,就叫做靜態(tài)工廠方法(Static Factory Method),用設(shè)計(jì)模式的行話來說,也是簡單工廠模式(Simple Factory Pattern)。
2. 簡單工廠模式的結(jié)構(gòu)
從上面那個(gè)簡單的例子可以看出來,這個(gè)模式其實(shí)挺簡單的,就是一個(gè)靜態(tài)方法(工廠),產(chǎn)生對象(產(chǎn)品),然后返回。
這和現(xiàn)實(shí)生活中的思想一樣。想一想,我們生活中會(huì)需要各種家用電器(洗衣機(jī)、冰箱等),但是我們會(huì)自己買零件組裝嗎?畢竟這個(gè)產(chǎn)品(對象)的創(chuàng)建過程挺費(fèi)勁的,我們只好把這個(gè)需求告訴工廠了(調(diào)用工廠方法),工廠知道需求后(根據(jù)我們的需求,比如上面的那個(gè)which)創(chuàng)建具體的產(chǎn)品并返回。完活兒。
這個(gè)模式的一般性結(jié)構(gòu)如下圖:
從上圖可以看到,這個(gè)簡單的模式有三個(gè)角色:
1.工廠類(Factory):這是工廠方法模式的核心(還有幾個(gè)工廠模式呢),生產(chǎn)產(chǎn)品的具體邏輯就在這里。在代碼的其他地方如果需要對象(產(chǎn)品),就會(huì)調(diào)用這個(gè)類的方法創(chuàng)建對象,通常使用一個(gè)Java類來實(shí)現(xiàn)。
2.抽象產(chǎn)品(Product):說是抽象的,是因?yàn)楣S生產(chǎn)的產(chǎn)品可以劃分為一個(gè)具體類別(比如汽車等)。但是這個(gè)名稱太抽象了,沒有具體指定是哪產(chǎn)品(比如化石燃料動(dòng)力還是電動(dòng)力等)。這個(gè)抽象產(chǎn)品的角色基本上使用一個(gè)Java接口或抽象類實(shí)現(xiàn)。
3.具體產(chǎn)品(Concrete Product): 這是工廠所生產(chǎn)的具體的產(chǎn)品(對象),通常是一個(gè)實(shí)現(xiàn)了抽象產(chǎn)品接口(或繼承了抽象類)的具體Java類。
下面是這個(gè)模式的基本代碼結(jié)構(gòu):
(1)工廠:
(2)抽象產(chǎn)品
//基本上,我們是面向接口編程,利于松耦合 public interface Product {}(3)具體產(chǎn)品
//具體的產(chǎn)品實(shí)現(xiàn)了抽象產(chǎn)品的接口,可能會(huì)有多個(gè)實(shí)現(xiàn),比如List接口的多個(gè)實(shí)現(xiàn) public class ConcreteProduct implements Product {}當(dāng)我們使用簡單工廠模式創(chuàng)建對象并使用的時(shí)候,會(huì)是這樣:
Product product = Factory.createProduct(); //使用product對象3. 簡單工廠模式的變形
上面給出的例子和結(jié)構(gòu)都是很簡單的,事實(shí)上現(xiàn)實(shí)中的例子都是復(fù)雜的,比如,產(chǎn)品可能會(huì)產(chǎn)生層狀結(jié)構(gòu):
這個(gè)時(shí)候,可以擴(kuò)展工廠類中的靜態(tài)方法,根據(jù)邏輯創(chuàng)建各個(gè)產(chǎn)品。
從這里就可以看出來了,簡單工廠模式的一個(gè)缺點(diǎn),就是當(dāng)產(chǎn)品增加或者發(fā)生變化時(shí),工廠類中的靜態(tài)方法也需要改變。確實(shí)是這樣,這個(gè)問題后面再討論。
還有,工廠類中可以有多個(gè)靜態(tài)方法:
如果只有一個(gè)具體的產(chǎn)品類的話,那么也就沒有抽象產(chǎn)品這個(gè)角色的必要了,這個(gè)時(shí)候工廠可以直接產(chǎn)生具體產(chǎn)品:
public class Factory {public static ConcreteProduct createProduct() {} }一些情況下,可以將工廠角色由抽象產(chǎn)品扮演,也就是說,這個(gè)抽象產(chǎn)品(這個(gè)時(shí)候,這個(gè)抽象產(chǎn)品就不是接口了,而是抽象類)可以創(chuàng)建具體的產(chǎn)品。其實(shí),Java中的java.text.DateFormat 類就是這樣的,下面會(huì)有具體的介紹。
更進(jìn)一步,可以將三個(gè)角色合并到一個(gè)角色中,一個(gè)產(chǎn)品自己就可以創(chuàng)造自己:
4. Java中的簡單工廠模式
上面已經(jīng)提到過了,在Java中的java.text.DateFomat 類就使用了簡單工廠模式。
DateFomat 類是一個(gè)抽象類,有一個(gè)子類是SimpleDateFormat。但是查看DateFomat 的代碼時(shí),發(fā)現(xiàn)這里面有一些靜態(tài)方法:
這些靜態(tài)方法返回的并不是通過構(gòu)造函數(shù)創(chuàng)建的DateFormat 對象(其實(shí)抽象類也不能實(shí)例化),而是使用了多態(tài)性,返回的是繼承了這個(gè)類的子類SimpleDateFormat。這樣,使用SimpleDateFormat 的代碼就不需要了解這個(gè)對象是如何創(chuàng)建的了。
5. 簡單工廠模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
簡單工廠模式的核心就是工廠類,為了創(chuàng)建符合需求的對象,這個(gè)類的方法需要有創(chuàng)建對象的具體邏輯。隨著對象的復(fù)雜,這個(gè)邏輯也會(huì)很復(fù)雜,這樣,使用對象的代碼就不需要花費(fèi)精力在創(chuàng)建對象上了。工廠負(fù)責(zé)對象的創(chuàng)建,客戶代碼僅僅提出需求,不用涉及對象的創(chuàng)建,只使用就可以了。這樣,達(dá)到了責(zé)任的分割。
缺點(diǎn)
不過這個(gè)模式也有缺點(diǎn)。具體就是當(dāng)出現(xiàn)新產(chǎn)品的時(shí)候,工廠類的靜態(tài)方法需要修改。比如,在前面的例子中,如果多了個(gè)List 實(shí)現(xiàn)類MagicList,如果客戶代碼需要這個(gè)對象的時(shí)候,工廠類的靜態(tài)方法就需要修改了:
public class ListFactory {public static List createList(String which) {if(which.equals("ArrayList")) return new ArrayList();else if(which.equals("LinkedList")) return new LinkedList();else if(which.equals("MagicList")) return new MagicList();else //oops, no Concrete List you want} }如果只是這么一層層摞的話,想想就鬧心(雖然可以使用switch,但邏輯上還是一樣)。
除此,還有一個(gè)缺點(diǎn),就是這里的例子僅僅只有一種類型的產(chǎn)品,如果產(chǎn)品種類多了的話,只放在一個(gè)工廠中就不合適了,這個(gè)問題可以在工廠方法模式中得到解決。
這就是簡單工廠模式,蠻簡單的。
參考資料:《Java與模式》,閻宏
總結(jié)
以上是生活随笔為你收集整理的设计模式(1):简单工厂模式(Simple Factory Pattern)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 探索 Java 隐藏的开销
- 下一篇: 设计模式(2):工厂方法模式(Facto