第5章 创建型模式—抽象工厂模式

1. 抽象工厂的定义

(1)提供一个创建一系列相关相互依赖对象的接口,而无需指定它们具体的类

  ①只需要知道创建一系列对象的接口,而无需知道具体使用的是哪一个实现

  ②这一系列对象是相关或相互依赖的,也就是说既要创建对象,还要约束它们之间的关系。

  ③一系列对象是构建新对象所需要的组成部分,并且对象之间相互有约束。如电脑由CPU和主板等组成,但CPU的针脚数和主板提供的插口必须是匹配的,否则无法组装。

(2)产品族和产品等级

  ①产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构,功能相关联的产品组成的家族。如,AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。

  ②产品等级结构:产品等级结构即产品的继承结构。如抽象电视机与具体品牌的电视机之间构成了一个产品等级结构。如,AMD和Intel这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成。

(2)抽象工厂模式的结构图

  ①Abstract Factory:抽象工厂,定义创建一系列产品对象的操作接口

  ②Concrete Factory:具体的工厂,实现抽象工厂定义的方法,具体实现一系列对象的创建。

  ③Abstract Product:定义一类产品对象的接口

  ④Concrete Product:具体的产品实现对象,通常在具体工厂里面,会选择具体的产品实现对象,来创建符合抽象工厂类中定义的相应的接口类型对象。

  ⑤Client:客户端,主要使用抽象工厂来获取一系列所需要的产品对象,然后面向这些产品对象的接口编程。

2. 思考抽象工厂模式

(1)抽象工厂模式的本质:选择产品簇的实现,即每个具体工厂创建的是一系列的产品。

  ①工厂方法针对的是单个产品对象的创建,本质是选择单个产品的实现。而抽象工厂着重的是创建一个产品簇的实现,在抽象工厂类里定义的接口通常是有联系的,它们都是产品的某一部分或者是相互依赖的。注意,这些接口不是任意堆砌的,而是一系列相关或相互依赖的方法。

  ②如果抽象工厂里面只定义一个方法,直接创建产品,那就退化为工厂方法了。

(2)切换产品簇:只要提供不同的抽象工厂实现,产品簇就可以作为一个整体被切换

【编程实验】电脑装机方案

//创建型模式:抽象工厂模式
//电脑组装
#include <stdio.h>

////////////////////////////////////////////产品(等级)////////////////////////////
//产品等级1:
//1、抽象CPU类
class CPUApi
{
public:
    virtual void calculate() = 0;
};

//1.1 AMD CPU(具体CPU产品类)
class AMDCPU : public CPUApi
{
private:
    int pints;
public:
    AMDCPU(int pints){this->pints = pints;}
    void calculate()
    {
        printf("now in AMD CPU, pints = %d\n",pints);
    }    
};

//1.2 Intel CPU(具体CPU产品类)
class IntelCPU : public CPUApi
{
private:
    int pints;
public:
    IntelCPU(int pints){this->pints = pints;}
    void calculate()
    {
        printf("now in Intel CPU, pints = %d\n",pints);
    }    
};

//产品等级2
//2.抽象主板类
class MainboardApi
{
public:
    virtual void installCPU() = 0;  
};

//2.1 技嘉主板
class GAMainboard : public MainboardApi
{
private:
    int cpuHoles; //CPU插槽的孔数
public:
    GAMainboard(int cpuHoles){this->cpuHoles = cpuHoles;}
    void installCPU()
    {
        printf("now in GAMainboard, cpuHoles = %d\n", cpuHoles);    
    }    
};

//2.2 微星主板
class MSIMainboard : public MainboardApi
{
private:
    int cpuHoles; //CPU插槽的孔数
public:
    MSIMainboard(int cpuHoles){this->cpuHoles = cpuHoles;}
    void installCPU()
    {
        printf("now in MSIMainboard, cpuHoles = %d\n", cpuHoles);    
    }    
};

////////////////////////////////////////////工厂类////////////////////////////
//3.抽象工厂接口,声明创建抽象产品对象的操作
class AbstractFactory
{
public:
    //创建CPU对象
    virtual CPUApi* createCPUApi() = 0;
    
    //创建主板对象
    virtual MainboardApi* createMainboardApi() = 0;    
};

//3.1具体工厂(装机方案1):Intel的CPU+技嘉主板
//  这里创建的CPU和主板对象是能匹配的,即有约束关系  
class Schema1 : public AbstractFactory
{
public:
    //CPU
    CPUApi* createCPUApi()
    {
        return new IntelCPU(1156);
    }
    
    //主板对象
    MainboardApi* createMainboardApi()
    {
        return new GAMainboard(1156);
    }    
};

//3.2具体工厂(装机方案2):AMD的CPU+微星主板
//  这里创建的CPU和主板对象是能匹配的,即有约束关系  
class Schema2 : public AbstractFactory
{
public:
    //CPU
    CPUApi* createCPUApi()
    {
        return new AMDCPU(939);
    }
    
    //主板对象
    MainboardApi* createMainboardApi()
    {
        return new MSIMainboard(939);
    }    
};

