C++ 深入淺出工廠模式(進階篇) - 小林coding - 部落格園
介紹
前文初始篇C++ 深入淺出工廠模式(初始篇),主要闡述了簡單工廠模式、工廠方法模式和抽象工廠模式的結構、特點和缺陷等。以上三種方式,在新增産品時,要麼修改工廠類,要麼需新增具體的工廠類,說明工廠類的封裝性還不夠好。
本文進階篇,主要是将工廠類的封裝性提高,達到新增産品時,也不需要修改工廠類,不需要新增具體的工廠類。封裝性高的工廠類特點是擴充性高、複用性也高。
模闆工廠
針對工廠方法模式封裝成模闆工廠類,那麼這樣在新增産品時,是不需要新增具體的工廠類,減少了代碼的編寫量。
UML圖:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yY2M2YwQjN4kjZ5IWOyITN2EjMjVTOmFTOkFmN5UmM38CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
模闆工廠代碼:
-
和Shoes
,分别為鞋子和衣服的抽象類(基類)Clothe
-
和NiKeShoes
,分别為耐克鞋子和優衣庫衣服具體産品類。UniqloClothe
// 基類 鞋子
class Shoes
{
public:
virtual void Show() = 0;
virtual ~Shoes() {}
};
// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
void Show()
{
std::cout << "我是耐克球鞋,我的廣告語:Just do it" << std::endl;
}
};
// 基類 衣服
class Clothe
{
public:
virtual void Show() = 0;
virtual ~Clothe() {}
};
// 優衣庫衣服
class UniqloClothe : public Clothe
{
public:
void Show()
{
std::cout << "我是優衣庫衣服,我的廣告語:I am Uniqlo" << std::endl;
}
};
-
為抽象模闆工廠類,其中模闆參數:AbstractFactory
産品抽象類,如AbstractProduct_t
、Shoes
Clothe
-
為具體模闆工廠類,其中模闆參數:ConcreteFactory
産品抽象類(如AbstractProduct_t
、Shoes
),Clothe
産品具體類(如ConcreteProduct_t
、NiKeShoes
)UniqloClothe
// 抽象模闆工廠類
// 模闆參數:AbstractProduct_t 産品抽象類
template <class AbstractProduct_t>
class AbstractFactory
{
public:
virtual AbstractProduct_t *CreateProduct() = 0;
virtual ~AbstractFactory() {}
};
// 具體模闆工廠類
// 模闆參數:AbstractProduct_t 産品抽象類,ConcreteProduct_t 産品具體類
template <class AbstractProduct_t, class ConcreteProduct_t>
class ConcreteFactory : public AbstractFactory<AbstractProduct_t>
{
public:
AbstractProduct_t *CreateProduct()
{
return new ConcreteProduct_t();
}
};
-
函數,根據不同類型的産品,構造對應的産品的工廠對象,便可通過對應産品的工廠對象建立具體的産品對象。main
int main()
{
// 構造耐克鞋的工廠對象
ConcreteFactory<Shoes, NiKeShoes> nikeFactory;
// 建立耐克鞋對象
Shoes *pNiKeShoes = nikeFactory.CreateProduct();
// 列印耐克鞋廣告語
pNiKeShoes->Show();
// 構造優衣庫衣服的工廠對象
ConcreteFactory<Clothe, UniqloClothe> uniqloFactory;
// 建立優衣庫衣服對象
Clothe *pUniqloClothe = uniqloFactory.CreateProduct();
// 列印優衣庫廣告語
pUniqloClothe->Show();
// 釋放資源
delete pNiKeShoes;
pNiKeShoes = NULL;
delete pUniqloClothe;
pUniqloClothe = NULL;
return 0;
}
- 輸出結果:
[[email protected] factory]# ./templateFactory
我是耐克球鞋,我的廣告語:Just do it
我是優衣庫衣服,我的廣告語:I am Uniqlo
産品注冊模闆類+單例工廠模闆類
前面的模闆工廠雖然在新增産品的時候,不需要新增具體的工廠類,但是缺少一個可以統一随時随地擷取指定的産品對象的類。
還有改進的空間,我們可以把産品注冊的對象用
std::map
的方式儲存,通過
key-valve
的方式可以輕松簡單的擷取對應的産品對象執行個體。
實作大緻思路:
- 把産品注冊的功能封裝成産品注冊模闆類。注冊的産品對象儲存在工廠模闆類的
,便于産品對象的擷取。std::map
- 把擷取産品對象的功能封裝成工廠模闆類。為了能随時随地擷取指定産品對象,則把工廠設計成單例模式。
UML圖:
産品注冊模闆類+單例工廠模闆類:
-
為産品注冊抽象類,模闆參數IProductRegistrar
表示的類是産品抽象類(如ProductType_t
、Shoes
)。提供了産品對象建立的純虛函數Clothe
。CreateProduct
-
為工廠模闆類,模闆參數ProductFactory
表示的類是産品抽象類(如ProductType_t
、Shoes
)。用于儲存注冊産品對象到Clothe
中和擷取對應的産品對象。std::map
-
為産品注冊模闆類,模闆參數ProductRegistrar
表示的類是産品抽象類(如ProductType_t
、Shoes
),Clothe
表示的類是具體産品(如ProductImpl_t
、NikeShoes
)。用于注冊産品到工廠類和建立産品執行個體對象。UniqloClothe
// 基類,産品注冊模闆接口類
// 模闆參數 ProductType_t 表示的類是産品抽象類
template <class ProductType_t>
class IProductRegistrar
{
public:
// 擷取産品對象抽象接口
virtual ProductType_t *CreateProduct() = 0;
protected:
// 禁止外部構造和虛構, 子類的"内部"的其他函數可以調用
IProductRegistrar() {}
virtual ~IProductRegistrar() {}
private:
// 禁止外部拷貝和指派操作
IProductRegistrar(const IProductRegistrar &);
const IProductRegistrar &operator=(const IProductRegistrar &);
};
// 工廠模闆類,用于擷取和注冊産品對象
// 模闆參數 ProductType_t 表示的類是産品抽象類
template <class ProductType_t>
class ProductFactory
{
public:
// 擷取工廠單例,工廠的執行個體是唯一的
static ProductFactory<ProductType_t> &Instance()
{
static ProductFactory<ProductType_t> instance;
return instance;
}
// 産品注冊
void RegisterProduct(IProductRegistrar<ProductType_t> *registrar, std::string name)
{
m_ProductRegistry[name] = registrar;
}
// 根據名字name,擷取對應具體的産品對象
ProductType_t *GetProduct(std::string name)
{
// 從map找到已經注冊過的産品,并傳回産品對象
if (m_ProductRegistry.find(name) != m_ProductRegistry.end())
{
return m_ProductRegistry[name]->CreateProduct();
}
// 未注冊的産品,則報錯未找到
std::cout << "No product found for " << name << std::endl;
return NULL;
}
private:
// 禁止外部構造和虛構
ProductFactory() {}
~ProductFactory() {}
// 禁止外部拷貝和指派操作
ProductFactory(const ProductFactory &);
const ProductFactory &operator=(const ProductFactory &);
// 儲存注冊過的産品,key:産品名字 , value:産品類型
std::map<std::string, IProductRegistrar<ProductType_t> *> m_ProductRegistry;
};
// 産品注冊模闆類,用于建立具體産品和從工廠裡注冊産品
// 模闆參數 ProductType_t 表示的類是産品抽象類(基類),ProductImpl_t 表示的類是具體産品(産品種類的子類)
template <class ProductType_t, class ProductImpl_t>
class ProductRegistrar : public IProductRegistrar<ProductType_t>
{
public:
// 構造函數,用于注冊産品到工廠,隻能顯示調用
explicit ProductRegistrar(std::string name)
{
// 通過工廠單例把産品注冊到工廠
ProductFactory<ProductType_t>::Instance().RegisterProduct(this, name);
}
// 建立具體産品對象指針
ProductType_t *CreateProduct()
{
return new ProductImpl_t();
}
};
-
函數,通過main
注冊各種不同類型産品,在統一由ProductRegistrar
單例工廠擷取指定的産品對象。ProductFactory
int main()
{
// ========================== 生産耐克球鞋過程 ===========================//
// 注冊産品種類為Shoes(基類),産品為NiKe(子類)到工廠,産品名為nike
ProductRegistrar<Shoes, NiKeShoes> nikeShoes("nike");
// 從工廠擷取産品種類為Shoes,名稱為nike的産品對象
Shoes *pNiKeShoes = ProductFactory<Shoes>::Instance().GetProduct("nike");
// 顯示産品的廣告語
pNiKeShoes->Show();
// 釋放資源
if (pNiKeShoes)
{
delete pNiKeShoes;
}
// ========================== 生産優衣庫衣服過程 ===========================//
// 注冊産品種類為Clothe(基類),産品為UniqloClothe(子類)到工廠,産品名為uniqlo
ProductRegistrar<Clothe, UniqloClothe> adidasShoes("uniqlo");
// 從工廠擷取産品種類為Shoes,名稱為adidas的産品對象
Clothe *pUniqloClothe = ProductFactory<Clothe>::Instance().GetProduct("uniqlo");
// 顯示産品的廣告語
pUniqloClothe->Show();
// 釋放資源
if (pUniqloClothe)
{
delete pUniqloClothe;
}
return 0;
}
- 輸出結果:
[[email protected] factory]# ./singleFactory
我是耐克球鞋,我的廣告語:Just do it
我是優衣庫衣服,我的廣告語:I am Uniqlo
總結
将工廠方法模式改良成模闆工廠,雖然可以解決産品新增時,不需要新增具體工廠類,但是缺少一個可以随時随地擷取産品對象的方式,說明還有改進的空間。
将模闆工廠改良成産品注冊模闆類+單例工廠模闆類,産品注冊模闆類用于注冊不同類型的産品,單例工廠模闆類用于擷取指定已注冊的産品對象。這種方式,可以把工廠模式中産品的注冊和擷取的主要功能很好的抽象成兩個類,并且使用單例模式使得工廠類可以随時随地擷取已注冊的産品對象。
是以産品注冊模闆類+單例工廠模闆類的工廠模式,達到了開閉法則,并且擴充性高和封裝度高。