介绍

前文初始篇C++ 深入浅出工厂模式(初始篇),主要阐述了简单工厂模式、工厂方法模式和抽象工厂模式的结构、特点和缺陷等。以上三种方式,在新增产品时,要么修改工厂类,要么需新增具体的工厂类,说明工厂类的封装性还不够好。

本文进阶篇,主要是将工厂类的封装性提高,达到新增产品时,也不需要修改工厂类,不需要新增具体的工厂类。封装性高的工厂类特点是扩展性高、复用性也高。

模板工厂

针对工厂方法模式封装成模板工厂类,那么这样在新增产品时,是不需要新增具体的工厂类,减少了代码的编写量。

UML图:

模板工厂代码:
  • ShoesClothe,分别为鞋子和衣服的抽象类(基类)
  • NiKeShoesUniqloClothe,分别为耐克鞋子和优衣库衣服具体产品类。
// 基类 鞋子
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 产品抽象类,如ShoesClothe
  • ConcreteFactory为具体模板工厂类,其中模板参数:AbstractProduct_t 产品抽象类(如ShoesClothe),ConcreteProduct_t 产品具体类(如NiKeShoesUniqloClothe
// 抽象模板工厂类
// 模板参数: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;
}
  • 输出结果:
[root@lincoding factory]# ./templateFactory
我是耐克球鞋,我的广告语:Just do it
我是优衣库衣服,我的广告语:I am Uniqlo

产品注册模板类+单例工厂模板类

前面的模板工厂虽然在新增产品的时候,不需要新增具体的工厂类,但是缺少一个可以统一随时随地获取指定的产品对象的类。

还有改进的空间,我们可以把产品注册的对象用std::map的方式保存,通过key-valve的方式可以轻松简单的获取对应的产品对象实例。

实现大致思路:

  • 把产品注册的功能封装成产品注册模板类。注册的产品对象保存在工厂模板类的std::map,便于产品对象的获取。

  • 把获取产品对象的功能封装成工厂模板类。为了能随时随地获取指定产品对象,则把工厂设计成单例模式。

UML图:

产品注册模板类+单例工厂模板类:
  • IProductRegistrar为产品注册抽象类,模板参数 ProductType_t 表示的类是产品抽象类(如ShoesClothe)。提供了产品对象创建的纯虚函数CreateProduct
  • ProductFactory为工厂模板类,模板参数 ProductType_t 表示的类是产品抽象类(如ShoesClothe)。用于保存注册产品对象到std::map中和获取对应的产品对象。
  • ProductRegistrar为产品注册模板类,模板参数 ProductType_t 表示的类是产品抽象类(如ShoesClothe),ProductImpl_t 表示的类是具体产品(如NikeShoesUniqloClothe)。用于注册产品到工厂类和创建产品实例对象。
// 基类,产品注册模板接口类
// 模板参数 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;
}
  • 输出结果:
[root@lincoding factory]# ./singleFactory
我是耐克球鞋,我的广告语:Just do it
我是优衣库衣服,我的广告语:I am Uniqlo

总结

将工厂方法模式改良成模板工厂,虽然可以解决产品新增时,不需要新增具体工厂类,但是缺少一个可以随时随地获取产品对象的方式,说明还有改进的空间。

将模板工厂改良成产品注册模板类+单例工厂模板类,产品注册模板类用于注册不同类型的产品,单例工厂模板类用于获取指定已注册的产品对象。这种方式,可以把工厂模式中产品的注册和获取的主要功能很好的抽象成两个类,并且使用单例模式使得工厂类可以随时随地获取已注册的产品对象。

所以产品注册模板类+单例工厂模板类的工厂模式,达到了开闭法则,并且扩展性高和封装度高。

PS:想学习更多单例模式,可以参考C++ 线程安全的单例模式总结文章阅读。


