設(shè)計模式分類
首先先簡單說一下設(shè)計模式的分類設(shè)計模式可以分為三大類,分別是創(chuàng)建型設(shè)計模式、行為型設(shè)計模式以及結(jié)構(gòu)型設(shè)計模式。
創(chuàng)建型的設(shè)計模式:單例模式(Singleton)、構(gòu)建模式(Builder)、原型模式(Prototype)、抽象工廠模式(Abstract Factory)、工廠方法模式(Factory Method)
行為設(shè)計模式:策略模式(Strategy)、狀態(tài)模式(State)、責(zé)任鏈模式(Chain of Responsibility)、解釋器模式(Interpreter)、命令模式(Command)、觀察者模式(Observer)、備忘錄模式(Memento)、迭代器模式(Iterator)、模板方法模式(Template Method)、訪問者模式(Visitor)、中介者模式(Mediator)
結(jié)構(gòu)型設(shè)計模式:裝飾者模式(Decorator)、代理模式(Proxy)、組合模式(Composite)、橋連接模式(Bridge)、適配器模式(Adapter)、蠅量模式(Flyweight)、外觀模式(Facade)
簡單工廠模式
從設(shè)計模式的類型上來說,簡單工廠模式是屬于創(chuàng)建型模式,又叫做靜態(tài)工廠方法(StaticFactory Method)模式,但不屬于23種GOF設(shè)計模式之一。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現(xiàn)。
簡單工廠模式的一般結(jié)構(gòu),如圖所示:

