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、工厂模式与策略模式对比
工厂模式 策略模式
用途 工厂模式是创建型模式,它的作用是创建对象 策略模式是行为模式,它的作用是让一个对象在许多行为中选择一种行为
关注点 关注对象创建 关注行为的封装
解决问题 它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。 为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。
透明度 黑盒子 白盒子
相关推荐
娅娅梨29 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
兵哥工控33 分钟前
MFC工控项目实例二十九主对话框调用子对话框设定参数值
c++·mfc
我爱工作&工作love我40 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
lexusv8ls600h1 小时前
探索 C++20:C++ 的新纪元
c++·c++20
lexusv8ls600h1 小时前
C++20 中最优雅的那个小特性 - Ranges
c++·c++20
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
依旧阳光的老码农2 小时前
标准C++ 字符串
开发语言·c++
白-胖-子2 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-成绩排序
c++·算法·蓝桥杯·真题·蓝桥等考