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


    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实现。

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

  • 相关阅读:
    基于RTP的h.264视频传输系统设计(一)
    NAS配置Time Machine,在D-Link DNS-320上的配置笔记
    重构版机房收费系统之分层、接口、数据库连接、反射+工厂(vb.net)
    复制表机构
    JVM内存
    System.gc()
    重写(Override) 重载(Overload)
    final 关键字
    JAVA stack
    java 获取环境变量
  • 原文地址:https://www.cnblogs.com/perfy576/p/8548309.html
Copyright © 2020-2023  润新知