最常用的設計模式----工廠模式家族(簡單工廠模式, 工廠方法模式, 抽象工廠模式)

工廠模式是個系列,分為簡單工廠模式, 工廠方法模式, 抽象工廠模式,這三種模式也非常常用。這些模式最最經(jīng)典的就例子就是設計計算器。

? ? ?簡單工廠模式

? ? ? ? ?嚴格的說,簡單工廠模式并不是23種常用的設計模式之一,它只算工廠模式的一個特殊實現(xiàn)。簡單工廠模式在實際中的應用相對于其他2個工廠模式用的還是相對少得多,因為它只適應很多簡單的情況,最最重要的是它違背了我們在概述中說的開放-封閉原則。因為每次你要新添加一個功能,都需要在生switch-case 語句(或者if-else 語句)中去修改代碼,添加分支條件。

簡單工廠模式角色分配:

? ? ? ?Creator(產(chǎn)品創(chuàng)建者)

? ? ? ? ? ? ? 簡單工廠模式的核心,它負責實現(xiàn)創(chuàng)建所有實例的內(nèi)部邏輯。工廠類可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對象。

? ? ? Product ( 產(chǎn)品抽象類)

? ? ? ? ? ? ? 簡單工廠模式所創(chuàng)建的所有對象的父類,它負責描述所有實例所共有的公共接口。

? ? ? ?Concrete Product (具體產(chǎn)品)

? ? ? ? ? ? ? 是簡單工廠模式的創(chuàng)建目標,所有創(chuàng)建的對象都是充當這個角色的某個具體類的實例。

簡單工廠模式uml圖:


? ? ? 考慮下面一個事例: 加入你是一個商人,你做的的是手機生意?,F(xiàn)在你生產(chǎn)android 手機和iphone等,考慮到以后你可能還會生產(chǎn)其他手機例如ubuntu手機。假定你選擇了簡單工廠模式來實現(xiàn)。那么顯然,我們需要所有產(chǎn)品的抽象基類(Product) 即是Phone類:

class Phone

{

public:

virtual ~Phone(){};//在刪除的時候防止內(nèi)存泄露

virtual void call(string number) = 0;

};

然后我們需要具體的產(chǎn)品類 Concrete Product:?AndroidPhone 和 IosPhone

class AndroidPhone : public Phone

{

public:

void call(string number){ cout<<"AndroidPhone is calling..."<<endl;}

};

class IosPhone : public Phone

{

public:

void call(string number) { cout<<"IosPhone is calling..."<<endl;}

};

最后我們需要Creator

class PhoneFactory

{

public:

Phone* createPhone(string phoneName)

{

if(phoneName == "AndroidPhone")

{

return new AndroidPhone();

}else if(phoneName == "IosPhone")

{

return new IosPhone();

}

return NULL;

}

};

客戶端這樣實現(xiàn):

void main()

{

PhoneFactor factory;

Phone* myAndroid = factory.createPhone("AndroidPhone");

Phone* myIPhone = factory.createPhone("IosPhone");

if(myAndroid)

{

myAndroid->call("123");

delete myAndroid;

myAndroid = NULL;

}

if(myIPhone)

{

myIPhone->call("123");

delete? myIPhone;

myIPhone = NULL;

}

}

這就是簡單工廠方法,把所有的創(chuàng)建交給creator,creator 通過switch-case(或者if-else)語句來選擇具體創(chuàng)建的對象。簡單明了。但是就如上面所說,它最致命的問題的違背了開放-封閉原則。每次你要新添加一個功能,都要修改factor里面的createPhone代碼。 但是工廠方法模式可以解決這個問題。

? ?工廠方法模式

? ? ? 個人覺得工廠方法模式在工廠模式家族中是用的最多模式。上面說過了,如果簡單工廠模式,要添加一個新功能,比如我現(xiàn)在要增加WinPhone 的生產(chǎn),那么我要修改PhoneFactory中的createPhone 中的分支判斷條件。這違背了開放-封閉原則,那為什么不能將創(chuàng)建方法放到子類中呢?

? ? ? 工廠方法的定義 就是:?定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類,工廠方法使一個類的實例化延遲到其子類。

? ? ? 工廠方法模式角色:

? ? ? ? ? ?抽象工廠(Creator)角色:是工廠方法模式的核心,與應用程序無關(guān)。任何在模式中創(chuàng)建的對象的工廠類必須實現(xiàn)這個接口。

