灵光一闪:

        抽象工厂模式经常被使用,那么同时也会多次写相同或类似的代码。能不能设计一个通用的工厂类,以避免重复设计工厂类呢?
        先回忆一下基础的工厂类设计。
    class Button{};class LinuxButton : public Button{};class WindowsButton: public Button{};class AbstractFactory{public:Button* create Button() = 0;};class LinuxConcreteFactory : public AbstractFactory{public:Button* createButton(){return new LinuxButton ;}};class WindowsConcreteFactory : public AbstractFactory{pulbic:Base *createBase(){return new WindowsButton;}};
        以上是一个基本的抽象工厂类。这套代码会根据不同的操作实现不同的控件,比如运行在linux下,那么会要产生一个Linux格式的Button,运行在Windows下,那么会产生Windows的Button。很多用过工厂类的猿们应该都有写过这样的工厂。但是这样的工厂有很大的问题:
  1. 有新产品类时,要修改AbstractFactory的接口,同时要改变所有具体工厂去实现新的创建对象函数。比如,现在要加一个Label的控件,那么会有Label、LinuxLabel和WindowsLabel类。这时就要去修改AbstractFactory的接口,然后所有实现类都要去实现新的函数。
  2. 要记住每个类创建对象的对应函数,当然,名字取得得当,这不是问题,就像上面的例子一样,对于类Button,创建一个createButton的函数。
  3. 当要使用这个工厂创建对象的类非常多时,这时工厂的接口就非常大。
  4. 当具体工厂的个数比较多时,这样实现具体工厂就更加麻烦。比如,这时要加上Mac系统的控件,此时就得去新建一个MacConcreteFactory的类,并去继承AbstractFactory类,然后实现所以函数。
        那么有没有比较简便点的方法呢?
