一、分类描述
通过“对象创建”模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持创建的稳定。它是接口抽象后的第一步工作。
二、模式定义
工厂模式,是除了单例之外,听过最多的一个设计模式。
软件系统中,经常面临着创建对象的工作,由于需求的变化,需要创建对象的具体类型经常变化。如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
1、原始版本
最开始,只有一个文件分割器,但是还会出现许多其他的分割器。
class FileSplitter {
public:
void split() {
// ...
}
};
当有变化的时候,便需要抽象出基类。
class ISplitter {
public:
virtual void split() = 0;
virtual ~ISplitter() {}
};
// 具体类
class BinarySplitter : public ISplitter {};
class TxtSplitter : public ISplitter {};
class PictureSplitter : public ISplitter {};
class VedioSplitter : public ISplitter {};
// 使用场景
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());
// 此处ISplitter面向接口编程,但是编译的时候,还是要依赖具体(BinarySplitter)类
ISplitter *splitter = new BinarySplitter(filePath, number);
splitter.spilt();
}
};
2、初次优化
添加一个Factory类。
class SplitterFactory {
public:
ISplitter* createSplitter() {
return new BinarySplitter();
}
};
// 使用场景
class MainForm : public Form {
public:
void Button1_Click(){
// 先把参数干掉
SplitterFactory factory;
ISplitter *splitter = factory.createSplitter();
splitter.spilt();
}
};
3、再次优化
对于这个示例,我理解每一步是如何变化的。虚函数,延迟到运行时才知道具体调用哪个子类的方法。
// Factory也改为virtual
class SplitterFactory {
public:
virtual ISplitter* createSplitter() = 0;
virtual ~SplitterFactory() {}
};
// 具体工厂
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 VedioSplitterFactory: public SplitterFactory {
public:
virtual ISplitter* createSplitter() {
return new VedioSplitter();
}
};
// 使用场景
class MainForm : public Form {
SplitterFactory* factory;
public:
// 这里传递factory进来,为什么不直接传递ISplitter*呢?
// 重新看了一遍视频,再次思考,这里确实是可以直接传递ISplitter的,也没有依赖BinarySplitter。
// 所以应该是这个示例,不太合适?
// 四年后再看一遍这视频,依然没想到答案但有一个猜想方向:
// 此处添加的Factory层,是为将Splitter的创建更延迟些
MainForm(SplitterFactory* factory){
this->factory = factory;
}
void Button1_Click(){
ISplitter *splitter = factory->createSplitter(); // 多态new
splitter.spilt();
}
};
三、各种tip
1、面向接口编程,最简单的体现为,变量要申明为抽象基类。为什么要实现面向接口编程?依赖倒置,应该依赖抽象,不应该依赖实现细节。
2、C++创建对象的方法
BinarySplitter bs(); // 在栈上创建
ISplitter *splitter = new BinarySplitter(); // 在堆中创建
// 最后一种就是上面的Factory设定
3、工厂模式并不是把变化干掉,只是将变化放到一个位置,把猫放进笼子里。(其实整体来讲,这个模式想要达到的目标,依然是为了让代码修改起来更容易些。)
4、老师的原话,理解工厂模式后,其他的模式,很快就理解了?
5、看了Java的实现,发现比c++的简单一些。中间的差异是?c++多了一层对splitter的虚函数封装?Java这边的实现有点类似key-value方式,扩展只需要加key和value,并不影响原有流程。
6、关于虚函数,接口类的析构函数必须为虚函数,这基于内存原因,只有当析构函数为虚函数时,子类的析构函数才会被调用到;为多态而添加的虚函数,是一种延迟,延迟到运行时。
7、四年后,老师说首先应该知道问题,然后再去解决问题,如果不清楚问题而直接学设计模式,是会蒙掉的,现在的我认为极有道理,于是再做一次记录:设计模式最好的学习时机,可能是在做完一个完整项目后。