设计模式--Note2--组件协作类
Template Method
定义一个操作中的算法的骨架(稳定),而将一些步骤的实现延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override重写)该算法的某些特定步骤。
早绑定与晚绑定
区分稳定与变化
要点总结
-
非常基础的设计模式
-
最简洁的机制(虚函数的多态性)
-
提供扩展点(继承+多态)
-
反向控制结构(Lib控制App),Lib调用App
上述实例:
- run()是固定的
- 运行时,Lib的run()调用App的Step2、Step4
-
具体实现中,被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
策略
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换(变化)。该模式使得算法可以独立于使用它的客户程序(稳定)而变化(扩展、子类化)。
解决什么问题
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,会使得对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担。
如何在运行时根据需求更透明的更改对象的算法?
将算法与对象本身解耦,从而避免上述问题?
结构
要点总结
- Strategy及其子类为组件提供了一系列可重用的算法,从而使得类型在运行时方便的根据需要在各个算法之间切换
- Strategy提供了判断语句外的另一种选择
- 如果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
观察者模式
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
解决什么问题
在软件构件过程中,我们需要为某些对象建立一种”通知依赖关系“,一个对象(目标对象)的状态发送改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于亲密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
结构
要点总结
- 使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
- 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
- 观察者自己决定是否需要订阅通知,目标对象对此一无所知。
- 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;
};
转载请注明出处