簡單工廠模式存在三個組成部分,參考《Java與模式》一書,對應(yīng)于三個不同的角色:
l 工廠角色
l 抽象產(chǎn)品角色
l 具體產(chǎn)品角色
其實角色這個詞用的比較確切,能夠讓我們理解到,每個角色的不是單純地指一個類,可能是一組類所構(gòu)成了這個角色。下面對三個角色進(jìn)行描述:
1. 工廠角色
工廠角色負(fù)責(zé)產(chǎn)品的生產(chǎn)工作。在簡單工廠模式中,工廠類是一個具體的實現(xiàn)類,在系統(tǒng)設(shè)計中工廠類負(fù)責(zé)實際對象的創(chuàng)建工作。
工廠類(Factory)的特點(diǎn)是:它知道系統(tǒng)中都存在哪些能夠創(chuàng)建對象的具體類(ConcreteProduct),也知道該如何將創(chuàng)建的對象,以某種能夠屏蔽具體類實現(xiàn)細(xì)節(jié)的方式(AbstractProduct)提供給所需要的其他角色來使用該對象提供的數(shù)據(jù)和服務(wù)。
2.抽象產(chǎn)品角色
抽象產(chǎn)品角色是具體的產(chǎn)品的抽象。抽象就是將產(chǎn)品的共性抽取出來,可以直接暴露給客戶端(需要使用具體產(chǎn)品的角色),對所有的客戶端來說,從工廠中直接獲取到的原始產(chǎn)品的外部形態(tài)都是相同的,沒有任何的差別,包括數(shù)據(jù)和服務(wù)。這也就是說,具體客戶端應(yīng)該“秘密”掌握著某一個或一些具體產(chǎn)品的詳細(xì)資料(具體產(chǎn)品類型、數(shù)據(jù)和服務(wù)),然后根據(jù)具體客戶端(任何一個需要使用某種具體產(chǎn)品的數(shù)據(jù)和服務(wù)的實現(xiàn)類)需要什么樣的附加數(shù)據(jù)和服務(wù),進(jìn)行類類型轉(zhuǎn)換后,通過借助于對應(yīng)的具體產(chǎn)品對象來完成其職責(zé)。
抽象產(chǎn)品角色,在實際系統(tǒng)中可以定義為接口或者抽象類。
3.具體產(chǎn)品角色
具體產(chǎn)品實現(xiàn)類一定是抽象產(chǎn)品類的實現(xiàn)或擴(kuò)展。為了保證工廠類能夠創(chuàng)建對象,工廠類需要知道具體產(chǎn)品的創(chuàng)建方式,這就涉及到具體產(chǎn)品類所提供的構(gòu)造方法,以便,可能工廠類會向客戶端提供具體創(chuàng)建服務(wù)所需要的數(shù)據(jù)。例如:某個產(chǎn)品類需要通過一個賬號才能構(gòu)造其實例,所以工廠類必須根據(jù)它的創(chuàng)建需求,為客戶端提供一個帶賬號參數(shù)的生產(chǎn)方法,才能創(chuàng)建該具體產(chǎn)品類的對象。
也就是說,工廠類依賴于具體產(chǎn)品實現(xiàn)類。同樣,客戶端類是依賴于工廠類的。
通過上述三個角色的描述,我們應(yīng)該能夠了解,系統(tǒng)中哪些類能夠勝任上述的三個角色,并通過各類之間的關(guān)系,通過工廠模式來實現(xiàn)系統(tǒng)或者某個模塊。在實際的設(shè)計過程中,可能不存在完全與上述基本簡單工廠模式完全適應(yīng)的,需要根據(jù)具體的需求來調(diào)整簡單工廠模式的應(yīng)用。只要能夠?qū)崿F(xiàn)系統(tǒng)的良好設(shè)計,有時候變化才能滿足需要。
下面用一個簡單的例子來說明一下,給大家加深一下印象(例子來自于網(wǎng)絡(luò)):
運(yùn)動員.java
public interface 運(yùn)動員 {
public void 跑();
public void 跳();
}
足球運(yùn)動員.java
public class 足球運(yùn)動員 implements 運(yùn)動員 {
public void 跑(){
//跑啊跑
}
public void 跳(){
//跳啊跳
}
}
籃球運(yùn)動員.java
public class 籃球運(yùn)動員 implements 運(yùn)動員 {
public void 跑(){
//do nothing
}
public void 跳(){
//do nothing
}
}
體育協(xié)會.java
public class 體育協(xié)會 {
public static 運(yùn)動員 注冊足球運(yùn)動員(){
return new 足球運(yùn)動員();
}
public static 運(yùn)動員 注冊籃球運(yùn)動員(){
return new 籃球運(yùn)動員();
}
}
俱樂部.java
public class 俱樂部 {
private 運(yùn)動員 守門員;
private 運(yùn)動員 后衛(wèi);
private 運(yùn)動員 前鋒;
public void test() {
this.前鋒 = 體育協(xié)會.注冊足球運(yùn)動員();
this.后衛(wèi) = 體育協(xié)會.注冊足球運(yùn)動員();
this.守門員 = 體育協(xié)會.注冊足球運(yùn)動員();
守門員.跑();
后衛(wèi).跳();
}
}
以上就是簡單工廠模式的一個簡單實例,讀者應(yīng)該想象不用接口不用工廠而把具體類暴露給客戶端的那種混亂情形吧(就好像沒了體育總局,各個俱樂部在市場上自己胡亂的尋找仔細(xì)需要的運(yùn)動員),簡單工廠就解決了這種混亂。
工廠方法模式
工廠方法模式是類的創(chuàng)建模式,又叫虛擬構(gòu)造子(Virtual Constructor)模式或者多態(tài)性工廠(Polymorphic Factory)模式。 工廠方法模式的用意是定義一個創(chuàng)建產(chǎn)品對象的工廠接口,將實際工作推遲到子類中。
工廠方法模式是簡單工廠模式的衍生,解決了許多簡單工廠模式的問題。首先完全實現(xiàn)‘開-閉 原則’,實現(xiàn)了可擴(kuò)展。其次更復(fù)雜的層次結(jié)構(gòu),可以應(yīng)用于產(chǎn)品結(jié)果復(fù)雜的場合。工廠方法模式的對簡單工廠模式進(jìn)行了抽象。有一個抽象的Factory類(可以是抽象類和接口),這個類將不在負(fù)責(zé)具體的產(chǎn)品生產(chǎn),而是只制定一些規(guī)范,具體的生產(chǎn)工作由其子類去完成。在這個模式中,工廠類和產(chǎn)品類往往可以依次對應(yīng)。即一個抽象工廠對應(yīng)一個抽象產(chǎn)品,一個具體工廠對應(yīng)一個具體產(chǎn)品,這個具體的工廠就負(fù)責(zé)生產(chǎn)對應(yīng)的產(chǎn)品。
工廠方法模式角色與結(jié)構(gòu)
1.抽象工廠(Creator)角色:是工廠方法模式的核心,與應(yīng)用程序無關(guān)。任何在模式中創(chuàng)建的對象的工廠類必須實現(xiàn)這個接口。
2.具體工廠(Concrete Creator)角色:這是實現(xiàn)抽象工廠接口的具體工廠類,包含與應(yīng)用程序密切相關(guān)的邏輯,并且受到應(yīng)用程序調(diào)用以創(chuàng)建產(chǎn)品對象。
3.抽象產(chǎn)品(Product)角色:工廠方法模式所創(chuàng)建的對象的超類型,也就是產(chǎn)品對象的共同父類或共同擁有的接口。
4.具體產(chǎn)品(Concrete Product)角色:這個角色實現(xiàn)了抽象產(chǎn)品角色所定義的接口。某具體產(chǎn)品有專門的具體工廠創(chuàng)建,它們之間往往一一對應(yīng)。
工廠方法模式的一般結(jié)構(gòu),如圖所示:

