C++设计模式(二)- 工厂模式(factory method and Abstract method)

工厂模式其实可以细分为工厂方法模式和抽象工厂模式, 因为很接近,所以在这里合起来介绍。
工厂模式属于“对象创建模式”的一种,通过“对象创建”模式,绕开new, 来避免对象创建模式导致的紧耦合(依赖具体类),从而支持对象创建的稳定。由于需求的变化,需要创建的对象的具体类型经常变化,工厂模式可以提供一种封装机制,来避免客户程序和这种 “具体对象创建工作”的紧耦合。

模式定义

工厂方法模式(Factory Method)

定义一个用于创建对象的接口,让子类决定实例化哪一个类。factory method使得一个类的实例化延迟到子类。(使用虚函数来解耦)
-《设计模式》GOF

抽象工厂模式(Abstract Method)

提供一个借口,让该接口负责创建“一系列相关或者相互依赖的对象”,无需指定他们的具体类。
-《设计模式》 GOF

总结

  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  • Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
  • Abstract Factory 应对的是 “多系列对象创建”的需求变化。( “系列对象”指的是,在某一特定系列下的对象之间有相互依赖或者作用关系,不同系列的对象之间不能相互依赖。)
  • Factory Method模式应对的是单个对象的创建。

缺点

  • Factory Method模式解决“单个对象”的需求变化,缺点在于要求创建方法,参数相同。
  • Abstract Factory模式主要在于应对“新系列”的需求变动,其缺点在于难以应对“新对象”的需求变动。

Factory Method 模式举例

写一个文件查看器,支持查看文本文件,二进制文件。

首先定义接口类:

//文件查看器抽象类
class IReader
{
public:
    virtual void read()=0; //只保留核心方法, 构造析构这里没写(注:接口中一定要有虚析构方法)。
};

//工厂基类
class ReaderFactory
{
public:
    virtual IReader* CreateReader()=0;//工厂生产文件查看器。
};

定义文件查看器具体类:

// 文本文件查看器
class TextReaderpublic IReader
{
	void read(){...};
}
// 二进制文件查看器
class BinaryReaderpublic IReader
{
	void read(){...};
}

定义工厂具体类:

//文本文件查看器工厂
class TextReaderFactory: public ReaderFactory
{
public:
	IReader * createReader()
	{
		return new TextReader();		
	}
}
//二进制文件查看器工厂
class BinaryReaderFactory: public ReaderFactory
{
public:
	IReader * createReader()
	{
		return new BinaryReader();
	}
}

客户程序

class MainForm : public Form
{
    ReaderFactory*  factory;//定义一个工厂。
public:    
    MainForm(ReaderFactory*  factory){
        this->factory=factory; //传入具体的工厂
    }
    void Button1_Click()
    {     
		IReader * reader= factory->CreateReader(); //多态new,根据传进来的factory类型,调用相应的工厂方法。这样,当外部传进来不同的工厂时,
		可以不用修改客户程序,就能找到具体的工厂类型。即使是将来扩展支持媒体文件,只需要添加媒文件对应的媒体文件查看器类,媒体文件查看器工厂类即可,
		做到遵循开闭原则-对扩展开放对修改关闭。      
        reader->read();
    }
};

总结:

通过在客户端程序实现 多态new,根据传进来的factory类型,调用相应的工厂方法。这样,当外部传进来不同的工厂时,可以不用修改客户程序,就能找到具体的工厂类型。即使是将来扩展支持媒体文件,只需要添加媒文件对应的媒体文件查看器类,媒体文件查看器工厂类即可,做到遵循开闭原则-对扩展开放对修改关闭。

项目中实际用到的Factory Method模式实例抽象:
开机过程中会有一些基本参数初始化,数据库连接,外围设备检查的工作,界面显示等20多种状态。如果一个一个写会很乱,维护成本很高。具体实现是:定义一个N个具体的状态类和对应的状态类工厂类,工厂类中只给一个对外接口,在客户程序中遍历执行工厂类生成N个状态类对象。这其实是多个模式的组合,不光有工厂模式还用到了状态模式。

