C++设计模式之1-工厂模式

作者: veaxen 分类: 设计模式 发布时间: 2019-02-21 22:59

一、工厂模式描述

定义:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

类型:创建类模式

用途:工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,降低耦合度,达到提高灵活性的目的。

类图:

blob.jpg

现在一般看来将工厂模式分为三类:

  1. 简单工厂模式(Simple Factory)

  2. 工厂方法模式(Factory Method)

  3. 抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。

GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。
将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

但是两种看法本质上是一致的,而且我们没必要深究那么多,下面来看看这些工厂模式是怎么来“治病”的。

工厂模式属于创建型模式,大致可以分为三类,简单工厂模式、工厂方法模式、抽象工厂模式。听上去差不多,都是工厂模式。

下面一个个介绍,首先介绍简单工厂模式,它的主要特点是需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类。

举个例子就明白了。有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号A,B的处理器核。不同的客户有不同的需求,有的客户喜欢A核,有的客户偏爱B核,怎么满足这种需求呢?

简单工厂,客户需要什么样的处理器核,一定要显示地告诉生产工厂。下面给出一种实现方案。

二、简单工厂模式

简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口

先来看看它的组成:

  • 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。它往往由一个具体类实现。
  • 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。
  • 具体产品角色:工厂类所创建的对象就是此角色的实例。由一个具体类实现。
#include <iostream>

// 生成产品依据标识
typedef enum ClassType {
    SINGCORE_A,
    SINGCORE_B,
} ClassType;

// 基础产品类 -=> 对应于抽象产品角色
class SingCore {
public:
    virtual ~SingCore() {};
    virtual void Show() = 0;
};

// 单核产品S -=> 对应于具体产品角色
class SingCoreA: public SingCore {
public:
    virtual ~SingCoreA() {};

    void Show() {
        std::cout << "SingCore A..." << std::endl;
    }
};

// 单核产品B -=> 对应于具体产品角色
class SingCoreB: public SingCore {
public:
    virtual ~SingCoreB() {};

    void Show() {
        std::cout << "SingCore B..." << std::endl;
    }
};

// 工厂类 -=> 对应于工厂角色
class Factory {
public:
    virtual ~Factory() {};

    SingCore* CreateSingCore(ClassType classType) {
        switch(classType) {
            case SINGCORE_A :
                return new SingCoreA();
            case SINGCORE_B :
                return new SingCoreB();
        }
    }
}

int main() {
    Factory* factory = new Factory();

    factory->CreateSingCore(SINGCORE_A)->Show();
    factory->CreateSingCore(SINGCORE_B)->Show();

    delete factory;
}

首先,使用了简单工厂模式后,我们的程序更加符合现实中的情况;而且客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品,使客户端不再依赖与具体产品的创建,就像我们买产品A和产品B,我们只关心它是不是我们需要的产品,并不关心它是怎么被创建的

但是这样设计的也有缺点,就是要增加新的核类型时,就需要修改工厂类。这就违反了开放封闭原则:软件实体(类、模块、函数)可以扩展,但是不可修改。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类,我们称它为全能类或者上帝类。

我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员,于是工厂方法模式作为救世主出现了。

三、工厂方法模式

所谓工厂方法模式,是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。

Factory Method工厂方法模式使一个类的实例化延迟到其子类。

工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。

你应该大致猜出了工厂方法模式的结构,来看下它的组成:

  1. 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。它由抽象类或者接口来实现。
  2. 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
  3. 抽象产品角色:它是具体产品继承的父类或者是实现的接口。一般有抽象类或者接口来实现。
  4. 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。由具体的类来实现。

还是以刚才的例子解释。这家生产处理器核的产家赚了不少钱,于是决定再开设一个工厂专门用来生产B型号的单核,而原来的工厂专门用来生产A型号的单核。这时,客户要做的是找好工厂,比如要A型号的核,就找A工厂要;否则找B工厂要,不再需要告诉工厂具体要什么型号的处理器核了。

#include <iostream>

// 核心产品基类  -=> 对应于抽象产品角色
class SingCore {
public :
    virtual ~SingCore(){};      // 虚析构函数

    virtual void Show() = 0;        // 产品显示函数
};

// 核心产品A  -=> 对应于具体产品角色
class SingCoreA : public SingCore {
public :
    virtual ~SingCoreA(){};     // 虚析构函数

    void Show() {
        std::cout <<"SingCore A..." <<std::endl;
    }
};

// 核心产品B  -=> 对应于具体产品角色
class SingCoreB : public SingCore {
public :
    virtual ~SingCoreB(){};     // 虚析构函数 

    void Show() {
        std::cout <<"SingCore B..." <<std::endl;
    }
};

// 工厂的基类 -=> 对应于抽象工厂角色
class Factory {
public:
    virtual ~Factory(){};           // 虚析构函数

    virtual SingCore* CreateSingCore() = 0; // 生产产品的生产线
};

// 生产核心A的工厂 -=> 对应于具体产品角色
class FactoryA : public Factory
{
public :
    virtual ~FactoryA(){};          // 虚析构函数 

    SingCoreA* CreateSingCore(){
        return new SingCoreA();
    }
};

// 生产核心B的工厂 -=> 对应于具体产品角色
class FactoryB : public Factory
{
public :
    virtual ~FactoryB( ){ };            // 虚析构函数 