我們在不改變產(chǎn)品類(“足球運(yùn)動員”類和“籃球運(yùn)動員”類)的情況下,寫一下工廠方法模式的例子:
運(yùn)動員.java
public interface 運(yùn)動員 {
public void 跑();
public void 跳();
}
足球運(yùn)動員.java
public class 足球運(yùn)動員 implements 運(yùn)動員 {
public void 跑(){
//跑啊跑
}
public void 跳(){
//跳啊跳
}
}
籃球運(yùn)動員.java
public class 籃球運(yùn)動員 implements 運(yùn)動員 {
public void 跑(){
//do nothing
}
public void 跳(){
//do nothing
}
}
體育協(xié)會.java
public interface 體育協(xié)會 {
public 運(yùn)動員 注冊();
}
足球協(xié)會.java
public class 足球協(xié)會 implements 體育協(xié)會 {
public 運(yùn)動員 注冊(){
return new 足球運(yùn)動員();
}
}
籃球協(xié)會.java
public class 籃球協(xié)會 implements 體育協(xié)會 {
public 運(yùn)動員 注冊(){
return new 籃球運(yùn)動員();
}
}
俱樂部.java
public class 俱樂部 {
private 運(yùn)動員 守門員;
private 運(yùn)動員 后衛(wèi);
private 運(yùn)動員 前鋒;
public void test() {
體育協(xié)會 中國足協(xié) = new 足球協(xié)會();
this.前鋒 = 中國足協(xié).注冊();
this.后衛(wèi) = 中國足協(xié).注冊();
守門員.跑();
后衛(wèi).跳();
}
}
很明顯可以看到,“體育協(xié)會”工廠類變成了“體育協(xié)會”接口,而實現(xiàn)此接口的分別是“足球協(xié)會”“籃球協(xié)會”等等具體的工廠類。
這樣做有什么好處呢?很明顯,這樣做就完全OCP了。如果需要再加入(或擴(kuò)展)產(chǎn)品類(比如加多個“乒乓球運(yùn)動員”)的話就不再需要修改工廠類了,而只需相應(yīng)的再添加一個實現(xiàn)了工廠接口(“體育協(xié)會”接口)的具體工廠類。
抽象工廠模式
抽象工廠模式是所有形態(tài)的工廠模式中最為抽象和最具一般性的一種形態(tài)。抽象工廠模式是指當(dāng)有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產(chǎn)品的具體的情況下,創(chuàng)建多個產(chǎn)品族中的產(chǎn)品對象。根據(jù)LSP原則,任何接受父類型的地方,都應(yīng)當(dāng)能夠接受子類型。因此,實際上系統(tǒng)所需要的,僅僅是類型與這些抽象產(chǎn)品角色相同的一些實例,而不是這些抽象產(chǎn)品的實例。換言之,也就是這些抽象產(chǎn)品的具體子類的實例。工廠類負(fù)責(zé)創(chuàng)建抽象產(chǎn)品的具體子類的實例。
先來認(rèn)識下什么是產(chǎn)品族: 位于不同產(chǎn)品等級結(jié)構(gòu)中,功能相關(guān)聯(lián)的產(chǎn)品組成的家族。還是讓我們用一個例子來形象地說明一下吧。

圖中的BmwCar和BenzCar就是兩個產(chǎn)品樹(產(chǎn)品層次結(jié)構(gòu));而如圖所示的BenzSportsCar和BmwSportsCar就是一個產(chǎn)品族。他們都可以放到跑車家族中,因此功能有所關(guān)聯(lián)。同理BmwBussinessCar和BenzSportsCar也是一個產(chǎn)品族。
抽象工廠模式中的有以下的四種角色:
抽象工廠(Abstract Factory)角色:擔(dān)任這個角色的是工廠方法模式的核心,它是與應(yīng)用系統(tǒng)商業(yè)邏輯無關(guān)的。
具體工廠(Concrete Factory)角色:這個角色直接在客戶端的調(diào)用下創(chuàng)建產(chǎn)品的實例。這個角色含有選擇合適的產(chǎn)品對象的邏輯,而這個邏輯是與應(yīng)用系統(tǒng)的商業(yè)邏輯緊密相關(guān)的。
抽象產(chǎn)品(Abstract Product)角色:擔(dān)任這個角色的類是工廠方法模式所創(chuàng)建的對象的父類,或它們共同擁有的接口。
具體產(chǎn)品(Concrete Product)角色:這個角色用以代表具體的產(chǎn)品。
Abstract Factory模式的結(jié)構(gòu):