定义接口类(仅用于具体类,这里工厂没有抽象出基类)

//定义一个模板状态接口
template <class T>
class State
{
public:
    virtual void Enter()=0;
    virtual void Exec()=0;
    virtual void Exit()=0;
	virtual QString getStateName() = 0;
    virtual ~State(){}
protected:
    T*  _p;
};
class IPowerUpManager
{
	public:
        // called from one of the state objects to start the state timer
        virtual bool startStateTimer(int timerInterval) = 0;
};

定义具体状态类

class POWERUP_INIT: public State<IPowerUpManager>
{
	POWERUP_INIT()};
class ORACLE_DB_CNCT : public State<IPowerUpManager>
{
	ORACLE_DB_CNCT();
};

定义具体工厂类:
这里没有使用虚函数机制进行多态new。而是通过遍历map表的方式实现接口稳定(定义一个map, 以状态名,工厂方法为键值对,存入所有的状态和工厂方法,外部使用的时候仅仅传入状态名即可),这种方式可以解决创建方法,参数不同的问题,但是项目中工厂方法参数是一样的。猜测设计时是为了放进map中方便维护。

class powerupStateFactory//这里没有使用虚函数机制进行多态new
{
public:
        State<IPowerUpManager> * createStateObject(stateName)//对外只提供该接口,传入stateName,查找并执行对应的工厂方法。
        {
            funcIt = _functionMap.find(stateName);
		    if (funcIt != _functionMap.end() )
		    {   
		        pReturnObj = *(funcIt.value()();//执行私有工厂方法。(稳定)          
		    }
		    return(pReturnObj);
        }
private:
	    State<IPowerUpManager> * powerupStateFactory::createInitObject()//创建POWERUP_INIT对象
	    {
	    	new POWERUP_INIT();
	    }
		State<IPowerUpManager> * powerupStateFactory::createOracleDBCnctObject()//创建ORACLE_DB_CNCT对象
		{
	    	new ORACLE_DB_CNCT();
	    }
        QMap<QString, OBJ_CREATE_FUNC_P >      _functionMap;//构造函数会把所有创建对象的工厂私有方法都放到这个map中,对外提供一个createStateObject方法,传进key值,取出工厂方法并执行。
};

客户程序
客户程序遍历状态列表,调用工厂接口一次性创建出N个状态对象。

class powerupStateMachine : public QObject, public IPowerUpManager 
{
public:
	bool createStateMachine()
	{
		 pStateFactory = new powerupStateFactory();//创建一个工厂。
		 for( it = _stateInfo.begin() ;it != _stateInfo.end(); ++it)//_stateInfo存的是20多种状态,遍历每个状态,把每个状态对应的类对象都new出来,并存到一个map中。
	    {
            State<IPowerUpManager> *stateObject = pStateFactory->createStateObject();//调用工厂方法创建类对象。
            powerUpStates.insert(stateData.stateName, stateObject);//把创建出来的类对象放到map中
        }
        _stateMachine = new StateMachine<IPowerUpManager>(powerUpStates)//把所有的状态对象封装到一个状态机中。通过状态机执行每个状态的对应的动作。
        

这样,如果要扩展新的状态类,如MyClass。1. 需要添加MyClass类文件(扩展,满足开闭原则),2. 在powerupStateFactory类中添加私有的 State * powerupStateFactory::createMyClassObject()方法,并修改其构造函数把MyClassObject工厂方法放进_functionMap(修改,这里其实违背了开闭原则,但并不会带来像if-else if语句一样的风险。3.后面的客户程序不用做任何修改(powerupStateMachine中的_stateInfo是通过配置文件加载进来的,只需要更新配置文件即可)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值