面向对象设计,为什么?
回答:变化是复用的天敌!面向对象设计最大的优势在于:抵御变化
重新认识面向对象
理解隔离变化
从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小
各司其职
• 从微观层面来看,面向对象的方式更强调各个类的“责任”
• 由于需求变化导致的新增类型不应该影响原来类型的实现——是所谓各负其责
对象是什么?
• 从语言实现层面来看,对象封装了代码和数据。
• 从规格层面讲,对象是一系列可被使用的公共接口。
• 从概念层面讲,对象是某种拥有责任的抽象。
GOF-23 模式分类
从目的来看:
• 创建型(Creational)模式:将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。
• 结构型(Structural)模式:通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击。
• 行为型(Behavioral)模式:通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击。
从范围来看:
• 类模式处理类与子类的静态关系。
• 对象模式处理对象间的动态关系。
从封装变化角度对模式分类
重构获得模式 Refactoring to Patterns
面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”指是那些可以满足 “应对变化,提高复用”的设计 。
现代软件设计的特征是**“需求的频繁变化”。设计模式的要点是“寻找变化点,然后在变化点处应用设计模式,从而来更好地应对需求的变化”.“什么时候、什么地点应用设计模式”**比“理解设计模式结构本身”更为重要。
设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。**敏捷软件开发实践提倡的“Refactoring to Patterns”**是目前普遍公认的最好的使用设计模式的方法。
重构关键技法
静态 -> 动态
早绑定 -> 晚绑定
继承 -> 组合
编译时依赖 -> 运行时依赖
紧耦合 -> 松耦合
模板方法模式(c++)
一、组件协作型模式
“组件协作”模式 :现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
早绑定——————>晚绑定
早绑定:在应用程序中调用库
晚绑定:在库中调用应用程序,模板方法模式就是把主流程放在库里,库的使用者只需要实现子步骤就可以了
典型模式
• Template Method
• Observer / Event
• Strategy
【1】TemplateMethod
**动机(Motivation)**💡:
- 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因 (比如框架与应用之间的关系)子步骤无法和任务的整体结构同时实现。
**问题思考(Consider)**🤔:
- 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
对某一项任务,用结构化软件设计流程(使用了早绑定)和面向对象设计软件流程(使用了晚绑定)的区别:
(1)结构化软件设计流程
C++执行代码:
template1_lib.cpp
#include<iostream>
using namespace std;
//程序库开发人员
class Library {
public:
void Step1() {
cout << "Step1" << endl;
}
void Step3() {
cout << "Step3" << endl;
}
void Step5() {
cout << "Step5" << endl;
}
};
template1_app.cpp
#include "template1_lib.cpp"
//应用程序开发人员
class Application {
public:
bool Step2() {
cout << "myStep2" << endl;
return true;
}
void Step4() {
cout << "myStep4" << endl;
}
};
int main() {
Library lib;
Application app;
lib.Step1();
if (app.Step2()) {
lib.Step3();
}
for (int i = 0; i < 4; i++) {
app.Step4();
}
lib.Step5();
}
(2)面向对象软件设计流程
C++执行代码:
template2_lib.cpp
#include<iostream>
using namespace std;
//程序库开发人员
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() {
//稳定
cout << "Step1" << endl;
}
void Step3() {
//稳定
cout << "Step3" << endl;
}
void Step5() {
//稳定
cout << "Step5" << endl;
}
virtual bool Step2() = 0;//变化
virtual void Step4() = 0;//变化
};
template2_app.cpp
#include "template2_lib.cpp"
#include <iostream>
using namespace std;
//应用程序开发人员
class Application : public Library {
protected:
virtual bool Step2() {
//... 子类重写实现
cout << "override Step2" << endl;
return true;
}
virtual void Step4() {
//... 子类重写实现
cout << "override Step4" << endl;
}
};
int main() {
Library* pLib = new Application();
pLib->Run();
delete pLib;
return 0;
}
模式定义
定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。
——《设计模式》GoF
结构(Structure)
要点总结
Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制**(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构**。
除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法。
【2】Strategy 策略模式
动机(Motivation)💡:
- 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
**问题思考(Consider)**🤔:
- 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
C++执行代码:
strategy1.cpp(无使用Strategy策略模式)
若想新增法国税法,如何实现?
#include<iostream>
using namespace std;
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //更改(增加法国税法计算方式)
};
class SalesOrder {
TaxBase tax;
double sum = 0;
public:
double CalculateTax(TaxBase tax) {
//...
if (tax == CN_Tax) {
//CN***********
cout << "中国税法计算" << endl;
sum += 1;
}
else if (tax == US_Tax) {
//US***********
cout << "美国税法计算" << endl;
sum += 2;
}
else if (tax == DE_Tax) {
//DE***********
cout << "德国税法计算" << endl;
sum += 3;
}
else if (tax == FR_Tax) { //更改(增加法国税法计算方式)
cout << "法国税法计算" << endl;
sum += 4;
}
//....
return sum;
}
};
int main() {
TaxBase tax;
tax = FR_Tax;
SalesOrder *so = new SalesOrder();
cout << so->CalculateTax(tax) << endl;
return 0;
}
缺点:第一种方法需要去改枚举类型,需要往方法内部补充一些代码,打破了开放封闭原则,违背了复用性(二进制层面的复用性)。
strategy2.cpp(使用Strategy策略模式)
// 这两个类的具体实现不重要
class Context {
};
class StrategyFactory {
public:
TaxStrategy* NewStrategy() {
return nullptr; // ...
}
};
//税率策略模式类
class TaxStrategy {
public:
virtual double Calculate(const Context& context) = 0;
virtual ~TaxStrategy() {}
};
//CNTax 中国税法
class CNTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//USTax 美国税法
class USTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//DETax 德国税法
class DETax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//扩展
//*********************************
//FRTax 法国税法
class FRTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//.........
}
};
//不需要变化的
class SalesOrder {
private:
TaxStrategy* strategy;
public:
// 工厂模式
SalesOrder(StrategyFactory* strategyFactory) {
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder() {
delete this->strategy;
}
double CalculateTax() {
//...
Context context;
double val = strategy->Calculate(context); //多态调用
//...
}
};
第二种写法遵循了开放封闭原则
模式定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
——《设计模式》GoF
结构(Structure)
要点总结
Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
【3】Observer 观察者模式
动机(Motivation)💡:
在软件构建过程中,我们需要为某些对象建立一种**“通知依赖关系”** ——一个对象(目标对象)的状态发生改变,所有的依赖对
象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
**问题思考(Consider)**🤔:
C++执行代码:
FileSplitter1.cpp
#include <string>
#include <iostream>
using std::string;
class ProgressBar{
public:
void setValue(float value){
// ...
}
};
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);
}
}
};
MainForm1.cpp
#include <string>
#include <iostream>
using std::string;
// 以下几个类的具体实现不重要
class Form{
};
class TextBox{
public:
string getText(){
// ...
return "";
}
};
class ProgressBar;
class FileSplitter{
public:
FileSplitter(string filePath, int number, ProgressBar* progressBar){
// ...
}
void split(){
// ...
}
};
//
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();
}
};
FileSplitter1.cpp
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
class FileSplitter
{
string m_filePath;
int m_fileNumber;
//ProgressBar* m_progressBar; //具体通知机制
//IProgress* m_iprogressList;//抽象通知机制,支持1个观察者
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++;
}
}
};
MainForm2.cpp
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 << ".";
}
};
网上代码(来子这个博主):https://github.com/chouxianyu/design-patterns-cpp
Observer.cpp:
#include <iostream>
#include <vector>
class Subject;
/// Observer 和 ConcreteObserver ///
//抽象观察者
class Observer
{
public:
virtual ~Observer() {}
virtual int getState() = 0;
virtual void update(Subject* subject) = 0;
};
//具体观察者
class ConcreteObserver : public Observer
{
public:
ConcreteObserver(const int state) :
observer_state(state) {} //osbserver_state = state
//析构函数
~ConcreteObserver() {}
//获取observer_state
int getState()
{
return observer_state;
}
void update(Subject* subject);
private:
int observer_state;
};
/// Subject 和 ConcreteObserver ///
class Subject
{
public:
virtual ~Subject() {}
//往observers添加observer
void attach(Observer* observer)
{
observers.push_back(observer);
}
//从observers删除指定index的observer
void detach(const int index)
{
observers.erase(observers.begin() + index);
}
//遍历observers,调用ConcreteObserver的update函数
void notify()
{
for (unsigned int i = 0; i < observers.size(); i++)
{
observers.at(i)->update(this);
}
}
virtual int getState() = 0;
virtual void setState(const int s) = 0;
private:
std::vector<Observer*> observers;// 抽象通知机制,支持多个观察者
};
class ConcreteSubject : public Subject
{
public:
~ConcreteSubject() {}
int getState()
{
return subject_state;
}
void setState(const int s)
{
subject_state = s;
}
private:
int subject_state;
};
void ConcreteObserver::update(Subject* subject)
{
observer_state = subject->getState();
std::cout << "Observer state updated." << std::endl;
}
int main()
{
ConcreteObserver observer1( 1 );
ConcreteObserver observer2( 2 );
std::cout << "Observer 1 state: " << observer1.getState() << std::endl;//Observer 1 state: 1
std::cout << "Observer 2 state: " << observer2.getState() << std::endl;//Observer 2 state: 2
Subject *subject = new ConcreteSubject();
subject->attach( &observer1 ); //将observer1 放进observers
subject->attach( &observer2 ); //将observer2 放进observers
subject->setState( 10 );//设置subject的subject_state = 10
subject->notify();//遍历observers,调用ConcreteObserver的update(Subject *subject)函数,将subject的subject_state=10 给 observers里的每一个对象设置 observer_state = 10
std::cout << "Observer 1 state: " << observer1.getState() << std::endl;//Observer 1 state: 10
std::cout << "Observer 2 state: " << observer2.getState() << std::endl;//Observer 2 state: 10
delete subject;
return 0;
}
模式定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
——《设计模式》GoF
结构(Structure)
要点总结
使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
观察者自己决定是否需要订阅通知,目标对象对此一无所知。
Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。