李建忠老师C++设计模式教程
源码资料来自
rhyspang/CPP-Design-Patterns: C++设计模式 (github.com)
组件协作:
单一职责:
对象创建:
对象性能:
接口隔离:
Façade
Proxy
Mediator
Adapter
状态变化:
Memento
State
数据结构:
Composite
Iterator
Chain of Resposibility
行为变化:
Command
Visitor
领域问题:
本文的代码,将老师在课程中一些疏忽错误的地方进行了修改
并在每个模式最后加入了个人的小结
有一个固定流程有5步,其中两步是需要随时更改的
旧版本由开发人员自己写流程和改动的两步
如何开发的更加合理和提高复用性
朴素版由应用开发人员写主流程
template1_lib.cpp
//程序库开发人员
class Library {
public:
void Step1() {
//...
}
void Step3() {
//...
}
void Step5() {
//...
}
};
template1_app.cpp
//应用程序开发人员
class Application {
public:
bool Step2() {
//...
}
void Step4() {
//...
}
};
int main() {
Library lib();
Application app();
lib.Step1();
if (app.Step2()) {
lib.Step3();
}
for (int i = 0; i < 4; i++) {
app.Step4();
}
lib.Step5();
}
借助继承的虚函数的晚绑定的机制
在程序库中直接写了主流程,应用开发人员仅需要实现具体细节函数即可
template2_lib.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; //变化
};
template2_app.cpp
//应用程序开发人员
class Application : public Library {
protected:
virtual bool Step2() {
//... 子类重写实现
}
virtual void Step4() {
//... 子类重写实现
}
};
int main() {
Library* pLib = new Application();
lib->Run();
delete pLib;
}
早绑定与晚绑定
版本1是早绑定
版本2是晚绑定
结构化软件设计流程
面向对象软件设计流程
这是非常非常常用的一种设计模式
常用到其实我们开发人员几乎每天都在无形中使用
比如:写一个创建一个线程的时候,一般只需要继承线程类
在override run()
然后再调用线程类的start()
这就是一个典型的模板方法
现有一个各种税率计算的类
现在需要添加新国家,如何避免在原代码上大量改动
加
if else
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //更改
};
class SalesOrder {
TaxBase tax;
public:
double CalculateTax() {
//...
if (tax == CN_Tax) {
// CN***********
} else if (tax == US_Tax) {
// US***********
} else if (tax == DE_Tax) {
// DE***********
} else if (tax == FR_Tax) { //更改
//...
}
//....
}
};
通过继承抽象类,来单独处理一个情况
软件使用时,动态的调用
SalesOrder
涉及到了工厂模式,但不是本文重点
class TaxStrategy {
public:
virtual double Calculate(const Context& context) = 0;
virtual ~TaxStrategy() {
}
};
class CNTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
class USTax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
class DETax : public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
//***********
}
};
//扩展
//*********************************
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;
}
public
double CalculateTax() {
//...
Context context();
double val = strategy->Calculate(context); //多态调用
//...
}
};
看到加if else或switch
的并列情况时候就要想到用策略模式
但是在确定if else
的可能性的情况下,可以保留
一个文件分割器,需要有一个进度条的展示 (这是一个实现细节)
实现细节非常容易变,如可能不用进度条,改为百分比,点点点或者其他
直接放一个进度条的对象
FileSplitter1.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);
}
}
};
MainForm1.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();
}
};
抽象出 事件 的本质,是一个通知
FileSplitter2.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_iprogress;
public:
FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress)
: m_filePath(filePath), m_fileNumber(fileNumber), m_iprogress(iprogress) {
}
void split() {
// 1.读取大文件
// 2.分批次向小文件中写入
for (int i = 0; i < m_fileNumber; i++) {
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);
}
}
protected:
virtual void onProgress(float value) {
if (m_iprogress != nullptr) {
// 更新进度条
m_iprogress->DoProgress(value);
}
}
};
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());
FileSplitter splitter(filePath, number, this);
splitter.split();
}
virtual void DoProgress(float value) {
progressBar->setValue(value);
}
};
如果是 一对多 一个通知,多个对象接收并操作
FileSplitter3.cpp
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.读取大文件
// 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++;
}
}
};
MainForm3.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 << ".";
}
};
这个案例的优化中第一反应很容易想到,ProgressBar抽象到一个基类中
但没从触发的角度思考,并且要考虑一对多的拓展性
考虑情景问题一定要多角度分析
有一个流的基类
拓展出了文件流,网络流,内存流
然后在这些流中进行一些扩展,比如加密操作,缓存操作等等
decorator1.cpp
最无脑的操作,直接对文件流,网络流,内存流进行继承,对应加密,缓存等操作
//业务操作
class Stream {
public:
virtual char Read(int number) = 0;
virtual void Seek(int position) = 0;
virtual void Write(char data) = 0;
virtual ~Stream() {
}
};
//主体类
class FileStream : public Stream {
public:
virtual char Read(int number) {
//读文件流
}
virtual void Seek(int position) {
//定位文件流
}
virtual void Write(char data) {
//写文件流
}
};
class NetworkStream : public Stream {
public:
virtual char Read(int number) {
//读网络流
}
virtual void Seek(int position) {
//定位网络流
}
virtual void Write(char data) {
//写网络流
}
};
class MemoryStream : public Stream {
public:
virtual char Read(int number) {
//读内存流
}
virtual void Seek(int position) {
//定位内存流
}
virtual void Write(char data) {
//写内存流
}
};
//扩展操作
class CryptoFileStream : public FileStream {
public:
virtual char Read(int number) {
//额外的加密操作...
FileStream::Read(number); //读文件流
}
virtual void Seek(int position) {
//额外的加密操作...
FileStream::Seek(position); //定位文件流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
FileStream::Write(data); //写文件流
//额外的加密操作...
}
};
class CryptoNetworkStream : public NetworkStream {
public:
virtual char Read(int number) {
//额外的加密操作...
NetworkStream::Read(number); //读网络流
}
virtual void Seek(int position) {
//额外的加密操作...
NetworkStream::Seek(position); //定位网络流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
NetworkStream::Write(data); //写网络流
//额外的加密操作...
}
};
class CryptoMemoryStream : public MemoryStream {
public:
virtual char Read(int number) {
//额外的加密操作...
MemoryStream::Read(number); //读内存流
}
virtual void Seek(int position) {
//额外的加密操作...
MemoryStream::Seek(position); //定位内存流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
MemoryStream::Write(data); //写内存流
//额外的加密操作...
}
};
class BufferedFileStream : public FileStream {
//...
};
class BufferedNetworkStream : public NetworkStream {
//...
};
class BufferedMemoryStream : public MemoryStream {
//...
};
class CryptoBufferedFileStream : public FileStream {
public:
virtual char Read(int number) {
//额外的加密操作...
//额外的缓冲操作...
FileStream::Read(number); //读文件流
}
virtual void Seek(int position) {
//额外的加密操作...
//额外的缓冲操作...
FileStream::Seek(position); //定位文件流
//额外的加密操作...
//额外的缓冲操作...
}
virtual void Write(byte data) {
//额外的加密操作...
//额外的缓冲操作...
FileStream::Write(data); //写文件流
//额外的加密操作...
//额外的缓冲操作...
}
};
void Process() {
//编译时装配
CryptoFileStream *fs1 = new CryptoFileStream();
BufferedFileStream *fs2 = new BufferedFileStream();
CryptoBufferedFileStream *fs3 = new CryptoBufferedFileStream();
}
decorator2.cpp
//业务操作
class Stream {
public:
virtual char Read(int number) = 0;
virtual void Seek(int position) = 0;
virtual void Write(char data) = 0;
virtual ~Stream() {
}
};
//主体类
class FileStream : public Stream {
public:
virtual char Read(int number) {
//读文件流
}
virtual void Seek(int position) {
//定位文件流
}
virtual void Write(char data) {
//写文件流
}
};
class NetworkStream : public Stream {
public:
virtual char Read(int number) {
//读网络流
}
virtual void Seek(int position) {
//定位网络流
}
virtual void Write(char data) {
//写网络流
}
};
class MemoryStream : public Stream {
public:
virtual char Read(int number) {
//读内存流
}
virtual void Seek(int position) {
//定位内存流
}
virtual void Write(char data) {
//写内存流
}
};
//扩展操作
// 这里的继承是为了完成接口规范
class CryptoStream : public Stream {
// 具体实现的发起方
// 组合引出多态
Stream* stream; //...
public:
// 不能直接实例 Stream
// 可以实例主体的分支
// - FileStream
// - NetworkStream
// - MemoryStream
CryptoStream(Stream* stm) : stream(stm) {
}
virtual char Read(int number) {
//额外的加密操作...
stream->Read(number); //读文件流
}
virtual void Seek(int position) {
//额外的加密操作...
stream->Seek(position); //定位文件流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
stream->Write(data); //写文件流
//额外的加密操作...
}
};
class BufferedStream : public Stream {
Stream* stream; //...
public:
BufferedStream(Stream* stm) : stream(stm) {
}
//...
};
void Process() {
//运行时装配
FileStream* s1 = new FileStream();
CryptoStream* s2 = new CryptoStream(s1);
MemoryStream* s3 = new MemoryStream(s1);
BufferedStream* s4 = new BufferedStream(s2);
}
decorator3.cpp
//业务操作
class Stream {
public:
virtual char Read(int number) = 0;
virtual void Seek(int position) = 0;
virtual void Write(char data) = 0;
virtual ~Stream() {
}
};
//主体类
class FileStream : public Stream {
public:
virtual char Read(int number) {
//读文件流
}
virtual void Seek(int position) {
//定位文件流
}
virtual void Write(char data) {
//写文件流
}
};
class NetworkStream : public Stream {
public:
virtual char Read(int number) {
//读网络流
}
virtual void Seek(int position) {
//定位网络流
}
virtual void Write(char data) {
//写网络流
}
};
class MemoryStream : public Stream {
public:
virtual char Read(int number) {
//读内存流
}
virtual void Seek(int position) {
//定位内存流
}
virtual void Write(char data) {
//写内存流
}
};
//扩展操作
// 中间的装饰类
DecoratorStream : public Stream {
protected:
Stream* stream; //...
DecoratorStream(Stream * stm) : stream(stm) {
}
};
class CryptoStream : public DecoratorStream {
public:
CryptoStream(Stream* stm) : DecoratorStream(stm) {
}
virtual char Read(int number) {
//额外的加密操作...
stream->Read(number); //读文件流
}
virtual void Seek(int position) {
//额外的加密操作...
stream->Seek(position); //定位文件流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
stream->Write(data); //写文件流
//额外的加密操作...
}
};
class BufferedStream : public DecoratorStream {
Stream* stream; //...
public:
BufferedStream(Stream* stm) : DecoratorStream(stm) {
}
//...
};
void Process() {
//运行时装配
FileStream* s1 = new FileStream();
CryptoStream* s2 = new CryptoStream(s1);
MemoryStream* s3 = new MemoryStream(s1);
BufferedStream* s4 = new BufferedStream(s2);
}
这种设计模式的样子非常特别,既有继承的类,又有基类成员变量的指针
但是这两者的作用是不一样的
从本样例来看,虽然都是从Stream
的延伸,但是
FileStream
主体的延伸
BufferedStream
是拓展的操作
两个的分支类型是不一样的,可以理解为一个是主体,一个数属性
既然这样就可以利用多态的指向提高代码的复用性了
有一个通讯模块,在不同平台要具体实现
然后在各自平台再退出不同版本
和装饰模式很像
但是这里的基类抽象方法很多(分类多,即不应该都集中在一个类中)
bridge1.cpp
// 通信的模块
class Messager {
public:
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual ~Messager() {
}
};
//平台实现
class PCMessagerBase : public Messager {
public:
virtual void PlaySound() {
//**********
}
virtual void DrawShape() {
//**********
}
virtual void WriteText() {
//**********
}
virtual void Connect() {
//**********
}
};
class MobileMessagerBase : public Messager {
public:
virtual void PlaySound() {
//==========
}
virtual void DrawShape() {
//==========
}
virtual void WriteText() {
//==========
}
virtual void Connect() {
//==========
}
};
//业务抽象
class PCMessagerLite : public PCMessagerBase {
public:
virtual void Login(string username, string password) {
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message) {
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image) {
PCMessagerBase::DrawShape();
//........
}
};
class PCMessagerPerfect : public PCMessagerBase {
public:
virtual void Login(string username, string password) {
PCMessagerBase::PlaySound();
//********
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message) {
PCMessagerBase::PlaySound();
//********
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image) {
PCMessagerBase::PlaySound();
//********
PCMessagerBase::DrawShape();
//........
}
};
class MobileMessagerLite : public MobileMessagerBase {
public:
virtual void Login(string username, string password) {
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message) {
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image) {
MobileMessagerBase::DrawShape();
//........
}
};
class MobileMessagerPerfect : public MobileMessagerBase {
public:
virtual void Login(string username, string password) {
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message) {
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image) {
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::DrawShape();
//........
}
};
// 类的数目
// 1 + n + n*m
void Process() {
//编译时装配
Messager *m = new MobileMessagerPerfect();
}
bridge2.cpp
class Messager {
protected:
// 多态指针提升的位置
MessagerImp* messagerImp; //...
public:
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
virtual ~Messager() {
}
};
class MessagerImp {
public:
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual MessagerImp() {
}
};
//平台实现 n
class PCMessagerImp : public MessagerImp {
public:
virtual void PlaySound() {
//**********
}
virtual void DrawShape() {
//**********
}
virtual void WriteText() {
//**********
}
virtual void Connect() {
//**********
}
};
class MobileMessagerImp : public MessagerImp {
public:
virtual void PlaySound() {
//==========
}
virtual void DrawShape() {
//==========
}
virtual void WriteText() {
//==========
}
virtual void Connect() {
//==========
}
};
//业务抽象 m
//类的数目:1+n+m
class MessagerLite : public Messager {
public:
MessagerLite(MessagerImp* mImp) : Messager(mImp) {
}
virtual void Login(string username, string password) {
messagerImp->Connect();
//........
}
virtual void SendMessage(string message) {
messagerImp->WriteText();
//........
}
virtual void SendPicture(Image image) {
messagerImp->DrawShape();
//........
}
};
class MessagerPerfect : public Messager {
public:
MessagerPerfect(MessagerImp* mImp) : Messager(mImp) {
}
virtual void Login(string username, string password) {
messagerImp->PlaySound();
//********
messagerImp->Connect();
//........
}
virtual void SendMessage(string message) {
messagerImp->PlaySound();
//********
messagerImp->WriteText();
//........
}
virtual void SendPicture(Image image) {
messagerImp->PlaySound();
//********
messagerImp->DrawShape();
//........
}
};
void Process() {
//运行时装配
// 运行时候组合
MessagerImp* mImp = new PCMessagerImp();
Messager* m = new MessagerLite(mImp);
}
和装饰模式非常像
但是这里的基类抽象方法很多(分类多,即不应该都集中在一个类中)
将基类按照功能再划分多个抽象类,再用指针指向其他的维度
装饰模式把成员多态指针放在一个中间的子类中,而桥模式则是在分离的基类中声明多态指针
然后该指针指向其他分离的基类
在 MainForm 中需要分解对对象进行分解操作
可能是二进制文件,文本文件,图片文件等等
这时候我们需要在 MainForm 创建对应的对象,然后调用分解函数
非常直接的写一堆类,继承拥有virtual void split()
的基类
FileSplitter1.cpp
非常直观的思路
class ISplitter {
public:
virtual void split() = 0;
virtual ~ISplitter() {
}
};
class BinarySplitter : public ISplitter {
};
class TxtSplitter : public ISplitter {
};
class PictureSplitter : public ISplitter {
};
class VideoSplitter : public ISplitter {
};
MainForm1.cpp
几乎没有复用性
class MainForm : public Form {
public:
void Button1_Click() {
ISplitter* splitter = new BinarySplitter(); // 依赖具体类
splitter->split();
}
};
编写专门的工厂类
再编写具体的工厂子类
在具体操作的 MainForm 中通过构造把子类对象传入
用多态的方式,避免了 MainForm 中的固定
ISplitterFactory.cpp
// 抽象类
class ISplitter {
public:
virtual void split() = 0;
virtual ~ISplitter() {
}
};
// 工厂基类
class SplitterFactory {
public:
// 虚函数,延迟到运行时依赖
virtual ISplitter* CreateSplitter() = 0;
virtual ~SplitterFactory() {
}
};
FileSplitter2.cpp
每个工厂类,对应一个具体类
// 具体类
class BinarySplitter : public ISplitter {
};
class TxtSplitter : public ISplitter {
};
class PictureSplitter : public ISplitter {
};
class VideoSplitter : public ISplitter {
};
// 具体工厂
class BinarySplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new BinarySplitter();
}
};
class TxtSplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new TxtSplitter();
}
};
class PictureSplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new PictureSplitter();
}
};
class VideoSplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new VideoSplitter();
}
};
MainForm2.cpp
把变化的内容从 MainFrom 中转移
使得 MainFrom 内容固定,通过接口或者构造传入变化的部分
// MainForm 没有具体类的依赖
// 只有抽象的类的依赖
// 把依赖具体类转移了
class MainForm : public Form {
SplitterFactory* factory; // 工厂
public:
MainForm(SplitterFactory* factory) {
this->factory = factory;
}
void Button1_Click() {
ISplitter* splitter = factory->CreateSplitter(); // 多态new
splitter->split();
}
};
代码样例中的版本1是非常直观的写法,但是无法让 MainFrom 复用
而工厂方法让 MainFrom 通过接口独立,每个工厂类专门对应一个具体子类
虽然编写的类多了一个类别,但关系都是一一对应的,比较明确
要对 SQL Server 数据库进行操作,需要有 Connection,Command,DataReader等一系列配套的操作
而当需要更换数据库的时候,也是这套操作
如何提高代码复用性
非常直接的写对应数据库的操作
class EmployeeDAO {
public:
vector<EmployeeDO> GetEmployees() {
SqlConnection* connection = new SqlConnection();
connection->ConnectionString("...");
SqlCommand* command = new SqlCommand();
command->CommandText("...");
command->SetConnection(connection);
SqlDataReader* reader = command->ExecuteReader();
while (reader->Read()) {
}
}
};
将每个操作都写成工厂方法的模式
但是有一个缺陷,就是这些操作其实是配套的一套
用户在不知情的情况下可能配套错,因此不能用传统的工厂方法
// 数据库访问有关的基类
class IDBConnection {};
class IDBConnectionFactory {
public:
virtual IDBConnection* CreateDBConnection() = 0;
};
class IDBCommand {};
class IDBCommandFactory {
public:
virtual IDBCommand* CreateDBCommand() = 0;
};
class IDataReader {};
class IDataReaderFactory {
public:
virtual IDataReader* CreateDataReader() = 0;
};
// 支持SQL Server
class SqlConnection : public IDBConnection {};
class SqlConnectionFactory : public IDBConnectionFactory {};
class SqlCommand : public IDBCommand {};
class SqlCommandFactory : public IDBCommandFactory {};
class SqlDataReader : public IDataReader {};
class SqlDataReaderFactory : public IDataReaderFactory {};
// 支持Oracle
class OracleConnection : public IDBConnection {};
class OracleConnectionFactory : public IDBConnectionFactory {};
class OracleCommand : public IDBCommand {};
class OracleCommandFactory : public IDBCommandFactory {};
class OracleDataReader : public IDataReader {};
class OracleDataReaderFactory : public IDataReaderFactory {};
///
// 工厂方法 暴露的问题,这三个指针必须强制一套
class EmployeeDAO {
IDBConnectionFactory* dbConnectionFactory;
IDBCommandFactory* dbCommandFactory;
IDataReaderFactory* dataReaderFactory;
public:
vector<EmployeeDO> GetEmployees() {
IDBConnection* connection = dbConnectionFactory->CreateDBConnection();
connection->ConnectionString("...");
IDBCommand* command = dbCommandFactory->CreateDBCommand();
command->CommandText("...");
command->SetConnection(connection); // 关联性
IDBDataReader* reader = command->ExecuteReader(); // 关联性
while (reader->Read()) {
}
}
};
将三个配套的操作再封装成一个类
避免产生配套错误
// 数据库访问有关的基类
class IDBConnection {};
class IDBCommand {};
class IDataReader {};
// 三个操作,绑定到一起
// 此处是这个模式的稳定部分
class IDBFactory {
public:
virtual IDBConnection* CreateDBConnection() = 0;
virtual IDBCommand* CreateDBCommand() = 0;
virtual IDataReader* CreateDataReader() = 0;
};
// 支持SQL Server
class SqlConnection : public IDBConnection {};
class SqlCommand : public IDBCommand {};
class SqlDataReader : public IDataReader {};
class SqlDBFactory : public IDBFactory {
public:
virtual IDBConnection* CreateDBConnection() = 0;
virtual IDBCommand* CreateDBCommand() = 0;
virtual IDataReader* CreateDataReader() = 0;
};
// 支持Oracle
class OracleConnection : public IDBConnection {};
class OracleCommand : public IDBCommand {};
class OracleDataReader : public IDataReader {};
class OracleDBFactory : public IDBFactory {
public:
virtual IDBConnection* CreateDBConnection() = 0;
virtual IDBCommand* CreateDBCommand() = 0;
virtual IDataReader* CreateDataReader() = 0;
};
class EmployeeDAO {
// 保证是同一个工厂
// 是一个 family
IDBFactory* dbFactory;
public:
vector<EmployeeDO> GetEmployees() {
IDBConnection* connection = dbFactory->CreateDBConnection();
connection->ConnectionString("...");
IDBCommand* command = dbFactory->CreateDBCommand();
command->CommandText("...");
command->SetConnection(connection); // 关联性
IDBDataReader* reader = command->ExecuteReader(); // 关联性
while (reader->Read()) {
}
}
};
抽象工厂和工厂方法非常相像
区别就在于抽象工厂需要一系列对应的对象,而工厂方法就是唯一对象
因此工厂方法也可以理解为抽象工厂的一个特例
情景沿用之前的文件分割器的代码
若这是一个复杂的对象,不能直接通过工厂模式从0创建
就可以使用原型模式直接克隆一个
Prototype.cpp
// 抽象类
class ISplitter {
public:
virtual void split() = 0;
virtual ISplitter* clone() = 0; // 通过克隆自己来创建对象
virtual ~ISplitter() {
}
};
ConcretePrototype.cpp
通过拷贝构造函数,复制一个一样的对象再返回
其他语言没有拷贝构造,可以使用序列化等操作实现
// 具体类
class BinarySplitter : public ISplitter {
public:
virtual ISplitter* clone() {
// 通过 拷贝构造函数
return new BinarySplitter(*this);
}
};
class TxtSplitter : public ISplitter {
public:
virtual ISplitter* clone() {
return new TxtSplitter(*this);
}
};
class PictureSplitter : public ISplitter {
public:
virtual ISplitter* clone() {
return new PictureSplitter(*this);
}
};
class VideoSplitter : public ISplitter {
public:
virtual ISplitter* clone() {
return new VideoSplitter(*this);
}
};
Client.cpp
原型对象不是用来使用的,是专门用来克隆的
class MainForm : public Form {
ISplitter* prototype; // 原型对象
public:
MainForm(ISplitter* prototype) {
this->prototype = prototype;
}
// 此处未考虑内存释放
void Button1_Click() {
// 原型对象不是用来使用的
// 是专门用来克隆的
ISplitter* splitter = prototype->clone(); // 克隆原型
splitter->split();
}
};
原型模式和工厂模式的区别主要在于
原型模式可以直接克隆一个比较复杂的,有过一定变化的状态
而工厂模式是从0开始创建对象
虽然老师在视频中说不常用,但我个人在项目中有遇到过场景的
在一个游戏中有一个造房子的操作
而这个操作的主要流程是固定的
但是不同房子的操作细节不同,如何更好的造房子
builder.cpp
class House {
//....
};
class HouseBuilder {
public:
House* GetResult() {
return pHouse;
}
virtual ~HouseBuilder() {
}
protected:
House* pHouse;
virtual void BuildPart1() = 0;
virtual void BuildPart2() = 0;
virtual void BuildPart3() = 0;
virtual void BuildPart4() = 0;
virtual void BuildPart5() = 0;
};
class StoneHouse : public House {};
class StoneHouseBuilder : public HouseBuilder {
protected:
virtual void BuildPart1() {
// pHouse->Part1 = ...;
}
virtual void BuildPart2() {
}
virtual void BuildPart3() {
}
virtual void BuildPart4() {
}
virtual void BuildPart5() {
}
};
// 构造函数不能调用纯虚函数
// 是静态绑定 (C++基本功)
class HouseDirector {
public:
HouseBuilder* pHouseBuilder;
HouseDirector(HouseBuilder* pHouseBuilder) {
this->pHouseBuilder = pHouseBuilder;
}
House* Construct() {
pHouseBuilder->BuildPart1();
for (int i = 0; i < 4; i++) {
pHouseBuilder->BuildPart2();
}
bool flag = pHouseBuilder->BuildPart3();
if (flag) {
pHouseBuilder->BuildPart4();
}
pHouseBuilder->BuildPart5();
return pHouseBuilder->GetResult();
}
};
此模式和模板方法
非常像,但不同的是在这个过程是含在这个对象的构造的概念中的
因此是属于创建类的设计模式
注意C++中不能在构造函数中用纯虚函数
整体框架
class Singleton {
private:
Singleton();
Singleton(const Singleton& other);
public:
static Singleton* getInstance();
static Singleton* m_instance;
};
Singleton* Singleton::m_instance = nullptr;
// 线程非安全版本
Singleton* Singleton::getInstance() {
// code
return m_instance;
}
线程不安全版本
线程非安全版本
Singleton* Singleton::getInstance() {
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
线程安全版本
线程安全版本,但锁的代价过高
Singleton* Singleton::getInstance() {
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
return m_instance;
}
双检查锁
双检查锁,但由于内存读写reorder不安全
Singleton* Singleton::getInstance() {
if (m_instance == nullptr) {
Lock lock;
if (m_instance == nullptr) {
m_instance = new Singleton();
}
}
return m_instance;
}
C++11 标准库
C++ 11版本之后的跨平台实现
不是所有编译器都支持 volatile
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;
Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire); // 获取内存fence
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release); // 释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
返回引用,一劳永逸
视频中未说的方法,在项目中非常常用
同时要将拷贝构造和=重载废弃
强制让外部使用引用接收
class Singleton {
Singleton(Singleton& obj) = delete;
Singleton& operator=(Singleton& obj) = delete;
};
Singleton& Singleton::getInstance() {
static Singleton obj;
return obj;
}
单件模式,现在通常称为单例模式
是在项目中非常常见的一种模式
也是我个人学习和使用的第一种设计模式
李老师的视频中讲的是返回指针的方式
但是还有一种返回对象的放置直接避免了指针的各种风险
需要根据健值,获取对应的字体
要想办法避免大量的创建
flyweight.cpp
采用一个池
来进行共享
一般来说都规定为只读
class Font {
private:
// unique object key
string key;
// object state
//....
public:
Font(const string& key) {
//...
}
};
class FontFactory {
private:
map<string, Font*> fontPool;
public:
Font* GetFont(const string& key) {
map<string, Font*>::iterator item = fontPool.find(key);
if (item != footPool.end()) {
return fontPool[key];
} else {
Font* font = new Font(key);
fontPool[key] = font;
return font;
}
}
void clear() {
//...
}
};
享元模式其实在一些语言的编译器内部有自己的实现
比如java的字符串常量池,可能实现方式不同,但是思想是一样的
操作系统就是一种典型的门面模式
这是一种今典的设计思想,将递增的变化与外界隔离
仅提供外界稳定的接口,内部怎么改怎么写不暴露给外部,外部不关心
要在客户端获得一个对象
client.cpp
class ISubject {
public:
virtual void process();
};
class RealSubject : public ISubject {
public:
virtual void process() {
//....
}
};
class ClientApp {
ISubject* subject;
public:
ClientApp() {
subject = new RealSubject();
}
void DoTask() {
//...
subject->process();
//....
}
};
proxy.cpp
class ISubject{
public:
virtual void process();
};
//Proxy的设计
class SubjectProxy: public ISubject{
public:
virtual void process(){
//对RealSubject的一种间接访问
//....
}
};
class ClientApp{
ISubject* subject;
public:
ClientApp(){
subject=new SubjectProxy();
}
void DoTask(){
//...
subject->process();
//....
}
};
本质就是增加一个中间层
让前后都觉得复杂的内容,到中间层操作
有一个其他项目,或者老项目的代码想要在新代码中进行复用
为了适配新项目,要设计一个适配器
Adapter.cpp
// 目标接口(新接口)
class ITarget {
public:
virtual void process() = 0;
};
// 遗留接口(老接口)
class IAdaptee {
public:
virtual void foo(int data) = 0;
virtual int bar() = 0;
};
// 遗留类型
class OldClass : public IAdaptee {
//....
};
// 对象适配器 [推荐]
class Adapter : public ITarget { // 继承
protected:
IAdaptee* pAdaptee; // 组合
public:
Adapter(IAdaptee* pAdaptee) {
this->pAdaptee = pAdaptee;
}
// 新接口的样子
// 但是内部是老接口
virtual void process() {
int data = pAdaptee->bar();
pAdaptee->foo(data);
}
};
// 类适配器 [不推荐]
class Adapter : public ITarget,
protected OldClass { // 多继承
};
int main() {
IAdaptee* pAdaptee = new OldClass();
ITarget* pTarget = new Adapter(pAdaptee);
pTarget->process();
}
// 在stl中的应用
// 老接口转新接口
class stack {
deqeue container;
};
class queue {
deqeue container;
};
这个模式在新老项目,多人合作的时候非常重要 (即编写适配的过渡接口)
对象适配器和装饰模式的写法有点类似
也是继承一个接口,内部再声明一个多态指针
但实际作用还是有大区别的
中介者可以不用显示的将各个对象的进行关联,而是通过一层中介隐藏这类关系
很多操作,具有很多状态判断的情形
在添加新状态后,如何减少原代码的修改
state1.cpp
暴力的
if else
状态判断增加状态,就增加else分支
enum NetworkState {
Network_Open,
Network_Close,
Network_Connect,
};
class NetworkProcessor {
NetworkState state;
public:
void Operation1() {
if (state == Network_Open) {
//**********
state = Network_Close;
} else if (state == Network_Close) {
//..........
state = Network_Connect;
} else if (state == Network_Connect) {
//$$$$$$$$$$
state = Network_Open;
}
}
public:
void Operation2() {
if (state == Network_Open) {
//**********
state = Network_Connect;
} else if (state == Network_Close) {
//.....
state = Network_Open;
} else if (state == Network_Connect) {
//$$$$$$$$$$
state = Network_Close;
}
}
public:
void Operation3() {
}
};
state2.cpp
用一个多态指针,不断指向下一个状态
用链表的形式不断走向下一个状态
class NetworkState {
public:
NetworkState* pNext;
virtual void Operation1() = 0;
virtual void Operation2() = 0;
virtual void Operation3() = 0;
virtual ~NetworkState() {
}
};
class OpenState : public NetworkState {
static NetworkState* m_instance;
public:
// 类似单例模式
static NetworkState* getInstance() {
if (m_instance == nullptr) {
m_instance = new OpenState();
}
return m_instance;
}
void Operation1() {
//**********
pNext = CloseState::getInstance();
}
void Operation2() {
//..........
pNext = ConnectState::getInstance();
}
void Operation3() {
//$$$$$$$$$$
pNext = OpenState::getInstance();
}
};
class CloseState : public NetworkState {};
//...
class NetworkProcessor {
NetworkState* pState;
public:
NetworkProcessor(NetworkState* pState) {
this->pState = pState;
}
// 用链表的形式不断往后走
void Operation1() {
//...
pState->Operation1();
pState = pState->pNext;
//...
}
void Operation2() {
//...
pState->Operation2();
pState = pState->pNext;
//...
}
void Operation3() {
//...
pState->Operation3();
pState = pState->pNext;
//...
}
};
这个模式和策略模式非常像,也是会出现一堆if else
但是本模式更加接近有限状态机的模型
注意我们一般说的状态机都是有限的
需要在一个状态经过一段改变后,又回到需要的状态点
memento.cpp
#include
using namespace std;
class Memento {
string state;
//..
public:
Memento(const string& s) : state(s) {
}
string getState() const {
return state;
}
void setState(const string& s) {
state = s;
}
};
class Originator {
string state;
//....
public:
Originator() {
}
Memento createMomento() {
Memento m(state);
return m;
}
void setMomento(const Memento& m) {
state = m.getState();
}
};
int main() {
Originator orginator;
// 其实就是暂存一个对象
// 需要的时候再把对象set进去
// 捕获对象状态,存储到备忘录
Memento mem = orginator.createMomento();
//... 改变orginator状态
// 从备忘录中恢复
orginator.setMomento(mem);
}
同名字一般,就是某个时间点或者状态下的状态点
在需要回溯的时候再set回去
但是实际业务场景一般都非常复杂,不可能只有单个属性,因此一般就直接记录下一个对象
set的时候,再把整个对象传入,set函数再根据需要进行回溯拷贝等
如何在树形结构上,对整体与部分,用相同的操作进行操作
composite.cpp
#include
#include
#include
#include
using namespace std;
class Component {
public:
virtual void process() = 0;
virtual ~Component() {
}
};
// 树节点
class Composite : public Component {
string name;
list<Component*> elements;
public:
Composite(const string& s) : name(s) {
}
void add(Component* element) {
elements.push_back(element);
}
void remove(Component* element) {
elements.remove(element);
}
void process() {
// 1. process current node
// 2. process leaf nodes
for (auto& e : elements) {
e->process(); // 多态调用
}
}
};
// 叶子节点
class Leaf : public Component {
string name;
public:
Leaf(string s) : name(s) {
}
void process() {
// process current node
}
};
void Invoke(Component& c) {
//...
c.process();
//...
}
int main() {
Composite root("root");
Composite treeNode1("treeNode1");
Composite treeNode2("treeNode2");
Composite treeNode3("treeNode3");
Composite treeNode4("treeNode4");
Leaf leaf1("leaf1");
Leaf leaf2("leaf2");
root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode2.add(&leaf1);
root.add(&treeNode3);
treeNode3.add(&treeNode4);
treeNode4.add(&leaf2);
// 老师的源代码写错了 // process(root)
// 访问该树
Invoke(root);
}
这个写法,就是在树形结构上,套用装饰模式
继承一个抽象类,再在类内有抽象类的多态指针
树是一种特殊的图,可见本模式也可以处理一些图的结构,这就是考验开发人员对数据结构的基本功是否扎实了
用类的方式实现迭代器的效果
Iterator.cpp
#include
template <typename T>
class Iterator {
public:
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() const = 0;
virtual T& current() = 0;
};
template <typename T>
class MyCollection {
public:
// 这里应该返回多态指针
Iterator<T>* GetIterator() {
//...
}
};
template <typename T>
class CollectionIterator : public Iterator<T> {
MyCollection<T> mc;
public:
CollectionIterator(const MyCollection<T>& c) : mc(c) {
}
void first() override {
}
void next() override {
}
bool isDone() const override {
}
T& current() override {
}
};
void MyAlgorithm() {
MyCollection<int> mc;
// 老师的源码写错,这里应该用多态指针
Iterator<int>* iter = mc.GetIterator();
for (iter->first(); !iter->isDone(); iter->next()) {
std::cout << iter->current() << std::endl;
}
}
作为C++的学习者,迭代器可以说是非常熟悉的一个东西了
现代C++拥有模板,泛式编程的方式,几乎不再使用类的方式实现迭代器了
但是像java,C#这类语言还是使用的类实现迭代器
其实方式不重要,目的是编写一种可以迭代的效果
个人在项目中还是使用类函数的方式编写迭代器
用链式的方式,不断递归搜索可以处理的子类
解决发送方和接收方有冗余的情况
ChainofResposibility.cpp
#include
#include
using namespace std;
// 请求类型
enum class RequestType {
REQ_HANDLER1,
REQ_HANDLER2,
REQ_HANDLER3
};
// 请求
class Request {
string description;
RequestType reqType;
public:
Request(const string &desc, RequestType type)
: description(desc), reqType(type) {
}
RequestType getReqType() const {
return reqType;
}
const string &getDescription() const {
return description;
}
};
// 链式处理
class ChainHandler {
ChainHandler *nextChain;
void sendReqestToNextHandler(const Request &req) {
if (nextChain != nullptr)
nextChain->handle(req);
}
protected:
virtual bool canHandleRequest(const Request &req) = 0;
virtual void processRequest(const Request &req) = 0;
public:
ChainHandler() {
nextChain = nullptr;
}
void setNextChain(ChainHandler *next) {
nextChain = next;
}
// 不断递归链表识别 req
void handle(const Request &req) {
// 子类的 canHandleRequest
if (canHandleRequest(req)) {
// 子类的 processRequest
processRequest(req);
} else {
// 本类的 sendReqestToNextHandler
sendReqestToNextHandler(req);
/* 等价代码
if (nextChain != nullptr)
nextChain->handle(req);
*/
}
}
};
class Handler1 : public ChainHandler {
protected:
bool canHandleRequest(const Request &req) override {
return req.getReqType() == RequestType::REQ_HANDLER1;
}
void processRequest(const Request &req) override {
cout << "Handler1 is handle Request: " << req.getDescription() << endl;
}
};
class Handler2 : public ChainHandler {
protected:
bool canHandleRequest(const Request &req) override {
return req.getReqType() == RequestType::REQ_HANDLER2;
}
void processRequest(const Request &req) override {
cout << "Handler2 is handle Request: " << req.getDescription() << endl;
}
};
class Handler3 : public ChainHandler {
protected:
bool canHandleRequest(const Request &req) override {
return req.getReqType() == RequestType::REQ_HANDLER3;
}
void processRequest(const Request &req) override {
cout << "Handler3 is handle Request: " << req.getDescription() << endl;
}
};
int main() {
// 构造链表
Handler1 h1;
Handler2 h2;
Handler3 h3;
h1.setNextChain(&h2);
h2.setNextChain(&h3);
Request req("process task ... ", RequestType::REQ_HANDLER3);
// h1为链表头
h1.handle(req);
return 0;
}
其实处理这个请求接收方的情景也可以直接用一堆if else
来处理 (似乎很多模式都可以来处理)
但是职责链的模式,更加明确了各自的职责
可以动态的绑定多个接收方,再进行处理
良好的统一管理同一类的请求
Command.cpp
#include
#include
#include
using namespace std;
class Command {
public:
virtual void execute() = 0;
};
class ConcreteCommand1 : public Command {
string arg;
public:
ConcreteCommand1(const string &a) : arg(a) {
}
void execute() override {
cout << "#1 process..." << arg << endl;
}
};
class ConcreteCommand2 : public Command {
string arg;
public:
ConcreteCommand2(const string &a) : arg(a) {
}
void execute() override {
cout << "#2 process..." << arg << endl;
}
};
class MacroCommand : public Command {
vector<Command *> commands;
public:
void addCommand(Command *c) {
commands.push_back(c);
}
void execute() override {
for (auto &c : commands) {
c->execute();
}
}
};
int main() {
ConcreteCommand1 command1(receiver, "Arg ###");
ConcreteCommand2 command2(receiver, "Arg $$$");
MacroCommand macro;
macro.addCommand(&command1);
macro.addCommand(&command2);
macro.execute();
}
在请求方和接收方之间增加一层
这层将良好的管理双方的各种要求
虽然继承同一个抽象类,但是作用是不一样的,有的是特化,有的是统领
在业务变更下,需要增加新方法,但是在基类中增加,则要在所有子类都增加,也有巨大的负担
这里老师把版本12搞反了
Visitor1.cpp
直接在基类增加方法,子类也要增加
#include
using namespace std;
class Visitor;
class Element {
public:
virtual void Func1() = 0;
virtual void Func2(int data) = 0;
virtual void Func3(int data) = 0;
//...
virtual ~Element() {
}
};
class ElementA : public Element {
public:
void Func1() override {
//...
}
void Func2(int data) override {
//...
}
};
class ElementB : public Element {
public:
void Func1() override {
//***
}
void Func2(int data) override {
//***
}
};
Visitor2.cpp
双重多态的方式实现效果
#include
using namespace std;
class Visitor;
class Element {
public:
virtual void accept(Visitor& visitor) = 0; // 第一次多态辨析
virtual ~Element() {
}
};
class ElementA : public Element {
public:
void accept(Visitor& visitor) override {
visitor.visitElementA(*this);
}
};
class ElementB : public Element {
public:
void accept(Visitor& visitor) override {
visitor.visitElementB(*this); // 第二次多态辨析
}
};
class Visitor {
public:
virtual void visitElementA(ElementA& element) = 0;
virtual void visitElementB(ElementB& element) = 0;
virtual ~Visitor() {
}
};
//==================================
// 扩展1
class Visitor1 : public Visitor {
public:
void visitElementA(ElementA& element) override {
cout << "Visitor1 is processing ElementA" << endl;
}
void visitElementB(ElementB& element) override {
cout << "Visitor1 is processing ElementB" << endl;
}
};
// 扩展2
class Visitor2 : public Visitor {
public:
void visitElementA(ElementA& element) override {
cout << "Visitor2 is processing ElementA" << endl;
}
void visitElementB(ElementB& element) override {
cout << "Visitor2 is processing ElementB" << endl;
}
};
int main() {
Visitor2 visitor;
ElementB elementB;
elementB.accept(visitor); // double dispatch
ElementA elementA;
elementA.accept(visitor);
return 0;
}
感觉这个模式有点鸡肋,当然也有这个场景要求的问题,过于苛刻了
需要确定扩展的子类,若要救确定了,或许很多地方就可以直接写死了
设计一个简单数学计算表达式的功能
main.cpp
#include
#include
#include
using namespace std;
class Expression {
public:
virtual int interpreter(map<char, int> var) = 0;
virtual ~Expression() {
}
};
// 变量表达式
class VarExpression : public Expression {
char key;
public:
VarExpression(const char& key) {
this->key = key;
}
int interpreter(map<char, int> var) override {
return var[key];
}
};
// 符号表达式
class SymbolExpression : public Expression {
// 运算符左右两个参数
protected:
Expression* left;
Expression* right;
public:
SymbolExpression(Expression* left, Expression* right)
: left(left), right(right) {
}
};
// 加法运算
class AddExpression : public SymbolExpression {
public:
AddExpression(Expression* left, Expression* right)
: SymbolExpression(left, right) {
}
int interpreter(map<char, int> var) override {
return left->interpreter(var) + right->interpreter(var);
}
};
// 减法运算
class SubExpression : public SymbolExpression {
public:
SubExpression(Expression* left, Expression* right)
: SymbolExpression(left, right) {
}
int interpreter(map<char, int> var) override {
return left->interpreter(var) - right->interpreter(var);
}
};
Expression* analyse(string expStr) {
stack<Expression*> expStack;
Expression* left = nullptr;
Expression* right = nullptr;
for (int i = 0; i < expStr.size(); i++) {
switch (expStr[i]) {
case '+':
// 加法运算
left = expStack.top();
right = new VarExpression(expStr[++i]);
expStack.push(new AddExpression(left, right));
break;
case '-':
// 减法运算
left = expStack.top();
right = new VarExpression(expStr[++i]);
expStack.push(new SubExpression(left, right));
break;
default:
// 变量表达式
expStack.push(new VarExpression(expStr[i]));
}
}
Expression* expression = expStack.top();
return expression;
}
void release(Expression* expression) {
// 释放表达式树的节点内存...
}
int main(int argc, const char* argv[]) {
string expStr = "a+b-c+d-e";
map<char, int> var;
var.insert(make_pair('a', 5));
var.insert(make_pair('b', 2));
var.insert(make_pair('c', 1));
var.insert(make_pair('d', 6));
var.insert(make_pair('e', 10));
Expression* expression = analyse(expStr);
int result = expression->interpreter(var);
cout << result << endl;
release(expression);
return 0;
}
算法题里做了好多这种题
一般来说都是处理简单或者有明确规则的字符串
复杂的就直接掉包吧
23种设计模式每一种都包含了前人的智慧
在实际运用中不要死记,要思考什么是稳定的,什么是变化的,通过不断的重构获得更加优秀的代码
在稳定和变化中寻求一种平衡 是设计模式的核心宗旨
我们的目的不是消除变化,因为变化是不能消除的,只能通过转移变化来提高代码的复用性
每一种设计模式的表现形式必然不一样,即使是同一种模式,不同人写的也可能不一样,但是核心原理和目的是一样的
有时两份不同的代码可能表示的是同一种模式,而极为相似的代码可能表示的是截然不同的模式
有些模式其实极为相似,不同点可能是在实际业务场景或者人的思维角度的不同而导致的不同
我们虽然是软件行业的从业者,但是也可以从其他行业寻求智慧,要不断思考,不断重构,不断学习
743936)] (李建忠 C++) 23种设计模式.assets/image-20221214030212601.png)
设计一个简单数学计算表达式的功能
main.cpp
#include
#include
#include
using namespace std;
class Expression {
public:
virtual int interpreter(map<char, int> var) = 0;
virtual ~Expression() {
}
};
// 变量表达式
class VarExpression : public Expression {
char key;
public:
VarExpression(const char& key) {
this->key = key;
}
int interpreter(map<char, int> var) override {
return var[key];
}
};
// 符号表达式
class SymbolExpression : public Expression {
// 运算符左右两个参数
protected:
Expression* left;
Expression* right;
public:
SymbolExpression(Expression* left, Expression* right)
: left(left), right(right) {
}
};
// 加法运算
class AddExpression : public SymbolExpression {
public:
AddExpression(Expression* left, Expression* right)
: SymbolExpression(left, right) {
}
int interpreter(map<char, int> var) override {
return left->interpreter(var) + right->interpreter(var);
}
};
// 减法运算
class SubExpression : public SymbolExpression {
public:
SubExpression(Expression* left, Expression* right)
: SymbolExpression(left, right) {
}
int interpreter(map<char, int> var) override {
return left->interpreter(var) - right->interpreter(var);
}
};
Expression* analyse(string expStr) {
stack<Expression*> expStack;
Expression* left = nullptr;
Expression* right = nullptr;
for (int i = 0; i < expStr.size(); i++) {
switch (expStr[i]) {
case '+':
// 加法运算
left = expStack.top();
right = new VarExpression(expStr[++i]);
expStack.push(new AddExpression(left, right));
break;
case '-':
// 减法运算
left = expStack.top();
right = new VarExpression(expStr[++i]);
expStack.push(new SubExpression(left, right));
break;
default:
// 变量表达式
expStack.push(new VarExpression(expStr[i]));
}
}
Expression* expression = expStack.top();
return expression;
}
void release(Expression* expression) {
// 释放表达式树的节点内存...
}
int main(int argc, const char* argv[]) {
string expStr = "a+b-c+d-e";
map<char, int> var;
var.insert(make_pair('a', 5));
var.insert(make_pair('b', 2));
var.insert(make_pair('c', 1));
var.insert(make_pair('d', 6));
var.insert(make_pair('e', 10));
Expression* expression = analyse(expStr);
int result = expression->interpreter(var);
cout << result << endl;
release(expression);
return 0;
}
算法题里做了好多这种题
一般来说都是处理简单或者有明确规则的字符串
复杂的就直接掉包吧
23种设计模式每一种都包含了前人的智慧
在实际运用中不要死记,要思考什么是稳定的,什么是变化的,通过不断的重构获得更加优秀的代码
在稳定和变化中寻求一种平衡 是设计模式的核心宗旨
我们的目的不是消除变化,因为变化是不能消除的,只能通过转移变化来提高代码的复用性
每一种设计模式的表现形式必然不一样,即使是同一种模式,不同人写的也可能不一样,但是核心原理和目的是一样的
有时两份不同的代码可能表示的是同一种模式,而极为相似的代码可能表示的是截然不同的模式
有些模式其实极为相似,不同点可能是在实际业务场景或者人的思维角度的不同而导致的不同
我们虽然是软件行业的从业者,但是也可以从其他行业寻求智慧,要不断思考,不断重构,不断学习