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、工厂模式与策略模式对比
工厂模式 | 策略模式 | |
---|---|---|
用途 | 工厂模式是创建型模式,它的作用是创建对象 | 策略模式是行为模式,它的作用是让一个对象在许多行为中选择一种行为 |
关注点 | 关注对象创建 | 关注行为的封装 |
解决问题 | 它接受指令,创建出符合要求的实例;它主要解决的是资源的统一分发,将对象的创建完全独立出来,让对象的创建和具体的使用客户无关。主要应用在多数据库选择,类库文件加载等。 | 为了解决的是策略的切换与扩展,更简洁的说是定义策略族,分别封装起来,让他们之间可以相互替换,策略模式让策略的变化独立于使用策略的客户。 |
透明度 | 黑盒子 | 白盒子 |