int main()
{
    //客户端调用例子
    
    //工厂
    AbstractFactory* af = new Schema1(); //可以整体换装机方案
    
    //CPU
    CPUApi* cpu = af->createCPUApi();
    
    //主板
    MainboardApi* mainboard = af->createMainboardApi();
    
    //测试一下配件是否正常
    cpu->calculate();
    mainboard->installCPU();
    
    delete cpu;
    delete mainboard;
    delete af;
    
    return 0;
}

3. 抽象工厂模式的优缺点

(1)优点

  ①封装性:抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。

  ②当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象(即产品对象是有约束的,不可以随心所欲的创建对象)。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。

  ③增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”

(2)缺点

  ①在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。

  ②开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。

4. 抽象工厂模式的使用场景

(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

(2)这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品

(3)同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束(比如:Intel主板必须使用Intel CPU、Intel芯片组),就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点

5. 抽象工厂模式和DAO

(1)数据访问对象(Data Access Object,DAO):用于解决访问数据对象所面临的一系列问题。

  ①数据源不同,如本地数据源和远程服务器上的数据源

  ②存储类型不同(如关系型数据库(RDBMS)、XML、纯文件等。

  ③访问方式不同,比如ODBC、ADO

  ④供应商不同,如Oracle、DB2、SqlServer、MySQL等等。

  ⑤版本不同,如关系型数据库,不同版本,实现功能是有差异的。就算是对标准的SQL的支持,也是有差异的。

(2)DAO和抽象工厂的关系

  ①DAO模式最常见的实现策略是工厂的策略,而且多是通过抽象工厂模式来实现。

  ②在使用抽象工厂模式时,也可以结合工厂方法模式。

(3)DAO访问方式的工厂实现策略(以订单处理为例)

  ①订单通常分为主记录(主表)和明细记录(子表)

  ②当业务对象需要操作订单的主表时,一般也需要操作子表

  ③如果业务简单,且对数据的操作是固定的,即不管订单业务如何变化,底层数据存储都是一样的,那么这时可以采用工厂方法模式。如果底层存储不固定(如存在数据库,或Xml文件),则一般采用抽象工厂模式

【编程实例】利用抽象工厂模式将业务对象存储在不同的数据仓库

//创建型模式:抽象工厂模式
//数据访问对象策略的抽象工厂实现
#include <stdio.h>

//以下的所说的数据仓库可以是关系型系数据库或Xml

//产品对象的接口,就是订单主、子记录的DAO定义
//订单主记录的DAO定义:(提供保存业务对象到数据仓库的功能)

class COrderMainDAO
{
public:
    virtual void saveOrderMain() = 0;
};

//订单子记录的DAO定义(提供保存业务对象到数据仓库的功能)

class COrderDetailDAO
{
public:
    virtual void saveOrderDetail() = 0;   
};

//Rdb实现:将业务对象保存到关系型数据库
class CRdbMainDAOImpl : public COrderMainDAO
{
public:
    void  saveOrderMain()
    {
        printf("now in RdbMainDAOImpl saveOrderMain\n");
    }   
};

//Rdb实现:将业务对象保存到关系型数据库
class CRdbDetailDAOImpl : public COrderDetailDAO
{
public:
    void  saveOrderDetail()
    {
        printf("now in RdbDetailDAOImpl saveOrderDetail\n");
    }   
};

//Xml实现:将业务对象保存到xml文件
class CXmlMainDAOImpl : public COrderMainDAO
{
public:
    void saveOrderMain()
    {
        printf("now in XmlMainDAOImpl saveOrderMain\n");        
    }
};

//Xml实现:将业务对象保存到xml文件
class CXmlDetailDAOImpl : public COrderDetailDAO
{
public:
    void  saveOrderDetail()
    {
        printf("now in XmlDetailDAOImpl saveOrderDetail\n");
    }   
};


//抽象工厂,创建订单主、子记录对应的DAO对象
class CDAOFactory
{
public:
    //订单主记录对应的DAO对象
    virtual COrderMainDAO* createOrderMainDAO() = 0; 
    
    //订单子记录对应的DAO对象
    virtual COrderDetailDAO* createOrderDetailDAO() = 0;
};

//具体工厂
//关系型数据库的实现方式
class CRdbDAOFactory : public CDAOFactory
{
public:
    COrderMainDAO* createOrderMainDAO()
    {
        return new CRdbMainDAOImpl();
    }   
    
    COrderDetailDAO* createOrderDetailDAO()
    {
        return new CRdbDetailDAOImpl();
    }    
};

//Xml的实现方式
class CXmlDAOFactory : public CDAOFactory
{
public:
    COrderMainDAO* createOrderMainDAO()
    {
        return new CXmlMainDAOImpl();
    }   
    
    COrderDetailDAO* createOrderDetailDAO()
    {
        return new CXmlDetailDAOImpl();
    }    
};


int main()
{
    //客户端调用例子
    
    //创建DAO的抽象工厂
    //CDAOFactory* df = new CRdbDAOFactory();
    CDAOFactory* df = new CXmlDAOFactory();
    
    //通过抽象工厂来获取需要的DAO接口
    COrderMainDAO* mainDAO = df->createOrderMainDAO();
    COrderDetailDAO* detailDAO = df->createOrderDetailDAO();
    
    //调用DAO来完成数据存储的功能
    mainDAO->saveOrderMain();
    detailDAO->saveOrderDetail();
    
    delete mainDAO;
    delete detailDAO;
    delete df;
 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/CherishPrecious/article/details/83986821