C++设计模式:工厂方法模式(六)

1、定义与动机
  • 对象创建模式:通过对象创建模式绕开new,来避免对象(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
    • Factory Method
    • Abstract Factory
    • Prototype
    • Builder
  • 工厂模式定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。
  • 动机
    • 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
    • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种"封装机制"来避免客户程序和这种"具体对象创建工作"的紧耦合带来的问题?
2、案例分析

MainForm类绑定一个Button按钮触发一个文件分割的事件。

2.1、普通设计方式
cpp 复制代码
class FileSplitter{
    void split(){
        // ...
    }
};

class MainForm: public Form{

    void button_event(){
        FileSplitter *fileSplitter = new FileSplitter();
        fileSplitter->split();
    }
};
  • 这种代码存在一定的问题,fileSplitter这个对象是具体的实现 不是一个抽象;假如需要根据分割文件的类型派生出各种音频、文本、二进制文件等等的分割,这里的代码很明显无法应对"变化",违背开闭原则

  • 其实违背开闭原则可以从一个很简单的方式去处理:策略模式,这个需求其实就可以很好的使用策略模式去解决

2.2、策略模式
  • 通过多态的形式来解决这一需求:MainForm里面组合一个ISplitter抽象接口,在使用是动态的传入一个对象,此时就有了运行时的多态!
cpp 复制代码
class ISplitter{
    virtual void split() = 0;
    virtual ~ISplitter(){}
};

class FileSplitter: ISplitter{
    virtual void split() override{
        // ...
    }
};

class VideoSplitter: ISplitter{
    virtual void split() override{
        // ...
    }
};

class MainForm: public Form{
private:
    ISplitter *splitter;
public:
    MainForm(ISplitter *iSplitter): splitter(iSplitter){

    }
    void button_event(){

        splitter.split();
    }
};

void process()
{
    MainForm mainForm(new FileSplitter());
    MainForm mainForm(new VideoSplitter());
}
3、工厂方法
  • 工厂方法模式是本节设计模式的核心,但是我觉得这个案例举例并不好,因为可以使用策略模式优化掉

  • 但是仔细一想,策略模式和工厂方法关注点并不一样。

3.1、优化代码(一)
  • 首先先把策略模式放在一边,把代码恢复到下面这个样子,这一份代码较比最开始的代码还是有一定的优化的,只是还是不够抽象!
  • 我们来分析下面这个代码存在的弊端问题:
    • 这一份代码核心点: ISplitter *splitter = new VideoSplitter();就是这个new语句
    • new对象这条语句,左半边已经优化好了是一个抽象,右半边是一个具体的实现,因此还是一个具体的实现,**右半边不够抽象!**这样就会导致是一个紧耦合的关系
    • 右半边不够抽象的原因:是因为是具体的new某个实现对象,也无法应对需求。就像一只猫,你没有缩小它的活动范围,使得它充斥在任何一个角落
cpp 复制代码
class ISplitter{
    virtual void split() = 0;
    virtual ~ISplitter(){}
};

class FileSplitter: ISplitter{
    virtual void split() override{
        // ...
    }

};

class VideoSplitter: ISplitter{
    virtual void split() override{
        // ...
    }

};

class MainForm: public Form{
public:
    MainForm(ISplitter *iSplitter): splitter(iSplitter){

    }
    void button_event(){
        ISplitter *splitter = new VideoSplitter();
//        ISplitter *splitter = new FileSplitter();
        splitter.split(..., ...);
    }
};

void process()
{
    MainForm mainForm(new FileSplitter());
    MainForm mainForm(new VideoSplitter());
}
3.2、工厂方法(二)
  • 工厂方法提倡的核心点是:在实现代码中不提倡使用new来具体指定某一个对象,而是将new这一过程向外传

  • 向外传的好处:使得这个实现变得抽象(赋值号右边)

  • 因此可以抽象出一个SplitterFactory接口,主要负责创建对象,具体的实现交给实现子类XXXSplitterFactory。

    • 第一个问题:为什么不把创建对象方法写在ISplitter接口中?因为要满足单一职责原则,强行写过去不单一

    • 第二个问题:是否满足开闭原则?满足,因为新加一个其他文件类型的分割类,只需要加XXXSplitter和XXXSplitterFactory。

    • 第三个问题:赋值号右边是具体的实现(不够抽象)吗?是一个抽象,完美解耦,只有运行时才知道具体的类型。

cpp 复制代码
class ISplitter{
    virtual void split() = 0;
    virtual ~ISplitter(){}
};

class SplitterFactory{
public:
    virtual ISplitter *CreateSplitter()=0;
    virtual ~SplitterFactory(){

    }
};

class FileSplitter: public ISplitter{
    virtual void split() override{
        // ...
    }
};

class VideoSplitter: ISplitter{
    virtual void split() override{
        // ...
    }
};


class FileSplitterFactory: public FileSplitter{
public:
    virtual ISplitter *CreateSplitter(){
        return new FileSplitter();
    }
};

class VideoSplitterFactory: public VideoSplitter{
public:
    virtual ISplitter *CreateSplitter(){
        return new VideoSplitter();
    }
};

class MainForm: public Form{
private:
    SplitterFactory *splitterFactory;
public:
    MainForm(SplitterFactory *_splitterFactory): splitterFactory(_splitterFactory){

    }
    void button_event(){
        ISplitter *splitter = splitterFactory->CreateSplitter();
        splitter.split();
    }
};

void process()
{
    MainForm mainForm(new FileSplitterFactory());
    MainForm mainForm(new VideoSplitterFactory());
}
4、总结
4.1 工厂方法总结
  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱
  • Factory Method模式通过面向对象的手法,将索要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合的关系。
  • Factory Method模式解决"单个对象"的需求变化,缺点在于要求创建方法、参数相同。
4.2、工厂模式与策略模式对比
工厂模式 策略模式
用途 工厂模式是创建型模式,它的作用是创建对象 策略模式是行为模式,它的作用是让一个对象在许多行为中选择一种行为
关注点 关注对象创建 关注行为的封装
解决问题 它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。 为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。
透明度 黑盒子 白盒子
相关推荐
虾球xz22 分钟前
CppCon 2018 学习:EFFECTIVE REPLACEMENT OF DYNAMIC POLYMORPHISM WITH std::variant
开发语言·c++·学习
缘来是庄33 分钟前
设计模式之组合模式
java·设计模式·组合模式
DKPT35 分钟前
Java组合模式实现方式与测试方法
java·笔记·学习·设计模式·组合模式
鼠鼠我呀237 分钟前
【设计模式09】组合模式
设计模式·组合模式
津津有味道2 小时前
Qt C++串口SerialPort通讯发送指令读写NFC M1卡
linux·c++·qt·串口通信·serial·m1·nfc
让我们一起加油好吗2 小时前
【C++】list 简介与模拟实现(详解)
开发语言·c++·visualstudio·stl·list
傅里叶的耶2 小时前
C++系列(二):告别低效循环!选择、循环、跳转原理与优化实战全解析
c++·visual studio
Vitta_U2 小时前
MFC的List Control自适应主界面大小
c++·list·mfc
N_NAN_N3 小时前
类图+案例+代码详解:软件设计模式----单例模式
java·单例模式·设计模式
尤物程序猿3 小时前
设计模式之代理模式--数据库查询代理和调用日志记录
设计模式·代理模式