? ? ? ? ? ?具體工廠(Concrete Creator)角色:這是實現(xiàn)抽象工廠接口的具體工廠類,包含與應用程序密切相關(guān)的邏輯,并且受到應用程序調(diào)用以創(chuàng)建產(chǎn)品對象。

? ? ? ? ? ?抽象產(chǎn)品(Product)角色:工廠方法模式所創(chuàng)建的對象的超類型,也就是產(chǎn)品對象的共同父類或共同擁有的接口。

? ? ? ? ? ?具體產(chǎn)品(Concrete Product)角色:這個角色實現(xiàn)了抽象產(chǎn)品角色所定義的接口。某具體產(chǎn)品有專門的具體工廠創(chuàng)建,它們之間往往一一對應。

? ? ? ?工廠方法模式uml圖:



? ? ? 看定義看的暈乎乎的?那么我們來看代碼:

? ? ? ?產(chǎn)品接口,以及其相應的子類。

class Phone

{

public:

virtual ~Phone(){};//在刪除的時候防止內(nèi)存泄露

virtual void call(string number) = 0;

};

class AndroidPhone : public Phone

{

public:

void call(string number){ cout<<"AndroidPhone is calling..."<<endl;}

};

class IosPhone : public Phone

{

public:

void call(string number) { cout<<"IosPhone is calling..."<<endl;}

};

上面這個和簡單工廠方法還是一樣的。接下來不一樣的來了...

class PhoneFactory

{

public:

virtual ~PhoneFactory(){};

virtual Phone* createPhone() = 0;

};

class AndroidPhoneFactory : public PhoneFactory

{

public:

virtual Phone* createPhone()

{

return new AndroidPhone();

}

};

class IosPhoneFactory : public PhoneFactory

{

public:

virtual Phone* createPhone()

{

return new IosPhone();

}

};

? ? ? ? 工廠方法將PhoneFactory抽象成了基類,PhoneFactory的createPhone不在像以前那樣將所有的判斷塞到里面。而是改由其子類來實現(xiàn)創(chuàng)建功能,這感覺就是權(quán)力下放。

客戶端:

void main()

{

PhoneFactory*? androidCreator = new AndroidPhoneFactory();

PhoneFactory*? iosCreator = new IosPhoneFactory();

Phone* myAndroid = androidCreator->createPhone();

Phone* myIPhone = iosCreator->createPhone();

if(myAndroid)

{

myAndroid->call("123");

delete myAndroid;

myAndroid = NULL;

}

if(myIPhone)

{

myIPhone->call("123");

delete? myIPhone;

myIPhone = NULL;

}

delete androidCreator;

delete iosCreator;

}

? ? ? ? 在工廠方法模式中,核心工廠類不在負責產(chǎn)品的創(chuàng)建,而是將具體的創(chuàng)建工作交給子類去完成。也就是后所這個核心工廠僅僅只是提供創(chuàng)建的接口,具體實現(xiàn)方法交給繼承它的子類去完成。當我們的系統(tǒng)需要增加其他新功能時,只需要繼承PhoneFactory這個類,并且實現(xiàn)createPhone接口。 不需要對原工廠PhoneFactory進行任何修改,這樣很好地符合了“開放-封閉“原則。

? ? ? 雖然工廠方法模式滿足了"開放-封閉”原則,但是這個模式也仍然有缺點:每次增加一個產(chǎn)品時,都需要增加一個具體類和對象實現(xiàn)工廠,是的系統(tǒng)中類的個數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復雜度,同時也增加了系統(tǒng)具體類的依賴。這并不是什么好事。

抽象工廠模式

? ? ?在工廠方法模式中,其實我們有一個潛在意識的意識。那就是我們生產(chǎn)的都是同一類產(chǎn)品,例如我們生產(chǎn)的都是手機!那么現(xiàn)在假如現(xiàn)在我們又要生產(chǎn)平板了了呢?那么就要用到抽象工廠模式。我抽象工廠模式也用的比較多在工廠模式家族中,僅次于工廠方法模式。在了解抽象工廠模式之前,還是老生常談的理清下產(chǎn)品等級結(jié)構(gòu)和產(chǎn)品簇的概念。下面的圖還是老圖。但是我講講我的理解:



