[设计模式] Factory Method

[设计模式] Factory Method

推荐阅读 C++ 设计模式 目录

动机

  • 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?

模式定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。 ——《设计模式》GoF

SHOW ME THE CODE

MainForm1.cpp

class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){
        
		string filePath = txtFilePath->getText();
        int number = atoi(txtFileNumber->getText().c_str());
        
        FileSplitter * splitter //声明成具体的类,没有考虑未来的变化
            = new FileSplitter(filePath,number);
        splitter->split();

	}
};

以静态的视角看过去其实没什么问题,但是以未来的视角来看实际上存在问题。

我们可以用抽象类/接口的方法来将问题暴露出来,回忆设计原则:面向接口编程

针对接口编程,而不是针对实现编程

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口(不要绝对化了,这里主要指业务类)
  • 客户程序无需获知对象的具体类型,需要知道对象所具有的接口
  • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案
  • 产业强盛的标志是接口标准化,接口标准化的本质是分工协作

写一个抽象类,然后考虑未来可能的扩展

//抽象类
class ISplitter{
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};

class BinarySplitter : public ISplitter{
    
};

class TxtSplitter: public ISplitter{
    
};

class PictureSplitter: public ISplitter{
    
};

class VideoSplitter: public ISplitter{
    
};

那么MainForm变为

class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){
		//去掉了一些没啥影响的代码
		ISplitter * splitter=//依赖抽象了
            new BinarySplitter();//依赖具体类
        
        splitter->split();

	}
};

在接口抽象之后还有个问题,对象创建(new)依赖了具体类

ISplitter * splitter=//依赖抽象了
    new BinarySplitter();//依赖具体类,违反了依赖倒置原则

根据依赖倒置(DIP):编译时依赖

  • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)
  • 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)

目前我们的思路大致如下

面向接口编程原则发现问题->接口抽象–>依赖倒置原则发现问题–>解决创建对象依赖具体类

考虑创建对象的方法

  • 直接创建一个抽象类?不可以,C++不允许这么做
  • 在堆上new一个对象?那就是上面代码暴露出的问题
  • 用一个方法来返回一个对象(实际上可以认为new也是一个方法)
class SplitterFactory{
public:
	ISplitter* CreateSpliter(){
		return new BinarySplitter();
	}
}
class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){

		SplitterFactory factory;
        ISplitter * splitter=
            factory.CreateSplitter(); 
        splitter->split();

	}
};

这样问题解决了吗?有改善但没完全解决

注意目前的依赖指的是编译时依赖

splitter依赖SplitterFactorySplitterFactory依赖BinarySplitter,最终splitter还是依赖BinarySplitter

那么考虑一下有没有运行时依赖的玩意:virtual函数

//工厂基类
class SplitterFactory{
public:
	virtual ISplitter* CreateSpliter()=0;
    virtual ~SplitterFactory(){}//任何一个纯虚基类都需要一个虚的析构函数
}
class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){

		SplitterFactory* factory;//依赖交给未来
        ISplitter * splitter=
            factory->CreateSplitter(); //多态new,用指针的方式调用纯虚基类
        splitter->split();

	}
};

virtual函数实际上就是一种延迟的手法,我们将依赖交给了未来

ISplitterFactory.cpp

//抽象类
class ISplitter{
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};


//工厂基类
class SplitterFactory{
public:
    virtual ISplitter* CreateSplitter()=0;
    virtual ~SplitterFactory(){}
};

FileSplitter2.cpp

//具体类
class BinarySplitter : public ISplitter{
    
};

class TxtSplitter: public ISplitter{
    
};

class PictureSplitter: public ISplitter{
    
};

class VideoSplitter: public ISplitter{
    
};

//具体工厂
class BinarySplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new BinarySplitter();
    }
};

class TxtSplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new TxtSplitter();
    }
};

class PictureSplitterFactory: public SplitterFactory{
public:
    virtual ISplitter* CreateSplitter(){
        return new PictureSplitter();
    }
};

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

MainForm2.cpp

class MainForm : public Form
{
    SplitterFactory*  factory;//工厂
public:
    
    MainForm(SplitterFactory*  factory){//未来从外界传递某一个具体的SplitterFactory
        this->factory=factory;
    }
    
	void Button1_Click(){

        
		ISplitter * splitter=
            factory->CreateSplitter(); //多态new
        
        splitter->split();

	}
};

现在MainForm解决了依赖具体类的问题

但是依赖具体类的问题真的完全被消灭了吗?可能没有。实际上我们的面向对象设计模式很多时候并不能消灭变化,但是我们可以把变化赶到某一个局部的地方。

在这个例子当中,我们真正解决的问题是变化在整个类设计的各个地方穿插,而非完全消灭变化

结构设计

红色稳定,蓝色变化

MainForm的创建现在依赖红色稳定的部分

image-20220207145959190

要点总结

  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  • Factory Method模式通过面向对象的手法(多态),将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
  • Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值