设计模式 组件协作

现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。

template

经典场景

  • 框架和应用
    • 框架的整体结构稳定,但是开发的不同应用有着不同的需求变化

思想

设计模式 组件协作_第1张图片

  • 利用虚函数 支持晚绑定,将流程规划好,但是具体实现延迟到子类中去
    • 非虚函数是支持流程化的,流程化的函数里面调用虚函数,就是稳定中带有不稳定

【设计模式中必须要有一个(相对)稳定点,全都不稳定是不行的,全都是稳定的,设计模式也没有作用了】

  • 晚绑定机制
    • C++最典型的就是虚函数进行反向控制
    • C开始就有函数指针了,其实虚函数也是通过函数指针实现的

uml

设计模式 组件协作_第2张图片



stragety

经典场景

联想到我之前写ns3的时候,换拓扑就用if-else,然后师兄就问我如果再加新的拓扑怎么办,我:“再加else呗”。(卒

思想

设计模式 组件协作_第3张图片

  • if-else这样就违背了开放封闭原则

  • 每次扩展都要加else进行添加

    • 虽然不需要对原有代码进行"修改"(狭隘的“修改”),只是增加了新代码,但是这不叫代码复用,复用一般是以编译单元角度来看的( 这个文件还是要重新编译啊,所以还是算修改了(广义))
    • 我们在实际工程中会发现,在原有代码后面进行添加往往会打破前面代码,对前面的代码引入bug
    • 非常不推荐if-else和switch模式 --》除非if-else是绝对稳定不变的情况下(比如一周只有七天) ][if和switch是代码中的bad smell]
  • 应该用子类去继承一个strategy类,然后在SalesOrder里面声明一个函数指针(利用多态

    • 这个对象指针用工厂模式创建
    • 这样也能提高代码的本定性(所有代码命中率都很高) -->只是一个顺带的好处

      代码命中率低的话,浪费高速缓存啊!

uml

设计模式 组件协作_第4张图片



obeserver

经典场景

MVC架构;基于事件的UI框架
非常重要

思想

再一个主窗口中显示子应用的进度

  • 主窗口
    设计模式 组件协作_第5张图片

  • 子应用
    设计模式 组件协作_第6张图片

  • 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

  • 记得方式观察者、订阅者、访问者没分清,留坑

你可能感兴趣的:(基础)