“组件协作”模式
现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者协作之间时常用的模式。
典型模式
Template Method
Strategy
Observer/Event
Template Method模式
模式定义
定义一个操作中的算法的骨架(稳定),而将一些步骤(变化)延迟到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override重写)该算法的某些特定步骤。
动机
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架和应用之间的关系)而无法和任务的整体结构同时实现。
如何实现在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求。
类图
AbstractClass
抽象模板类,定义了模板结构(一个或多个抽象方法)。
ConcreteClass
具体的模板类,实现抽象类中的抽象方法。
结构化软件设计流程
Library开发人员:
开发1、3、5三个步骤
Application开发人员:
开发2、4两个步骤以及程序主流程
绑定机制:早绑定
非Template Method模式软件设计流程
//template_lib1.cpp
//程序库开发人员
class Library{
public:
void Step1(){
//...
}
void Step3(){
//...
}
void Step5(){
//...
}
};
//template_app1.cpp
//应用程序开发人员
class Application{
public:
bool Step2(){
//...
}
void Step4(){
//...
}
};
//main.cpp
int main()
{
Library lib();
Application app();
lib.Step1();
if (app.Step2()){
lib.Step3();
}
for (int i = 0; i < 4; i++){
app.Step4();
}
lib.Step5();
}
Template Method模式软件设计流程
Library开发人员:
开发1、3、5三个步骤以及程序主流程
Application开发人员:
开发2、4两个步骤
绑定机制:晚绑定
//template_lib2.cpp
//程序库开发人员
class Library{
public:
//稳定 template method
void Run(){
Step1();
if (Step2()) { //支持变化 ==> 虚函数的多态调用
Step3();
}
for (int i = 0; i < 4; i++){
Step4(); //支持变化 ==> 虚函数的多态调用
}
Step5();
}
virtual ~Library(){ }
protected:
void Step1() { //稳定
//.....
}
void Step3() {//稳定
//.....
}
void Step5() { //稳定
//.....
}
virtual bool Step2() = 0;//变化
virtual void Step4() =0; //变化
};
//template_app2.cpp
//应用程序开发人员
class Application : public Library {
protected:
virtual bool Step2(){
//... 子类重写实现
}
virtual void Step4() {
//... 子类重写实现
}
};
//main.cpp
int main()
{
Library* pLib=new Application();
lib->Run();
delete pLib;
}
}
Template Method要点总结
- Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
- 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Mothod的典型应用。
- 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将他们设置为protected方法。
Stragety策略模式
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展、子类化)。
动机
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一种性能负担。
如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
类图
Context
语境类,持有一个Strategy的引用。
Strategy
抽象类,提供所有的具体策略类所需的接口。
ConcreteStrategy
具体策略类的实例,实现抽象类的抽象方法。
非Stragety策略模式软件设计流程
enum TaxBase {
CN_Tax,
US_Tax,
IT_Tax,
FR_Tax //新增
};
class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if (tax == CN_Tax){
//CN***********
}
else if (tax == US_Tax){
//US***********
}
else if (tax == IT_Tax){
//DE***********
}
else if (tax == FR_Tax){ //新增
//...
}
//....
}
};
Stragety策略模式软件设计流程
//TaxStrategy.cpp
class TaxStrategy{
public:
virtual double Calculate(const Context& context)=0;
virtual ~TaxStrategy(){}
};
//CNTax.cpp
class CNTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
//USTax.cpp
class USTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
//ITTax.cpp
class ITTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
//扩展
//*********************************
//FRTax.cpp
class FRTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//.........
}
};
//SalesOrder.cpp
class SalesOrder{
private:
TaxStrategy* strategy;
public:
SalesOrder(StrategyFactory* strategyFactory){
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder(){
delete this->strategy;
}
public double CalculateTax(){
//...
Context context();
double val =
strategy->Calculate(context); //多态调用
//...
}
};
解析:结构化设计中使用条件判断语句,存在支持不使用的算法,增加了软件的性能负担,更重要的是面对需求变动,违背了开放封闭原则。
Stragety策略模式要点总结
Stragety及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
Stragety模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Stragety模式。
如果Stragety对象没有实例变量,那么各个上下文可以共享同一个,从而节省对象开销。
Observer观察者模式
定义对象间的一对多(变化)的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
动机
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”------一个对象的(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都讲得到通知。如果这样的依赖关系过于紧密,将使软件不能很好的抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系的松耦合。
类图
Subject
对象的抽象类,定义了抽象类中对观察者列表的一系列操作, 包括增加,删除, 通知等。
Concrete Subject
具体对象的实例,实现抽象类中的接口
Observer
抽象观察者接口,定义了观察者对Subject类更新状态接受操作。
ConcreteObserver
具体观察者类,实现观察者接口更新Subject类通知等逻辑。
非Observer观察者模式软件设计流程
//FileSplitter.cpp
class FileSplitter
{
string m_filePath;
int m_fileNumber;
ProgressBar* m_progressBar;
public:
FileSplitter(const string& filePath, int fileNumber, ProgressBar* progressBar) :
m_filePath(filePath),
m_fileNumber(fileNumber),
m_progressBar(progressBar){
}
void split(){
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
m_progressBar->setValue(progressValue);
}
}
};
//MainForm.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(filePath, number, progressBar);
splitter.split();
}
};
Observer观察者模式软件设计流程
//IProgress.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
//FileSplitter.cpp
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.读取大文件
//2.分批次向小文件中写入
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++;
}
}
};
//
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);
}
};
//ConsoleNotifier.cpp
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
解析:结构化设计流程中的当面临需求变动(如添加进度显示),违背依赖倒置原则,Progressbar是实现细节,抽象(进度显示,稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。
Observer观察者模式要点总结
使用面向对象的抽象,Observer观察者模式可以使得我们独立地改变目标与观察者,从而使二者的关系达到松耦合。
目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
观察者可以自己决定是否需要订阅通知,目标对象对此一无所知。
Observer模式是基于对象事件的UI框架中非常常用的设计模式,也是mvc模式的一个重要组成部分。