设计模式-简单工厂模式、工厂模式、抽象工厂模式-(创建型模式)

0 创建型模式

工厂模式说起来很虚的感觉,如果构造函数很简单, 直接就可以new出来。那还需要工厂模式做什么?
设计模式嘛,就是要将简单的东西标准化,用统一的模式或者方式去做某件事情,包括创建对象。更重要的是设计模式一直在强调解耦。怎么解耦?通常的方法就是中间加一层——抽象层,高层抽象,底层抽象都向这个中间层靠。
那换句话说,其实是在“封装”代码。将一些常用,会用到的代码进一步封装。

因为啊,以后底层的类可能要更改,底层类的构造函数可能要更改,如果使用了工厂模式,那么只需要改这一个抽象层就行了,不用在所有代码里挨个找。

这种方式符合迪米特法则。

1 简单工厂模式

简单工厂模式不是23中设计模式中的一种。

1.0 需求

在继承中,一个基类有多个派生类,在不同的场景中可能需要不同派生类的对象。

而且派生类的构造方式可能改变,或许可能某个派生类被删除,使用new的方法创建的方法变得不可靠。

因此使用一种同一的方法来创建对象。

更深层的原因,可能是所需要的对象的代码,不属于我们维护。因此,底层的代码提供了同一的接口创建对象。

这就符合最少知道原则,使用了一个隔离层,来与实际功能类进行操作。

1.1 实现

1个基类,1个创建类,多个派生类。基类作为抽象层,为了让创建类能够返回派生类,符合依赖倒置原则。

创建类只有重载了的静态成员函数,返回值为基类指针。重载的原因在于,可能不同的派生类具有不同的参数。

//基类
class AB_Compiler
{
public:
    virtual void do() = 0;
};

//派生类1
class Gnu : public AB_Compiler 
{
public:
    virtual void do();
};

//派生类1
class Clang: public AB_Compiler
{
public:
    virtual void do(); 
};

//创建类
class Compiler
{
public:
    // 可能的多个重载
    // create 内部也可能是有多个 if 分支
    static AB_Compiler* create(/*可能不同的参数*/);
    static AB_Compiler* create(/*可能不同的参数*/);
}

  

1.2 缺点

当新的派生类产生的时候,创建类内部的代码需要变更。因此不符合开闭原则。

2 工厂模式

2.0 需求

为了解决简单工厂不符合开闭原则,因此出现了工厂模式。

工厂模式,则是在简单工厂模式的基础上再加一层抽象类。

2.1 实现

1个实际功能类的基类,1个创建类的基类,多个实际功能类,对应的多个实际功能类的创建类(继承自创建类的基类)。

使用的时候,先创建一个创建类,然后调用方法,创建对应的对象

//基类
class AB_Compiler
{
public:
    virtual void do() = 0;
};

//派生类1
class Gnu : public AB_Compiler 
{
public:
    virtual void do();
};

//派生类1
class Clang: public AB_Compiler
{
public:
    virtual void do(); 
};


//创建类基类
class Creator 
{
public:
    virtual AB_Compiler* create(/*参数*/)=0;
};

// 对应的创建类
class GunCreator :public Creator
{
   public:
    virtual AB_Compiler* create(/*参数*/); 
};

class ClangCreator :public Creator
{
   public:
    virtual AB_Compiler* create(/*参数*/); 
};

int main()
{
    Creator* creator = new ClangCreator;
    AB_Compiler* clang = creator->create();
}
View Code

代码又臭又长,折叠了。

当有新的实际功能类的时候,就添加对应的创建类。

2.2 缺点

工厂模式其实并没有提供统一的接口,因为在实例化创建类的时候,仍然需要知道对应创建类的类名。

因此这个就是简单一个迪米特法则,使用了一个隔离层(也就是创建类)

但实际上,创建类并不需要继承,直接创建对应的创建类对象,使用静态函数的方式即可。因为最终我们都需要知道对应的创建类的类型。

工厂模式是符合开闭原则的。在扩展的时候只增加类,而不需要修改已有的类。

3 抽象工厂模式

3.0 需求

工厂模式符合开闭原则,因此每个类也就只能创建一个实际功能类。也就是说一个实际功能类就对应了一个创建类。

而抽象工厂模式,则是组合了简单工厂模式和工厂模式:也就是一个创建者类可以创建不同的实际功能类。

例如实际功能类有版本差别的时候,就比如说,gnu有不同的版本,因此一个gnu的创建类,可以创建不同版本的gnu。

因此每一个创建类其实就是简单工厂模式。

3.1 实现

 代码同样又臭又长,折叠了。

其实现过程就是将同种产品不同版本号的对象,使用简单工厂模式来封装。

//基类
class AB_Compiler
{
public:
    virtual void do() = 0;
};

//派生类1
class Gnu_1_0 : public AB_Compiler 
{
public:
    virtual void do();
};
class Gnu_2_0 : public AB_Compiler 
{
public:
    virtual void do();
};

//派生类1
class Clang: public AB_Compiler
{
public:
    virtual void do(); 
};


//创建类基类
class Creator 
{
public:
    virtual AB_Compiler* create(/*参数*/)=0;
};

// 对应的创建类
class GunCreator :public Creator
{
   public:
    virtual AB_Compiler* create(/*参数*/); 
};
// 对应的创建类
class ClangCreator :public Creator
{
   public:
    virtual AB_Compiler* create(/*参数*/); 
};
View Code

3.2 缺点

和简单工厂模式一样,不符合开闭原则了,因此当有新版本的gnu加入的时候,gnu的创建类内部代码就需要修改。

4 总结

三种工厂模式的意义都在于,将new对象的过程封装起来,其原因和目的可能在于:

  1. 无法了解类的源码,类的源码由其他人维护,因此我们使用其他人提供的同一的接口来创建对象。
  2. 创建对象的过程可能会改变,例如,在创建对象的时候打个log,工厂模式可以不重载new实现。

工厂模式是迪米特法则的很好应用,同时也是在违背开闭原则时候的改变。

原文地址:https://www.cnblogs.com/perfy576/p/8548309.html