C++ 深入浅出工厂模式(进阶篇)的更多相关文章

  1. C++ 深入浅出工厂模式(初识篇)

    初识工厂模式 我们先看工厂模式的介绍 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创 ...

  2. java 深入浅出工厂模式

    一.引子 话说十年前,有一个暴发户,他家有三辆汽车——Benz奔驰.Bmw宝马.Audi奥迪,还雇了司机为他开车.不过,暴发户坐车时总是怪怪的:上Benz车后跟司机说“开奔驰车!”,坐上Bmw后他说“ ...

  3. 简单工厂模式-Java篇

    简单工厂模式就是考虑如何实例化对象的问题,就是说到底要实例化谁,将来会不会增加实例化对象,比如计算器类中增加开根元素,应该考虑用一个单独的类来创造实例的过程,这就是工厂.下面将利用计算器类举例,解释简 ...

  4. 工厂模式的进阶复习(Factory)

    工厂模式进阶复习 看了多遍的工厂模式,老是忘记不同模式有什么区别,本文重点说明一下工厂模式的三种方式(简单工厂模式,工厂方法模式,抽象工厂模式)的区别 1.简单工厂模式 简单工厂模式通过Factory ...

  5. Java设计模式(一) 简单工厂模式不简单

    摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...

  6. Java进阶篇设计模式之二 ----- 工厂模式

    前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...

  7. JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象

    本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...

  8. Java进阶篇设计模式之三 ----- 建造者模式和原型模式

    前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...

  9. 设计模式总结篇系列:抽象工厂模式(Abstract Factory)

    在上一篇的工厂方法模式中,通过一个公用的类对其他具有相同特性(实现相同接口或继承同一父类)的类的对象进行创建.随之带来的问题在于:当新定义了一个具有相同特性的类时,需要修改工厂类.这与设计模式中的开闭 ...

随机推荐

  1. Scrum Meeting - 第六周【Alpha阶段】

    每日任务内容: 本次会议为第六次Scrum Meeting会议 本次会议项目经理召开时间为20:00,在北区男生宿舍楼召开,召开时长约15分钟,探讨了本周选课网站编写的后续工作. 小组成员 本周任务 ...

  2. phper使用MySQL 针对千万级的大表要怎么优化?

    有需要学习交流的友人请加入交流群的咱们一起,群内都是1-7年的开发者,希望可以一起交流,探讨PHP,swoole这块的技术 或者有其他问题 也可以问,获取swoole或者php进阶相关资料私聊管理即可 ...

  3. Codeforces Round #608 (Div. 2) 题解

    目录 Codeforces Round #608 (Div. 2) 题解 前言 A. Suits 题意 做法 程序 B. Blocks 题意 做法 程序 C. Shawarma Tent 题意 做法 ...

  4. python 内置函数zip,map,三元,lambda表达式

    #内置函数zip(),将多个可迭代对象(集合等)按照顺序进行组合成tuple元祖,放在zip 对象进行存储,: #当参数为空时候,返回空 #如果 zip() 函数压缩的两个列表长度不相等,那么 zip ...

  5. 秒杀系统(一)----环境搭建及集成Mybatis、Redis

    1.1 环境搭建--pom文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=& ...

  6. .net core 获取树莓派的机器码,唯一ID,唯一串号

    今天在实际开发树莓派程序的时候,碰到了一个问题,需要获取到树莓派的唯一串号信息.必须在.net core的环境下: 那么如何实现呢?我们先查找树莓派的基本信息是储存在哪里的? 我们在下面的路径里找到了 ...

  7. Java生鲜电商平台-生鲜电商中商品类目、属性、品牌、单位架构设计与实战

    Java生鲜电商平台-生鲜电商中商品类目.属性.品牌.单位架构设计与实战 说明:Java生鲜电商平台-生鲜电商中商品类目.属性.品牌.单位架构设计与实战经验分享 凡是涉及到购物,必然是建立在商品的基础 ...

  8. ARTS改版啦,在改变中前行

    这次打卡,稍微进行了一次改版,在算法和英文文档上进行了拆分,具体的内容在前两天的文章里已经输出,所以在这篇上针对这两块做了一个汇总. 当然,技巧方面的还是在这里先输出,后续再考虑整改吧.循序渐进地上升 ...

  9. CSS学习笔记-过渡模块

    过渡模块:    1.过渡三要素        1.1必须要有属性发生变化        1.2必须告诉系统哪个属性需要执行过渡效果        1.3必须告诉系统过渡效果持续时长    2.格式: ...

  10. [转]RPA Developer Advanced Certification - Exam #1 UiPath 练习

    本文转自:https://github.com/miyaichi/CertificationExam1 RPA Developer Advanced Certification - Exam #1 E ...