? ? ? ?產(chǎn)品等級結(jié)構(gòu):產(chǎn)品的等級結(jié)構(gòu)也就是產(chǎn)品的繼承結(jié)構(gòu)。我理解就是同一類產(chǎn)品,比如手機是一個系列,有android手機,ios手機,win手機,那么這個抽象類手機和他的子類就構(gòu)成了一個產(chǎn)品等級結(jié)構(gòu)。那其他的平板顯然不是和手機一個系列的,一個平板,一個是手機,所以他們是不同的產(chǎn)品等級結(jié)構(gòu)。

? ? ? ?產(chǎn)品族: 在抽象工廠模式中,產(chǎn)品族是指由同一個工廠生產(chǎn)的,位于不同產(chǎn)品等級結(jié)構(gòu)中的一組產(chǎn)品。比如分為android產(chǎn)品,和ios產(chǎn)品。其中一個ios產(chǎn)品包含ios手機和ios平板。顯然ios手機和ios平板不是同一個產(chǎn)品等級結(jié)構(gòu)的,因為一個是手機,一個是平板。但他們是同一個產(chǎn)品簇---都是ios產(chǎn)品。

? ? ? ?希望大家通過上面的例子大家明白了這兩個概念。

? ? ? ?抽象工廠模式的Uml 圖:


? ? ?接著上面的話題,現(xiàn)在假如我要增加對平板的支持,那么我們肯定先添加兩個產(chǎn)品等級結(jié)構(gòu),一個是手機,一個是平板:

//產(chǎn)品等級結(jié)構(gòu)--手機

class Phone

{

public:

virtual ~Phone(){};//在刪除的時候防止內(nèi)存泄露

virtual void call(string number) = 0;

};

class AndroidPhone : public Phone

{

public:

void call(string number){ cout<<"AndroidPhone is calling..."<<endl; }

};

class IosPhone : public Phone

{

public:

void call(string number) { cout<<"IosPhone is calling..."<<endl; }

};

//產(chǎn)品等級結(jié)構(gòu)--平板

class Pad

{

public:

virtual ~Pad(){};

virtual void playMovie() = 0;

};

class AndroidPad : public Pad

{

public:

virtual void playMovie(){ cout<<"AndriodPad is playing movie..."<<endl; }

};

class IosPad : public Pad

{

public:

virtual void playMovie(){ cout<<"IosPad is playing movie..."<<endl; }

};

然后具體的工廠我們整個工廠是生產(chǎn)移動設備的所以我們?nèi)∶麨镸obileFactory,然后工廠可以生產(chǎn)平板和手機,故有了createPhone 和createPad兩個接口。

class MobileFactory

{

public:

virtual ~MobileFactory(){};

virtual Phone* createPhone() = 0;

virtual Pad* createPad() = 0;

};

接著是 android 產(chǎn)品簇 的工廠類,負責生產(chǎn)android 的手機和平板:

class AndroidFactory : public MobileFactory

{

public:

Phone* createPhone()

{

return new AndroidPhone();

}

Pad* createPad()

{

return new AndroidPad();

}

};

接著是ios的產(chǎn)品簇的工廠類,負責生產(chǎn)ios的手機和平板:

class IosFactory : public MobileFactory

{

public:

Phone* createPhone()

{

return new IosPhone();

}

Pad* createPad()

{

return new IosPad();

}

};

最后客戶端這樣實現(xiàn):

void main()

{

MobileFactory*? androidCreator = new AndroidFactory();

MobileFactory*? iosCreator = new IosFactory();

Phone* myAndroidPhone = androidCreator->createPhone();

Pad* myAndroidPad = androidCreator->createPad();

Phone* myIosPhone = iosCreator->createPhone();

Pad* myIosPad = iosCreator->createPad();

myAndroidPhone->call("123");

myAndroidPad->playMovie();

myIosPhone->call("123");

myIosPad->playMovie();

//這里沒有做釋放和判斷,請自己判斷和釋放

}

總結(jié):

? ? 抽象工廠模式適用于那些有多種產(chǎn)品的產(chǎn)品簇,并且每次使用其中的某一產(chǎn)品簇的產(chǎn)品。

? ? 缺點 : 抽象工廠模式的添加新功能也非常麻煩,比工廠方法模式都還要復雜的多。

? ? 優(yōu)點:?當一個產(chǎn)品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產(chǎn)品族中的對象。

上面說到抽象工廠和工廠方法模式的功能添加都非常復雜,那么我們有沒有什么辦法可以簡化呢? 答案是肯定有的: 那就是工廠模式 + ?配置文件 + 反射。具體怎么實現(xiàn),請看下回分解。?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容