1. 介紹
工廠方法模式是一種創(chuàng)建型模式,是簡單工廠模式的一個升級版本,主要解決簡單工廠模式不符合開閉原則的問題。本文會先說一下簡單工廠模式,以及存在的問題,然后再引出工廠模式。
工廠設(shè)計模式是一種創(chuàng)建對象的設(shè)計模式,它的主要目的是通過定義一個接口來創(chuàng)建對象,使得子類決定實例化哪個類。這篇文章,我們將分析工廠模式是什么,它包含什么類型以及如何工作。
從整體上看,工廠模式可以分為三種主要類型:簡單工廠模式、工廠方法模式和抽象工廠模式。
1.1 簡單工廠模式
簡單工廠模式定義一個創(chuàng)建對象的類(工廠類)來負(fù)責(zé)創(chuàng)建其他對象(產(chǎn)品對象)的實例,這個類(工廠類)中的創(chuàng)建功能根據(jù)參數(shù)不同,創(chuàng)建不同的對象(產(chǎn)品對象)實例。這就存在一個問題,每增加一個產(chǎn)品類,就要修改工廠的創(chuàng)建邏輯,增加一個相應(yīng)的產(chǎn)品對象創(chuàng)建,這違反了程序設(shè)計的開閉原則。
概述
簡單工廠模式并不是一個正式的設(shè)計模式,而是一個創(chuàng)建對象的簡單方法。在簡單工廠模式中,通常會有一個工廠類,它根據(jù)參數(shù)的不同返回不同類型的對象。這個模式的優(yōu)點是簡單明了,但缺點是違背了開閉原則。
角色
工廠類:負(fù)責(zé)創(chuàng)建產(chǎn)品的實例,提供一個靜態(tài)方法供外部調(diào)用。
產(chǎn)品類:所有產(chǎn)品類都需實現(xiàn)相同的接口,用于定義產(chǎn)品的公共行為。
客戶端:通過工廠類來獲取產(chǎn)品實例,并使用這些實例。
設(shè)計模式之簡單工廠模式(C#語言描述)
嚴(yán)格意義上來說,簡單工廠模式并不屬于GoF的23種設(shè)計模式,但是它是學(xué)習(xí)其他工廠模式的基礎(chǔ)和前提條件。理解了簡單工廠模式,學(xué)習(xí)工廠方法模式和抽象工廠模式會比較容易一些。
簡單工廠模式的定義
定義一個工廠類,他可以根據(jù)不同的參數(shù)返回不同類的實例。通常情況下,被創(chuàng)建的類的實例通常都具有共同的父類。
簡單工廠模式又可以稱之為靜態(tài)工廠方法模式,因為創(chuàng)建對象實例的方法通常都是靜態(tài)方法。
在簡單工廠模式中,只需要記住一點。一個簡單的參數(shù)可以即可獲得所需的對象實例。
簡單工廠模式的UML圖如下:
簡單工廠模式包含三個角色:
1、工廠角色(Factory):該類是簡單工廠的核心,我們所需要的對象實例就是這個類幫我們創(chuàng)建的,它里面有一個靜態(tài)方法GetProduct(string productType),我們通過傳入產(chǎn)品類型(productType)來得到我們想要的產(chǎn)品。
2、抽象產(chǎn)品角色(AbstractProduct):該類是所有產(chǎn)品的父類。它可以是接口或者抽象類。只有這樣我們才能給工廠的GetProduct(string productType)方法定義返回值。
3、實體產(chǎn)品(ConcreteProductA,ConcreteProductB,ConcreteProductC):這些類是簡單工廠最終要創(chuàng)建的目標(biāo),也是客戶端最終想要的對象實例。
現(xiàn)在來假設(shè)一種場景,我們開發(fā)過程中,肯定會用到按鈕,圓形按鈕、正方形按鈕、矩形按鈕等等,在程序的很多地方我們都可能會復(fù)用這些按鈕。我們可以通過一個簡單工廠來幫助我們創(chuàng)建這些按鈕。
簡單工廠模式的類圖
SimpleFactory是個工廠類,負(fù)責(zé)創(chuàng)建對象(具體的工廠)。Product是要創(chuàng)建產(chǎn)品的抽象類,負(fù)責(zé)定義統(tǒng)一的接口。ProductA和ProductB是具體的產(chǎn)品類型,是Product的實現(xiàn)。我們看一下簡單工廠模式的優(yōu)缺點。
簡單工廠模式優(yōu)點:
實現(xiàn)簡單、結(jié)構(gòu)清晰。抽象出一個專門的工廠類來負(fù)責(zé)某類工廠對象的創(chuàng)建。使用者可以不關(guān)注具體工廠對象的類名,只要傳入相應(yīng)的參數(shù)就能創(chuàng)建對應(yīng)的工廠對象。簡單工廠模式的缺點:
不易擴(kuò)展,一旦新增產(chǎn)品類型,就得修改工廠的創(chuàng)建邏輯,不符合開閉原則。當(dāng)產(chǎn)品類型較多的時候,工廠創(chuàng)建邏輯會過于復(fù)雜,不利于維護(hù)。簡單工廠模式的應(yīng)用場景:
產(chǎn)品具有明顯的繼承關(guān)系,并且產(chǎn)品的類型不多或有限。所有的產(chǎn)品具體有相同的方法和類似的屬性,使用者不關(guān)心具體的類型,只希望傳入合適的參數(shù),然后返回合適的工廠對象。1.2 工廠方法模式
為了解決簡單工廠存在的,新增產(chǎn)品類型,就修改工廠的創(chuàng)建邏輯的問題。提出了工廠方法模式。工廠方法模式定義了一個創(chuàng)建對象(產(chǎn)品對象)的接口,讓子類(工廠類)來決定創(chuàng)建哪個類(產(chǎn)品類)的實例。它是一個類(工廠類)的實例化,延遲到其子類。
Product是要創(chuàng)建產(chǎn)品的抽象類,ProductA和ProductB是具體的產(chǎn)品類型。Factory是所有工廠的抽象類,負(fù)責(zé)定義統(tǒng)一的接口。ProductAFactory和ProductBFactory是具體的工廠類,分別負(fù)責(zé)產(chǎn)品ProductA和ProductB的創(chuàng)建。工廠方法類利用依賴倒置原則,高層模塊不依賴于底層的模塊,它們應(yīng)該依賴于抽象,面向接口編程,從而解決了簡單工廠模式違反軟件設(shè)計的開閉原則的問題。工廠方法模式也有一些優(yōu)缺點,接下來我們看一下。
工廠方法模式的優(yōu)點:
解決了簡單工廠模式違反開閉原則的問題,使程序擴(kuò)展性增強(qiáng)。工廠方法模式的缺點:
對于由多種分類(存在不同的分類方式組合)的產(chǎn)品,或者具有二級分類的產(chǎn)品,工廠方法模式不適用。工廠方法模式的應(yīng)用場景:
客戶端不知道它所需要的對象的類。工廠類希望通過其子類來決定創(chuàng)建哪個具體類的對象。2, 舉例
一般主程序只關(guān)注業(yè)務(wù)邏輯(抽象類接口),不關(guān)心具體的業(yè)務(wù)細(xì)節(jié)(抽象類接口的具體實現(xiàn))。這些業(yè)務(wù)細(xì)節(jié)就可以放在創(chuàng)建對象的邏輯(工廠創(chuàng)建邏輯)中,主程序只要設(shè)置指定的參數(shù),就能通過創(chuàng)建對象的邏輯獲取相應(yīng)對象(工廠對象)。
接下來會分別舉例說明簡單工廠模式、工廠方法模式的C語言實現(xiàn)。
2.1 簡單工廠模式舉例
#include
#include
enum {
PRODUCTA = 1,
PRODUCTB,
PRODUCTC,
};
// define product interface
typedef struct Product {
void (*show)(struct Product*);
} Product;
// define specific productA class
typedef struct ProductA {
Product product;
} ProductA;
// define productA show function
void ProductA_show(Product* product) {
printf("This is ProductA\n");
}
// define specific productB class
typedef struct ProductB {
Product product;
} ProductB;
// define productB show function
void ProductB_show(Product* product) {
printf("This is ProductB\n");
}
// define factory class
typedef struct Factory {
Product* (*create_product)(int);
} Factory;
// define factory create product logic
Product* factory_create_product(int type) {
Product* product = NULL;
switch (type) {
case 1:
product = (Product*)malloc(sizeof(ProductA));
product->show = ProductA_show;
break;
case 2:
product = (Product*)malloc(sizeof(ProductB));
product->show = ProductB_show;
break;
default:
printf("Invalid product type\n");
break;
}
return product;
}
int main() {
// implement factory instance
Factory simple_factory = { factory_create_product };
// use factory instance to create specific productA
Product* productA = simple_factory.create_product(PRODUCTA);
productA->show(productA);
free(productA);
// use factory instance to create specific productB
Product* productB = simple_factory.create_product(PRODUCTB);
productB->show(productB);
free(productB);
// use factory instance to create specific productC
Product* productC = simple_factory.create_product(PRODUCTC);
if (productC != NULL) {
free(productC);
}
return 0;
}
在這個例子中,定義了一個產(chǎn)品接口(Product)和兩個具體產(chǎn)品類(ProductA 和 ProductB),這兩個具體的產(chǎn)品類都實現(xiàn)了產(chǎn)品接口。此外,還定義了一個工廠類(Factory),它有一個 create_product 方法,通過該方法可以根據(jù)傳入的參數(shù)創(chuàng)建不同類型的產(chǎn)品對象。
在 main 函數(shù)中,使用工廠類實例化了一個工廠對象,然后用這個工廠對象創(chuàng)建了具體產(chǎn)品對象ProductA 和ProductB,并調(diào)用它們的 show 方法打印出相應(yīng)的信息。當(dāng)傳入一個無效的參數(shù)時,工廠類會打印出錯誤信息。
2.2 工廠方法模式舉例
#include
#include
// define product interface
typedef struct Product {
void (*show)(struct Product*);
} Product;
// define specific productA class
typedef struct ProductA {
Product product;
} ProductA;
// define productA show function
void ProductA_show(Product* product) {
printf("This is ProductA\n");
}
// define specific productB class
typedef struct ProductB {
Product product;
} ProductB;
// define productB show function
void ProductB_show(Product* product) {
printf("This is ProductB\n");
}
// define factory interface
typedef struct Factory {
Product* (*create_product)(struct Factory*);
} Factory;
// define specific factoryA class
typedef struct FactoryA {
Factory factory;
} FactoryA;
// inplement factoryA create_product function
Product* factoryA_create_product(Factory* factory) {
Product* product = (Product*)malloc(sizeof(Product));
product->show = ProductA_show;
return product;
}
// define specific factoryB class
typedef struct FactoryB {
Factory factory;
} FactoryB;
// inplement factoryB create_product function
Product* factoryB_create_product(Factory* factory) {
Product* product = (Product*)malloc(sizeof(Product));
product->show = ProductB_show;
return product;
}
int main() {
// create specific factoryA instance
FactoryA FactoryA;
FactoryA.factory.create_product = factoryA_create_product;
// create specific factoryB instance
FactoryB FactoryB;
FactoryB.factory.create_product = factoryB_create_product;
// use specific factory instance create specific product
Product* productA = FactoryA.factory.create_product(&FactoryA.factory);
productA->show(productA);
free(productA);
// use specific factory instance create specific product
Product* productB = FactoryB.factory.create_product(&FactoryB.factory);
productB->show(productB);
free(productB);
return 0;
}
在這個例子中,定義了一個產(chǎn)品接口(Product)和兩個具體產(chǎn)品類(ProductA 和 ProductB),這兩個具體的產(chǎn)品類都實現(xiàn)了產(chǎn)品接口。另外,還定義了一個工廠接口(Factory)和兩個具體工廠類(FactoryA 和 FactoryB),它們都實現(xiàn)了工廠接口。每個具體工廠類都有一個創(chuàng)建產(chǎn)品的方法,通過該方法可以創(chuàng)建相應(yīng)的產(chǎn)品對象。
在 main 函數(shù)中,首先創(chuàng)建了具體工廠對象FactoryA 和 FactoryB,然后通過它們的create_product方法分別創(chuàng)建了具體產(chǎn)品對象ProductA 和ProductB。最后,使用 free 函數(shù)釋放了創(chuàng)建的產(chǎn)品對象。