抽象工廠模式就相當(dāng)于創(chuàng)建實例對象的new,由于經(jīng)常要根據(jù)類生成實例對象,抽象工廠模式也是用來創(chuàng)建實例對象的,所以在需要新的事例對象時便可以考慮是否使用工廠模式。雖然這樣做可能多做一些工作,但會給你系統(tǒng)帶來更大的可擴(kuò)展性和盡量少的修改量。
舉例來說:生產(chǎn)餐具和相應(yīng)食物的工廠,有兩個車間,其中一個車間用以生產(chǎn)餐具,一個車間用以生產(chǎn)相應(yīng)的食物。
當(dāng)消費(fèi)者消費(fèi)時,只需要向相應(yīng)的具體工廠請求具體餐具和具體食物便可以使用餐具消費(fèi)食物。
使用UML圖表示以上的描述如下:

圖1抽象工廠與具體工廠

圖2抽象餐具與具體餐具(生產(chǎn)車間)

圖3抽象食物與具體食物
注:圖中廚房單詞寫錯了:kitchen
每個具體工廠生產(chǎn)出來的具體產(chǎn)品根據(jù)不同工廠的不同各不相同,但是客戶使用產(chǎn)品的方法是一致的。比如客戶在得到餐具和食物之后,兩者的搭配是正確的(使用湯匙喝牛奶,使用刀子切面包)。
在本例子中有3個具體工廠AKitchen, BKitchen, BKitchen,分別生產(chǎn)牛奶和湯匙、面包和刀、肉和叉子。牛奶、面包和肉都實現(xiàn)了食物接口。湯匙、刀和叉子都實現(xiàn)了餐具接口。
抽象工廠的接口定義如下所示;
package abstractFactory;
public interface KitchenFactory{
public Food getFood();
public TableWare getTableWare();
}
抽象餐具的接口定義如下所示:
package abstractFactory;
public interface TableWare{
public String getTool();
}
抽象事物的接口定義如下所示:
package abstractFactory;
public interface Food{
public String getEatable();
}
而具體的實現(xiàn)也非常簡單,以AKitchen為例子
具體工廠AKitchen的定義如下所示;
package abstractFactory;
public class AKitchenimplements KitchenFactory{
public Food getFood(){
return new Milk();
}
public TableWare getTableWare(){
return new Spoon();
}
}
具體餐具(spoon)的定義如下所示:
package abstractFactory;
public class Spoonimplements TableWare{
public String getTool() {
return "spoon";
}
}
具體食物(milk)的定義如下所示:
package abstractFactory;
public class Milkimplements Food{
public String getEatable(){
return "milk";
}
}
客戶端的定義如下:
package abstractFactory;
public class Client{
public void eat(KitchenFactory k){
System.out.println("A person eat "+k.getFood().getEatable()
+" with "+k.getTableWare().getTool()+"!");
}
public static void main(String[] args){
Client client=new Client();
KitchenFactory kf =new AKitchen();
client.eat(kf);
kf=new BKitchen();
client.eat(kf);
kf=new CKitchen();
client.eat(kf);
}
}
在以下情況下應(yīng)當(dāng)考慮使用抽象工廠模式:
· 一個系統(tǒng)不應(yīng)當(dāng)依賴于產(chǎn)品類實例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié),這對于所有形態(tài)的工廠模式都是重要的。
· 這個系統(tǒng)有多于一個的產(chǎn)品族,而系統(tǒng)只消費(fèi)其中某一產(chǎn)品族。
· 同屬于同一個產(chǎn)品族的產(chǎn)品是在一起使用的,這一約束必須在系統(tǒng)的設(shè)計中體現(xiàn)出來。
· 系統(tǒng)提供一個產(chǎn)品類的庫,所有的產(chǎn)品以同樣的接口出現(xiàn),從而使客戶端不依賴于實現(xiàn)。