简介
简单工厂模式是设计模式中之一的典例,是一种可以达到完全解耦性的编程思想
工厂模式典例
比如设计一个播放器类,这个类可以解析A视频网站的资源,也可以进行解析B网站的资源,如果想达到类的设计程度,必须按照多态的思想进行编程,多态的其意思可以大概理解成相同的类具有不同的功能。
设计方式可以给定一个带有纯虚函数的类,比如这个虚函数有播放,暂停,切换上一个视频,切换下一个视频、这几个常用的功能是这个类方法的组合基本上是不变的,如播放-->暂停-->切换下一个视频这组合基本上不变,变的是这个类的方法。
这个类方法如播放,具体播放那条视频?有链接吗?是怎么解析的?这些都是不确定因素,称为经常变的变量,一个类分一半大致就分为基本不变的量与经常变的量
但是在设计模式中,需要对经常变化的量进行隔离,只有隔离的经常变化的量,剩下的只有基本不变的量,程序才会变得更加稳定,就例如查账单下订单的需要进行大量的数据库操作,比如sql的insert、update、detele、select属于基本不变的量,而且整个过程中需要大量的重复使用这些数据操作代码,但是不会那么轻易改变,这就是基本不变的量
经常变量的量如查订单(select),下单(insert)的业务逻辑可能对应的类方法不同,但是可以与数据库操作之间设计一个中间层,这个中间层对经常不变的量与经常变化的量进行隔离,如果需要查订单则把参数提交给中间层让中间层中转到基本不变的量层!
但是有一天我突然不想用sql了、想换成access即我的中间层发送变化,业务逻辑完全不会发生这变化,这个中间层就是接口
应用案例
创建一个API程序接口类,这个API类提供最基本的基础服务,这个服务的逻辑可以不用改变可以用多态的方式对他进行封装!
接口在语法中表示是一个特殊抽象类,在类中可以封装基本抽象类,实现类,工厂类,助理类,自动化工厂类,类工厂类。大概分为6个组合,因为C++中不支持反射,所以可以用宏进行封装通用的工厂类
基本的API提供基本服务,里面有一个虚函数test,这个虚函数没有进行实现需要特定的实现类进行实现、传入一个文本参数类型为string,但是可以封装多个虚函数进行的逻辑实现!例如刚刚所剩的播放,暂停,下一条视频,就可以定义成虚函数!
#pragma once
#include <iostream>
using namespace std;
class Api
{
public:
virtual int test(string s) = 0;
protected:
Api();//屏蔽构造函数
};
再次定义一个实现抽象类的类,此时实现了虚函数test的方法
#pragma once
#include "Api.h"
#include "DynOBJ.h"
class ImplTwo:public Api
{
public:
int test(string s);
};
#include "ImplTwo.h"
int ImplTwo::test(string s)
{
cout << "ImplTwo正在执行" << s << endl;
return 0;
}
此时可以直接声明API* pApi = new ImplTwo进行创建这个实现类,此时需要隔离经常变化的量,如果一个程序中到处都是new进行创建这个实现类,显得这个程序非常石板,因为这个是抽象类,是经常改变的类,所以改变new这种模式增强灵活性
Api* pApi = new ImplTwo;
解决问题 工厂类的建立
此时可能会有很多个实现类,那么必须建立一个工厂类进行管理这些实现类,其中ImplOne与ImplTwo为两个不同的实现类
创建一个工厂类,再创建一个静态方法createApi用于制造实现类,传入指定的参数从而创建不同的实现类,传入是类型是1,则创建ImplOne实现类,传入的类型是2,则创建ImplTwo实现类,从而化简这个new的操作
#pragma once
#include "ImplOne.h" //实现类1
#include "ImplTwo.h" //实现类2
class Factory
{
public:
static Api* createApi(int type);
};
#include "Factory.h"
Api* Factory::createApi(int type)
{
Api* pApi = nullptr;
if (type == 1)
{
pApi = new ImplOne();
}
else if (type == 2)
{
pApi = new ImplTwo();
}
return pApi;
}
使用工厂类进行创建实现类,这种方式了就大大降低耦合度(编程的思想高内聚,低耦合),但是这种方式还是存在一种问题,那就是需要输入1进行确定一个类,输入2再次确定一个类,这种方式还是显得太死板,所以对这种方式进行优化
Api* pOne = Factory::createApi(1);
Api* pTwo = Factory::createApi(2);
pOne->test("哈哈哈");
pTwo->test("嘻嘻嘻");
优化工厂-自动化工程类的建立
自动化工程是为了解决通用性的问题,也进一步增强了灵活性,完美的解耦!
自动化工厂类包括类构造工厂,助理类,助理类用于注册这个类到类构造工厂
建立类构造工厂类,其中typedef void* (*Constructor)();为仿函数,利用这个进行传递这个类指针
registerClass:注册类
createObject:创建类
#include <string>
#include <map>
//仿函数
typedef void* (*Constructor)();
class CObjectFactory
{
public:
//可能有多个类,此时用map进行管理,用名字进行对应
static void registerClass(std::string className, Constructor constructor);
static void* createObject(const std::string& className);
private:
//string-->key:动态创建的类的类名,value是构建
inline static std::map<std::string, Constructor>& constructors();
};
类构造工厂类的具体实现,用map进行管理,用类名与类指针进行绑定,这个类中有两个方法,第一个是静态注册类方法(registerClass),调用constructors()方法返回一个静态的map引用,再把类指针加入到这个map中(键值对,类名称与类指针进行绑定),绑定完成之后就可以调用createObject进行动态创建,首先是调用find()寻找传递进来的类名称,如果find()寻找到,则肯定不等于end()、所以进行取出键值(second),这个second就是类指针了
#include "DynOBJ.h"
void CObjectFactory::registerClass(std::string className, Constructor constructor)
{
constructors()[className] = constructor;
}
void* CObjectFactory::createObject(const std::string& className)
{
Constructor constructor = nullptr;
//找到对应的类名
if (constructors().find(className) != constructors().end())
{
//把key传给constructor变量,也就是registerClass的constructor
constructor = constructors().find(className)->second;
}
//判断这个类指针是否被赋值,如果等于NULL则返回NULL
if (constructor == nullptr)
return nullptr;
//return (*constructor)();//或 constructor()
return constructor;
}
inline std::map<std::string, Constructor>& CObjectFactory::constructors()
{
static std::map<std::string, Constructor> instance;
return instance;
}
创建通用助理类宏
##宏字符串拼接,如形参class_name、实参传递进来的为OOP,最终拼接出来的是OOPHelper
#字符串宏,把形参class_name转换成字符串,如实参传递进来的是OOP,结果为"OOP"
\宏连接符号,如果宏中换行则必须用到\进行上下文连接
其中,class_name##Helper为助理类,class_name##Helper()为构造函数,构造函数中把传递进来的类进行注册,注册类其实就是把类插入到map中,进行绑定
静态的createObjFunc是动态创建类的方法,在构造函数中第二个参数就是调用这个方法进行动态创建这个类
class_name##Helper class_name##helper;对这个类进行实例化,这一步不可少,只有实例化之后这个助理类的构造函数才会被调用
#define REG_CLASS(class_name) \
class class_name##Helper\
{ \
public: \
class_name##Helper() \
{ \
CObjectFactory::registerClass(#class_name, (Constructor)class_name##Helper::createObjFunc()); \
} \
static void* createObjFunc() \
{ \
return new class_name; \
} \
}; \
class_name##Helper class_name##helper;
#endif
ImplTwo* pTwo = static_cast<ImplTwo*>(CObjectFactory::createObject("ImplTwo"));
ImplOne* pOne = static_cast<ImplOne*>(CObjectFactory::createObject("ImplOne"));
创建自动化工厂类与助理类
这里是最后一个步骤,通过REG_CLASS对这个类进行注册,因为传递的是字符串,宏展开的时候就自动调用上面助理类的构造函数了,REG_CLASS的形参确定具体哪一个实现类,通过构造函数进行构造这一个实现类,这就是类的动态创建机制、此时就实现了完全解耦的状态。
#ifndef __AUTOFACTORY_H
#define __AUTOFACTORY_H
#include "Factory.h"
class AutoFactory {
public:
static Api* createApi();
};
#endif
此时createApi方法即可把这类加入到类的构造工厂中,后就可以调用类CObjectFactory::createObject拿到这个类的对象
REG_CLASS(ImplTwo)
REG_CLASS(ImplOne)
Api* AutoFactory::createApi()
{
Api* pApi = nullptr;
//实际应用:这样做的好处就是、如果ImplOne字符串在配置文件中写进来,可以实现配置开发,使用非常灵活,扩展自如
pApi = static_cast<Api*>(CObjectFactory::createObject("ImplTwo"));
return pApi;
}
声明自动化工厂
就可以完成ImplTwo实现类的动态创建,并用API类完成基本的设施(也就是业务逻辑)
auto pTwo = AutoFactory::createApi();
pTwo->test("RegisterClass");
应用与总结
1 需要对经常变化的量进行隔离,来达到高度解耦的效果
2 隔离经常变化的量指的是对小部分的量进行改变,即可改变大部分的经常改变的量
3 如果把一个类的名称写入到配置文件,就可以读取配置文件中的内容确定类的名称从而确定使用哪一个实现类,大大增强了程序的灵活性与扩展性(应用方法)