浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第4章 创建型模式—工厂方法模式(2)

Posted on 2016-05-14 20:42  浅墨浓香  阅读(537)  评论(0编辑  收藏  举报

2. 工厂方法模式

2.1工厂方法模式的定义

(1)定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类

 

  ①Product:定义了工厂方法创建对象的接口。也就是实际需要使用的产品对象的接口

  ②ConcreteProduct:具体的Product接口的实现对象。

  ③Factory(Creator):定义了工厂方法的抽象类并返回一个产品对象。

  ④ConcreteCreator:具体的创建器对象,该类实现和覆盖了父工厂类声明的方法。返回一个具体的Product实例

(2)思考工厂方法模式

  ①工厂方法模式的本质延迟到子类来选择实现。简单工厂是直接在工厂类进行“选择实现”,而工厂方法会把这个工作延迟到子类来实现。由于工厂类依赖于抽象而不是具体的实现,从而系统更加灵活,具有更好的可维护性和可扩展性。

  ②工厂方法模式是简单工厂模式的进一步抽象,它保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心工厂类不再负责所有产品的创建,而是将具体的创建工作交给子类去做。这个核心类仅仅负责给具体工厂必须实现的接口,而不负责哪一个产品被实例化这种细节,这使得工厂模式可以允许系统在不修改工厂角色的情况下引用新产品。

  ③当系统扩展需要添加新产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象原有工厂对象不需要进行任何修改,也不需要修改客户端,很好地遵守了“开闭原则”,而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。

【编程实验】利用工厂方法模式实现的计算器程序

 

//创建型模式:工厂方法模式

#include <stdio.h>

//运算类(其它的加减乘法类从这里继承)
class COperator
{
protected:
    double mFirst;
    double mSecond;
public:
    void setFirst(double value){mFirst = value;}
    double getFirst(){return mFirst;}
    
    void setSecond(double value){mSecond = value;}
    double getSecond(){return mSecond;}
    
    virtual double getResult(){return 0;} 
};

//加法类
class CAdd : public COperator
{
public:
    double getResult()
    {
        return mFirst + mSecond;
    }    
};

//减法类
class CSub : public COperator
{
public: 
    double getResult()
    {
        return mFirst - mSecond;
    }    
};

//乘法类
class CMul : public COperator
{
public:
    double getResult()
    {
        return mFirst * mSecond;
    }    
};

//除法类
class CDiv : public COperator
{
public: 
    double getResult()
    {
         const double P = 0.000000000000001;
         if((-P < mSecond) && (mSecond< P)) //除数不能为0
         {
            return 0;
         }
            
        return mFirst / mSecond;
    }    
};

//工厂类(接口)
class CFactory
{
public:
    virtual COperator* createOperator() = 0;//由子类去实现
};

//加法工厂类
class CAddFactory : public CFactory
{
public:
    COperator* createOperator()
    {
        return new CAdd();
    }
};

//减法工厂类
class CSubFactory : public CFactory
{
public:
    COperator* createOperator()
    {
        return new CSub();
    }
};

//乘法工厂类
class CMulFactory : public CFactory
{
public:
    COperator* createOperator()
    {
        return new CMul();
    }
};

//除法工厂类
class CDivFactory : public CFactory
{
public:
    COperator* createOperator()
    {
        return new CDiv();
    }
};

int main()
{
    //客户端调用例子
    
    //客户端只需依赖COperator的接口类和工厂类,而无法知道具体的实现类
    //实现了客户端和具体实现类之间的解耦
    CFactory* ft= new CDivFactory(); //除法工厂
    COperator* oper =  ft->createOperator(); 
    oper->setFirst(1);
    oper->setSecond(2);
    
    printf("%f + %f = %f\n", oper->getFirst(),oper->getSecond(),oper->getResult());
    
    delete oper;
    delete ft;
    
    return 0;
}
View Code

2.2 工厂模式的优点

(1)良好的封装性,代码结构清晰。客户端不需要创建具体产品对象的艰辛过程,降低模块间的耦合。

(2)其次,工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。

(3)屏蔽产品类。产品类的实现,调用者不需要关心,它只需要关心产品接口,只要接口保持不变,系统中的上层模块就不要发生变化。

(4)工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类不用关心,符合迪米特法则,也符合依赖倒置原则。

【编程实验】利用工厂方法实现导出各种类型文件

//创建型模式:工厂模式
//利用工厂模式实现导出各种类型的文件

#include <stdio.h>

//导出文件接口类(抽象产品类)
class CExportFileApi
{
public:
    virtual bool Export(const char* data) = 0;   
};

//导出文本文件(实现导出接口类)
class CExportTxtFile : public CExportFileApi
{
public:
    bool Export(const char* data)
    {
        printf("Export data \"%s\" to txt file!\n",data);
    }    
};

//导出文件到数据库文件(实现导出接口的类)
class CExportDBFile: public CExportFileApi
{
public:
    bool Export(const char* data)
    {
        printf("Export data \"%s\" to Database!\n",data);
    }    
};

//生成导出文件对象的工厂类(抽象类)
class CExportOperate
{
protected:
    //由工厂的子类去生成具体的产品类实例
    virtual CExportFileApi* factoryMethod()=0; //protected属性
    
public:

    //这个方法将客户端与具体产品类解耦
    bool Export(const char* data) 
    {
        CExportFileApi* api = factoryMethod();
        
        api->Export(data);
        
        delete api;  
    }    
};

//生成导出文件对象的具体工厂(实现类)
class CExportTxtFileOperate : public CExportOperate
{
protected:
    CExportFileApi* factoryMethod()
    {
        return new CExportTxtFile;
    }
};

//生成导出文件对象的具体工厂(实现类)
class CExportDBOperate : public CExportOperate
{
protected:
    CExportFileApi* factoryMethod()
    {
        return new CExportDBFile;
    }  
};

int main()
{    
    //客户端调用例子(注意只依赖工厂类接口与具体工厂类,而
    //与产品类及具体的产品的实现没有依赖关系,起到很好的解耦
    
    //创建需要使用的Creator对象
    CExportOperate* oper = new CExportDBOperate();
    
    //调用输出数据的功能方法
    oper->Export("abcd");
    
    delete oper;
        
    return 0;
}

2.3 工厂方法模式的使用场景

(1)工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但需要慎重考虑是否要增加一个工厂类进行管理,增加代码的复杂度

(2)需要灵活、可扩展的框架时,可以考虑采用工厂方法模式。

(3)当子类可能会很多,以后需要不断增添不同的子类实现时。

(4)如果一个类需要创建某个接口的对象但是又不知道具体的实现时。可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现。

【编程实验】利用工厂方法模式实现多数据库的连接

//创建型模式:工厂方法模式
//利用工厂方法模式切换数据库
#include <stdio.h>

//数据库连接对象
class CConnection
{
    //_ConnectionPtr m_pConn;
public:
    CConnection(const char* connectionString){}
    //bool Open(CString ConnectionString,CString UserID,CString Password, ConnectOptionEnum ConnectOption)
    //bool Close();      
};

//数据集对象
class CRecordSet
{   
};

//抽象产品类
class CAbstractDB
{
protected:
   CConnection* m_pConn;
public:
    CAbstractDB(const char* connectionString)
    {
        printf("%s\n",connectionString);
        m_pConn = new CConnection(connectionString);
    }
    
    ~CAbstractDB()
    {
        //关闭数据库
        //close();
        
        delete m_pConn;
    }
    
    //打开数据库连接
    void open()
    {
        if(m_pConn != NULL)
        {
            //m_pConn->Open();
            printf("打开数据库连接!\n");
        }     
    }
    
    //关闭数据库连接
    void close()
    {
        if(m_pConn != NULL)
        {
            //m_pConn->Close();
            printf("关闭数据库连接!\n");
        }     
    }
    
    //执行SQL语句,返回结果集
    virtual CRecordSet*  Execute(const char* commandText) = 0;
};

//MSSql数据库
class CMSSqlDB : public CAbstractDB
{
public:
    CMSSqlDB(const char* connectionStr):CAbstractDB(connectionStr){}
    
    CRecordSet*  Execute(const char* commandText)
    {
        printf("MSSql: %s\n", commandText);
        return NULL; //示例,实际中要返回结果集,new CRecordSet();
    }    
};

//Oracle数据库
class COracleDB : public CAbstractDB
{
public:
    COracleDB(const char* connectionStr):CAbstractDB(connectionStr){}
    
    CRecordSet*  Execute(const char* commandText)
    {
        printf("Oracle: %s\n", commandText);
        return NULL;//new CRecordSet();
    }    
};

//Access数据库
class CAccessDB : public CAbstractDB
{
public:
    CAccessDB(const char* connectionStr):CAbstractDB(connectionStr){}
    
    CRecordSet*  Execute(const char* commandText)
    {
        printf("Access: %s\n", commandText);
        return NULL;//new CRecordSet();
    }    
};

//工厂类
class CDBFactory
{
public:
   virtual CAbstractDB* createDB() = 0;
};

//MSSql工厂类
class CMSsqlDBFactory : public CDBFactory
{
public:
   CAbstractDB* createDB()
   {
       return new CMSSqlDB("MSSQL ConnectionString!\n");
   }
};

//Oracle工厂类
class COracleDBFactory : public CDBFactory
{
public:
   CAbstractDB* createDB()
   {
       return new COracleDB("Oracle ConnectionString!\n");
   }
};

//Access工厂类
class CAccessDBFactory : public CDBFactory
{
public:
   CAbstractDB* createDB()
   {
       return new CAccessDB("Access ConnectionString!");
   }
};

int main()
{
    //客户端调用例子
    
    //利用工厂方法模式切换数据库
    CDBFactory* dbf = new CAccessDBFactory(); //在这里更改数据库!
    CAbstractDB* db = dbf->createDB();
    
    db->Execute("Select * from demo");
    
    delete db;
    delete dbf;
    
    return 0;
}

2.4 工厂方法模式的扩展

(1) 缩小为简单工厂模式

    当一个模块仅需要一个工厂类时,就只需要使用具体工厂类并使用静态方法就可以了,去掉抽象类,于是就演变成一个简单工厂类

(2)升级为多个工厂类

    当一个产品类有多个具体的实现,并且每个实现类的初始化方法(包括new和对对象设置初始值)的方法都不同时,可以为每个具体实现定义相应的创建者,形成多个工厂。