=============================================================================
        下面给出一个《C++ API设计》这本书的一个工厂类的设计。
        Button* createLinuxButton(){return new LinuxButton;}Button *createWindowsButton(){return new WindowsButton;}class ButtonFactoryEx{public:void registerClass(const string &name, Button* (*callback)()){cm[name] = callback;}Button* create(const string &name){CreateMap::iterator it = cm.find(name);if (it != cm.end()){return (*(it->second))();}else{return NULL;}}// ...省略一些其他不用介绍的函数,可以参考《C++ API设计》private:typedef Button* (*CreateCallback)();typedef map<string, CreateCallback> CreateMaps;CreateMaps cm;};

        使用这个拓展的工厂,就不用再使用抽象工厂,然后对其子类化,再实现各个函数了。不过引用了每一个类的生产函数,这些函数可以放入到类中做static函数。
        现在要使用Linux类的Button,使用Windows类的Button,只要将生产的回调函数注册进去(以下称为注册类到工厂)。如:
        ButtonFactoryEx factory;factory.registerClass("linux", createLinuxButton);factory.registerClass("windows", createWindowsButton);factory.create("linux");  // 产生Linux的buttonfactory.create("windows"); // 产生windows的button

        这样设计符合了开闭原则。现在如果要加入Mac格式的Button,只要写好MacButton后,创建一个createMacButton的函数,然后将它注册到工厂里。已经不用更改工厂类的接口了。
        但是,这样设计又带来了一些新的问题:
  1. 对于不同的产品类,要使用不同的工厂,使工厂类个数膨胀。比如,现在要加一个Label的类,那么就要弄一个LabelFactoryEx的工厂。
  2. 如果传了没有注册进行的字符串去创建对象,或者拼错字符串了,比如"linux"拼成了"linus",那么将会返回NULL。这要运行时才能知道错误,而不是编译期。
  3. 在运行期要先注册类到工厂。
  4. 要管理多个不同的工厂对象。这是一个比较严重的问题。如果没管理好,在其他的模块中将访问不了这个对象,那么这个工厂就没有用了。
        这样设计其实是将产品的对象创建函数移到了工厂之外,然后将该函数注册到工厂里面。
        对于以上问题,可以对所有的产品类创建一个基类Object,然后将工厂的回调函数设计成返回Object*,然后用户再使用dynamic_cast。这样处理可以解决上面的第1和第4个问题,因为要管理的工厂对象减少到一个了,这样也方便处理。而要实现基础工厂那个,根据不同的环境产生不同的工厂,只要产生不同的工厂对象就行了。但是,这样做却会引入这样的问题:
  1. 所有的产品类要继承Object。如果这个产品类已经写好了,或者这个产品类不是你写的,你只有这个类的库,没有源代码,不能改接口,那怎么办?即使可以修改这个类的设计,但是所有的产品都继承Object将会很繁琐。修改已经存在的类还可能会引起已存在的代码产生新的问题。
  2. 是使用dynamic_cast会使系统的运行效率严重降低。
下页开始设计这篇文章要讲解的工厂。

============================================================================================
        还有一种替换的方案是使用模板类或者模板函数。
        将工厂类设计成模板的,这样就可以解决实现多个工厂的问题。但是,还有第4个问题还解决不了,只是工厂类的实现由程序员交给了编译器去处理了。读者可以自己去实现,以区别。
        
        如果将工厂类不设计成模块的,而将里面的方法设计成模板的,这样一个工厂对象就可以接收不同的产品的创建回调函数。只要在使用的时候将产品的类型传到模板函数里就行了。
        下面是这个工厂的初步设计。
        class Factory{public:template <typename ReturnType>bool registerClass(const string &name, ReturnType* (*createCallback)());template <typename ReturnType>ReturnType* produce(const string &name);// ...};

        这里使用registerClass去将一个产品的创建回调函数注册进去,使用produce去生产对象。
        这时将引起另外的一个问题,那就是对于不同产品的回调函数,怎么去储存?因为类的数据成员不能因为模板函数的参数不同而加入不同的数据成员。也就是说不能使用数据成员存在回调函数了。
        那能不能用成员函数去处理呢?答案是:可以的。下面是我的设计:
class Factory{public:// 接上面的类设计private:enum HandleType {GET, ADD};template <typename ReturnType>bool callback_container(const string, ReturnType* (**createCallback), HandleType type){typedef ReturnType* (*Callback)();typedef map<string, Callback> CallbackMap;static CallbackMap cm;}};

        使用callback_container的模板函数来处理不同类型产品的存在问题。当有不同的产品注册进来时,编译器将会在编译期产生不同的重载函数。每个函数里面又有一个static的成员记录回调函数。因为static的成员变量只会被初始化一次,且在内存中一直存在。所以这样的设计是可以行得通的。因为要对同一个产品类型都用同一个callback_container来处理,那么就要加入操作标志的耦合,以决定是要处理取回调函数操作还是增加新的回调函数操作。
        现在要处理注册和回调,只要这样操作。
       Factory linuxFactory;linuxFactory.registerClass<Button>("button", createLinuxButton);linuxFactory.registerClass<Label>("label", createLinuxLabel);   // 假设设计了这个类和回调函数Factory windowsFactory;windowsFactory.registerClass<Button>("button", createWindowsButton);windowsFactory.registerClass<Label>("label", createWindowsLabel);   // 假设设计了这个类和回调函数// 产生Linux的buttonlinuxFactory.produce<Button>("button");// 产生Linux的LabellinuxFactory.produce<Label>("label");

        这样就解决了使用Object基类设计的问题。
        现在还有问题有:
  1. 运行期要先注册回调函数到工厂。
  2. 不存在的名字或拼错的名字去创建。
        这两个问题到目前为止,C++是解决不了的。

        还有一个问题,就是像上面的linuxFactory和windowsFactory能不能再进行管理。因为一个程序中使用的工厂应该是有限的,而且是很少的。特别是设计成这样通用的工厂,那应该只有一个对象了。当然,这是可以解决的,通过单例模式封装工厂。
static Factory& Factory::instance(const string &name);

        使用不同的名字处理工厂对象,不同的工厂对象对应一个名字,使用容器放起来。下次再通过名字可以找出该工厂。这样也方便在其他模块中使用工厂,而不用使用全局变量。当然,这样处理后,那么对于callback_container的处理就更复杂了。因为不同的工厂对象里面,它们不能使用相同的callback_container里面的cm。所以就要再加一层窗口去存在不同对象的cm了。完整代码参照我的github。这样处理后,可以将callback_container设成static的。
        使用单例对象,上面的使用就可以这样使用了。
         #define LINUX_FACTORY Factory::instance("linux")#define WIN_FACTORY Factory::instance("windows")LINUX_FACTORY.registerClass<Button>("button", createLinuxButton);LINUX_FACTORY.registerClass<Label>("label", createLinuxButton);WIN_FACTORY.registerClass<Button>("button", createWindowsButton);WIN_FACTORY.registerClass<Label>("label", createWindowsLabel);LINUX_FACTORY.produce<Button>("button");LINUX_FACTORY.produce<Label>("label");

        对于上面所设计的代码,剩下的问题是:
  1. 运行期要先注册回调函数到工厂。
  2. 不存在的名字或拼错的名字去创建。
        当然,目前是还解决不了的。

        在代码设计方面,对于callback_container的函数太过于复杂。与一个函数只处理一个逻辑相违。但是,目前我只有这样的实现方法,如果读者有更好的解决方案,欢迎分享。
转载请注明出处。http://blog.csdn.net/tenghui0425/article/details/23838535

通用工厂类Factory(C++实现)相关推荐

  1. JavaWeb学习篇——使用通用工厂类解耦MVC框架

    通过使用经典的MVC(model.view.controller)三层开发框架,可以使我们的项目结构更符合模块化的特点,各层(web.service.dao)只负责本层最擅长的工作.当某一层的业务逻辑 ...

  2. 设计模式 - 学习笔记 - 工厂模式Factory Pattern

    设计模式 - 学习笔记 - 工厂模式Factory Pattern 1. 简单工厂 1.1 应用场景 1.2 UML 1.3 优劣分析 好处 缺点 1.4 代码示例 抽象产品 AbstractProd ...

  3. 类对象工厂设计模式(Factory Pattern)

    本文朋友在深圳逛街的时候突然想到的...这段时间就有想写几篇关于类对象的笔记,所以回家到之后就奋笔疾书的写出来发布了 提供了比工厂模式更高一级的接口级,用于返回若个工厂之一.这个模式是属于创立模式之一 ...

  4. 【java设计模式】之 工厂(Factory)模式

    1.工厂模式的定义 工厂模式使用的频率很高.我们在开发中总能见到它们的身影.其定义为:Define an interface for creating an object, but let subcl ...

  5. 设计模式之工厂类模式总结对比、简单工厂模式、工厂方法模式、抽象工厂模式、带反射的工厂模式、例子代码分析、最详细

    1. 题目 假设某公司同时用SqlServer.MySql数据库,即会切换两数据库(不同数据库的sql语句有些许差异),同时,两数据库里均有对Users.Departments表的操作(sql代码不一 ...

  6. Spring 通过工厂方法(Factory Method)来配置bean

    在spring的世界中, 我们通常会利用bean config file 或者 annotation注解方式来配置bean. 在第一种利用bean config file(spring xml)方式中 ...

  7. java的工厂类_深入理解Java的三种工厂模式

    一.简单工厂模式 简单工厂的定义:提供一个创建对象实例的功能,而无须关心其具体实现.被创建实例的类型可以是接口.抽象类,也可以是具体的类 实现汽车接口 1 public interfaceCar {2 ...

  8. 设计模式(一)工厂模式Factory(创建型)

    设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.但是在一些情况下, new操作符直接生成对象会带来一些问题. ...

  9. Spring 创建对象的问题,不同构造方法创建对象,使用工厂类类获取对象、单例或多例、延迟创建问题

    创建实体类Student package star.july.b_ioc; public class Student {private String name;private int age;publ ...

最新文章

  1. pycharm 链接wsl和 wsl 配置cuda nvidia
  2. 如何隐藏iPhone导航栏上的“后退”按钮?
  3. 弹出窗口显示输出内容_前端加油站(3)-JavaScript 输出
  4. mysql安装check requirements出错_超详细的MySQL8.0.17版本安装教程
  5. position absolute定位之所属的containing box
  6. iPhone清理喇叭灰尘_厉害了!原来可以这样一键清理 iPhone 喇叭灰尘!
  7. Lunix git stash clear 或者 git stash drop后恢复的方法
  8. GAN生成的人脸数据集
  9. c语言炒股软件公式,股票软件怎么使用指标选股公式
  10. 用计算机怎么弹星辰大海,【计算机学院】你们的征途将是星辰大海~
  11. python实现报表的分组统计_Python 分组处理
  12. 几款制作网页线框图的软件介绍
  13. ICPC North Central NA Contest 2017 H.Zebras and Ocelots
  14. 编写批量修改扩展名脚本
  15. FileZilla ftp传输文件报错: 响应:550 Create directory operation failed.
  16. Mac电脑最强截图工具--iShot
  17. MySQL高级篇——日志
  18. 索骥馆-网络创业之《网上赚钱从入门到精通》扫描版[PDF]
  19. Google 百度 图标收藏(三)
  20. 计算机程序员的英文简历,电脑程序员英文简历范文

热门文章

  1. 芯片低功耗相关的术语简介
  2. 关于以太网物理层测试
  3. 店盈通:拼多多运营必须要知道的订单修改规则
  4. 中国互联网十五年的22个创新模式
  5. ISCC国际可持续性和碳认证介绍
  6. 全球与中国单晶炉市场现状及未来发展趋势
  7. 2021-07-23 Week1 基于截图的钓鱼网站检测、中小微企业安全现状调查
  8. tesseract-ocr识别中文扫描图片实例讲解
  9. 什么样的人适合创业开公司(转贴)
  10. nginx 加载图片 nginx图片服务器