0
点赞
收藏
分享

微信扫一扫

设计模式--Note2--组件协作类

Template Method

定义一个操作中的算法的骨架(稳定),而将一些步骤的实现延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override重写)该算法的某些特定步骤。

早绑定与晚绑定

区分稳定与变化

要点总结

  1. 非常基础的设计模式
  2. 最简洁的机制(虚函数的多态性)
  3. 提供扩展点(继承+多态)
  4. 反向控制结构(Lib控制App),Lib调用App

上述实例:

  1. run()是固定的
  2. 运行时,Lib的run()调用App的Step2、Step4
  1. 具体实现中,被Template Method调用的方法可以有也可以没有具体实现,一般推荐设置为protected方法

示例

// 结构化
class Library {
public:
Step1();
Step3();
Step5();
...
};

class Application {
public:
Step2();
Step4();
};

int main() {
Library lib;
Application app;

lib.Step1();

if (app.Step2()) {
lib.Step3();
}

for (...) {
app.Step4();
}

lib.Step5();
...
}

// 面向对象
class Library {
public:
// 稳定中包含变化
void Run() {
Step1();

if (Step2()) { // 支持变化 虚函数多态调用
Step3();
}

for (...) {
Step4();// 支持变化 虚函数多态调用
}

Step5();
}

virtual ~Library();
private:
Step1();
Step3();
Step5();

virtual Step2();
virtual Step4();
};

class Application : public Library {
public:
Step4();
Step5();
};

int main() {
Library* pLib = new Application;
pLib->run();// run()并不是虚函数,此处调用基类的run(),但是在run()内部的Step2()、Step4()又是虚函数,调用的时Application的Step2()、Step4()
delete pLib;
...
}

Strategy

策略

定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换(变化)。该模式使得算法可以独立于使用它的客户程序(稳定)而变化(扩展、子类化)。

解决什么问题

在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,会使得对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担。

如何在运行时根据需求更透明的更改对象的算法?

将算法与对象本身解耦,从而避免上述问题?

结构

设计模式--Note2--组件协作类_子类

要点总结

  1. Strategy及其子类为组件提供了一系列可重用的算法,从而使得类型在运行时方便的根据需要在各个算法之间切换
  2. Strategy提供了判断语句外的另一种选择
  3. 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省开销

示例:税率问题

// 结构化
enum TaxBase { // 变化
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax // 增加需求
};

class SalesOrder {
public:
// 稳定
double CalculateTax() {
// ...

if (tax == CN_Tax) {
// ...
}
else if (tax == US_Tax) {

}
else if (tax == DE_Tax) {

}
else if (tax == FR_Tax) { // 增加 应该对扩展开放,对修改封闭

}
//...
}

private:
TaxBase tax;
};

// Strategy
class TaxStrategy {
public:
virtual double Calculate(const Context& context)=0;
virtual ~TaxStrategy(){}// 基类最好都实现一个虚的析构函数
};

// 变化
class CNTax : public TaxStratygy {
public:
virtual double Calculate(const Context& context) {
// ...
}
};

class USTax : public TaxStratygy {
public:
virtual double Calculate(const Context& context) {
// ...
}
};

class DETax : public TaxStratygy {
public:
virtual double Calculate(const Context& context) {
// ...
}
};

// 增加
class FRTax : public TaxStratygy {
public:
virtual double Calculate(const Context& context) {
// ...
}
};

// 稳定
class SalesOrder {
public:
SalesOrder(StrategyFactory* strategyFactory) {
this->strategy = strategyFactory->NewStrategy();
}

~SalesOrder() {
delete this->strategy;
}

double Calculate() {
// ...
Context context();

double val = strategy->Calculate(contex);

// ...
}

private:
TaxStrategy* strategy;
};

Observer

观察者模式

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

解决什么问题

在软件构件过程中,我们需要为某些对象建立一种”通知依赖关系“,一个对象(目标对象)的状态发送改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于亲密,将使软件不能很好地抵御变化。

使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

结构

设计模式--Note2--组件协作类_依赖关系_02

要点总结

  1. 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
  2. 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
  3. 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
  4. Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。

示例:窗口消息通知

// 最初实现
class FileSplitter {
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) {

}
}

private:
string m_filePath;
int m_fileNumber;
};

class MainForm : public Form {
public:
void Button1_Click() {
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());

FileSplitter splitter(filePath, number);

splitter.split();
}

private:
TextBox* txtFilePath;
TextBox* txtFileNumber;
};

// 增加一个通知功能 显示文件切分进度
class FileSplitter {
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) {
// ...

if (m_progressBar != nullptr) {
float progressValue = m_fileNumber;
m_progressBar->setValur((i + 1) / progressValue);
}

}
}

private:
string m_filePath;
int m_fileNumber;

ProgressBar* m_progressBar;// 通知的方式是变化的 当通知是稳定的 稳定不应该依赖变化,而应该依赖抽象
};

class MainForm : public Form {
public:
void Button1_Click() {
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());

FileSplitter splitter(filePath, number, progressBar);

splitter.split();
}

private:
TextBox* txtFilePath;
TextBox* txtFileNumber;

ProgressBar* progressBar;
};

// 通知时,除了ProgressBar外,可能会使用其他方式
// 间ProgressBar与FileSplitter解耦合
class IProgress {
public:
virtual void DoProgress(float value) = 0;
virtual ~IProgress() {}
};

class ProgressBar {
public:
void setValue(float value) {
// ...
}
}

class FileSplitter {
public:
FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress) :
m_filePath(filePath),
m_fileNumber(fileNumber),
m_progressBar(iprogress) {

}

void split() {
// 1.读取文件

// 2.分批向小文件写入
for (int i = 0; i < m_fileNumber; ++i) {
// ...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);

}
}
protected:
void onProgress(float value) {
if (m_iprogress != nullptr) {
m_iprogress->DoProgress(value);
}
}


private:
string m_filePath;
int m_fileNumber;

//ProgressBar* m_progressBar;// 具体通知控件
IProgress* m_iprogress; // 抽象通知
};

class MainForm : public Form, public IProgress {
public:
void Button1_Click() {
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());

FileSplitter splitter(filePath, number, this);

splitter.split();
}

virtual void DoProgress(float value) {
progressBar->setValue(value);
}

private:
TextBox* txtFilePath;
TextBox* txtFileNumber;

ProgressBar* progressBar;
};

// 支持出现变化时通知多个对象
class IProgress {
public:
virtual void DoProgress(float value) = 0;
virtual ~IProgress() {}
};

class ProgressBar {
public:
void setValue(float value) {
// ...
}
}

class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value) {
// ...
}
};

class FileSplitter {
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:
void onProgress(float value) {
for (auto iprogress : m_iprogressList) {
m_iprogress->DoProgress(value);
}
}


private:
string m_filePath;
int m_fileNumber;

//ProgressBar* m_progressBar;// 具体通知控件
//IProgress* m_iprogress; // 抽象通知
vector<IProgress*> m_iprogressList; // 支持多个观察者
};

class MainForm : public Form, public IProgress {
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();
}

virtual void DoProgress(float value) {
progressBar->setValue(value);
}

private:
TextBox* txtFilePath;
TextBox* txtFileNumber;

ProgressBar* progressBar;
};

转载请注明出处



举报

相关推荐

0 条评论