    SingCoreB* CreateSingCore() {
        return new SingCoreB();
    }
};

int main( )
{
    Factory *factoryA = new FactoryA();
    factoryA->CreateSingCore()->Show();

    Factory *factoryB = new FactoryB();
    factoryB->CreateSingCore()->Show();

    return 0;
}

工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的!就相当于市面上同样的产品有很多个牌子,有A牌子和B牌子,每家都有一个单独的工厂用来生产。这样每次增加一个新的牌子的产品上市,都会有一家新的工厂出现。

四、抽象工厂模式

好了那到底什么是抽象工工厂呢,它到底有什么作用呢?

在将抽象工厂前我们先来认识一下产品族。

还是举这个例子,这家公司的技术不断进步,不仅可以生产单核处理器,也能生产多核处理器。每个处理器下都有A,B两种型号的。那么这里A单核和B单核就同属于一个单核产品族。A多核和B多核就同属于一个多核产品族。

对于这种情况,就需要用到抽象工厂了。它的定义为提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。具体这样应用,这家公司还是开设两个工厂,一个专门用来生产A型号的单核多核处理器,而另一个工厂专门用来生产B型号的单核多核处理器,每个工厂用来生产一个产品族。

抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象

而且使用抽象工厂模式还要满足以下条件:

  1. 系统中有多个产品族,而系统一次只可能消费其中一族产品。
  2. 同属于同一个产品族的产品以其使用。

看看抽象工厂模式的各个角色(和工厂方法的如出一辙):

  1. 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。它由抽象类或者接口来实现。
  2. 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。它由具体的类来实现。
  3. 抽象产品角色:它是具体产品继承的父类或者是实现的接口。一般有抽象类或者接口来实现。
  4. 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。由具体的类来实现

抽象工厂模式和工厂方法模式的区别:可以说,抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。你能见到的大部分抽象工厂模式都是这样的:它的里面是一堆工厂方法,每个工厂方法返回某种类型的对象。如果说抽象工厂中的工厂是一个实际的工厂的话,那么工厂方法中的工厂更像现实中工厂的一条生产线。

下面举个例子具体描述一下:
比如说工厂可以生产鼠标和键盘。那么抽象工厂的实现类(它的某个具体子类)的对象都可以生产鼠标和键盘,但可能工厂A生产的是罗技的键盘和鼠标,工厂B是微软的。这样A和B就是工厂,对应于抽象工厂;每个工厂生产的鼠标和键盘就是产品,对应于工厂方法;用了工厂方法模式,你替换生成键盘的工厂方法,就可以把键盘从罗技换到微软, 但是鼠标还是原来罗技的但是用了抽象工厂模式,你只要换家工厂,就可以同时替换鼠标和键盘一套。如果你要的产品有几十个,当然用抽象工厂模式一次替换全部最方便(这个工厂会替你用相应的工厂方法)所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线。

抽象工厂模式适合与如下的情况:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类可以创建多个具体产品类的实例。

下面附上抽象工厂的代码:

#include <iostream>

// 单核产品基类
class SingleCore {
public:
    virtual void Show() const = 0;
};

// 单核A
class SingleCoreA: public SingleCore {
public:
    void Show( )const {
        std::cout<<"SingleCore A"<<std::endl; 
    }
};

// 单核B
class SingleCoreB: public SingleCore {
public:
    void Show() const {
        std::cout<<"SingleCore B"<<std::endl; 
    }
};

// 多核产品类族基类
class MultiCore {
public:
    virtual void Show() const = 0;
};

// 多核产品A
class MultiCoreA : public MultiCore {
public:
    void Show() const {
        std::cout<<"Multi Core A"<<std::endl;
    }
};

// 多核产品B
class MultiCoreB : public MultiCore {
public:
    void Show() const {
        std::cout<<"Multi Core B"<<std::endl;
    }
};

// 工厂
class Factory {
public:
    virtual SingleCore* CreateSingleCore() = 0;
    virtual MultiCore* CreateMultiCore() = 0;
};

// 工厂A,专门用来生产A型号的处理器
class FactoryA :public Factory {
public:
    SingleCore* CreateSingleCore() {
        return new SingleCoreA();
    }

    MultiCore* CreateMultiCore() {
        return new MultiCoreA();
    }
};

// 工厂B,专门用来生产B型号的处理器
class FactoryB : public Factory {
public:
    SingleCore* CreateSingleCore() {
        return new SingleCoreB();
    }

    MultiCore* CreateMultiCore() {
        return new MultiCoreB();
    }
};

int main()
{
    // 生产产品A系列的产品工厂 
    FactoryA *factoryA = new FactoryA();
    factoryA->CreateSingleCore()->Show();
    factoryA->CreateMultiCore()->Show();

    // 生产B系列的产品工厂 
    FactoryB *factoryB = new FactoryB();
    factoryB->CreateSingleCore()->Show();
    factoryB->CreateMultiCore()->Show();
}

本文的例子都是直接返回对象的堆指针,会造成内存泄漏,但只是为了讲解一下工厂模式的基本思路,因此本文中不加以描述。


转载自:https://blog.csdn.net/gatieme/article/details/17952033

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据