现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
template
经典场景
- 框架和应用
- 框架的整体结构稳定,但是开发的不同应用有着不同的需求变化
思想
- 利用虚函数 支持晚绑定,将流程规划好,但是具体实现延迟到子类中去
- 非虚函数是支持流程化的,流程化的函数里面调用虚函数,就是稳定中带有不稳定
【设计模式中必须要有一个(相对)稳定点,全都不稳定是不行的,全都是稳定的,设计模式也没有作用了】
- 晚绑定机制
- C++最典型的就是虚函数进行反向控制
- C开始就有函数指针了,其实虚函数也是通过函数指针实现的
uml
stragety
经典场景
联想到我之前写ns3的时候,换拓扑就用if-else,然后师兄就问我如果再加新的拓扑怎么办,我:“再加else呗”。(卒
思想
-
if-else这样就违背了开放封闭原则
-
每次扩展都要加else进行添加
- 虽然不需要对原有代码进行"修改"(狭隘的“修改”),只是增加了新代码,但是这不叫代码复用,复用一般是以编译单元角度来看的( 这个文件还是要重新编译啊,所以还是算修改了(广义))
- 我们在实际工程中会发现,在原有代码后面进行添加往往会打破前面代码,对前面的代码引入bug
- 非常不推荐if-else和switch模式 --》除非if-else是绝对稳定不变的情况下(比如一周只有七天) ][if和switch是代码中的bad smell]
-
应该用子类去继承一个strategy类,然后在SalesOrder里面声明一个函数指针(利用多态
- 这个对象指针用工厂模式创建
- 这样也能提高代码的本定性(所有代码命中率都很高) -->只是一个顺带的好处
代码命中率低的话,浪费高速缓存啊!
uml
obeserver
经典场景
MVC架构;基于事件的UI框架
非常重要
思想
再一个主窗口中显示子应用的进度
-
主窗口
-
子应用
-
progressbar,违背了DIP原则,主应用(高层)的实现居然要依赖于子应用(低层)的具体实现中,而且我万一不用进度条,就只想打点点表示正在处理中,岂不是主类和子类又都要修改?!
-
第一个想法就是 , 不要依赖一个具体的类,要依赖一个类的基类 --》 progressBar提到父类,然后调用父类的Doprogress,具体的操作每个子类自己去完成
-
进一步,在主类(observer)中,定义接口
可能这里会有些疑惑,为什么MainForm不添加一个IProgress指针,然后将progressBar(进度条形式进行提示)和ConsoleNotifier(cout省略号的形式进行提示) 一起继承IProgress,利用多态来实现呢?
我个人的理解是,这里打点和progressBar两个类相差太大(比如,提示函数的名称很难统一成一个(毕竟功能差那么多),再比如,打点就很简单一句话就能实现,progressBar类还需要一些别的绘制函数),很难提取出同一个基类,干脆用接口的形式,只要求observer实现DoProgress就行,至于你想进一步怎么操作,都不关subject的事情,这样同时实现了在subject处隐藏了修改点。【妙啊】
-
class MainForm : public Form, public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
ConsoleNotifier cn;
FileSplitter splitter(filePath, number);
splitter.addIProgress(this); //¶©ÔÄ֪ͨ
splitter.addIProgress(&cn)£» //¶©ÔÄ֪ͨ
splitter.split();
splitter.removeIProgress(this);
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
class FileSplitter
{
string m_filePath;
int m_fileNumber;
List<IProgress*> m_iprogressList; // ³éÏó֪ͨ»úÖÆ£¬Ö§³Ö¶à¸ö¹Û²ìÕß
public:
FileSplitter(const string& filePath, int fileNumber) :
m_filePath(filePath),
m_fileNumber(fileNumber){
}
void split(){
//1.preparation work
//2.main workload
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);//·¢ËÍ֪ͨ
}
}
void addIProgress(IProgress* iprogress){
m_iprogressList.push_back(iprogress);
}
void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
protected:
virtual void onProgress(float value){
List<IProgress*>::iterator itor=m_iprogressList.begin();
while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value);
itor++;
}
}
};
-
C++有多继承模式,但是不推荐(会出很多问题 ),但是推荐一种方法,就是只有一个骨骼继承类,剩下的都是接口(如下)
-
这里还做了进进一步的修改以支持多个观察者——对象指针变成指针vector (对应的增加add和remove操作)
q
- 记得方式观察者、订阅者、访问者没分清,留坑