目录
为什么使用设计模式
抵御变化复用代码
性能提升
单例模式
单例模式的定义
确保某一个类只有一个实例, 而且自行实例化并向整个系统提供这个实例。
方法一:
static Singleton *getInstance()
{
static Singleton locla_s;
return &locla_s;
}
该代码可能在c++11之前的版本导致多次构造函数的调用,所以只能在较新的编译器上使用。
方法二:
static Singleton *getInstance()
{
pthread_mutex_lock(&mutex);
if (local_instance == nullptr)
{
local_instance = new Singleton();
}
pthread_mutex_unlock(&mutex);
return local_instance;
}
下面这个版本使用了mutex以及静态成员来析构单例。该方案的劣处在于锁导致速度慢,效率低。但是至少是正确的,也能在c++11之前的版本使用。
方法三:
static Singleton *getInstance()
{
if(local_instance == nullptr){
pthread_mutex_lock(&mutex);
if (local_instance == nullptr)
{
local_instance = new Singleton();
}
pthread_mutex_unlock(&mutex);
}
return local_instance;
}
使用双锁检查导致未初始化的内存访问
使用如下的代码来实现已经初始化的对象的直接返回。可以使上述代码性能会大大加快。但是相同的代码在Java下面有很明显的问题,由于CPU乱序执行,可能导致访问到未经初始化的对象的引用。c++也存在相同的问题,可能导致未定义行为导致段错误
假如线程A进入锁内并分配对象的空间,但是由于指令可能乱序,实际上导致local_instance被先指向一块未被分配的内存,然后再在这块内存上进程初始化。但是在指向后,未初始化前,另一线程B可能通过getInstance获取到这个指针。
方法四
在新的标准中,atomic类实现了内存栅栏,使得多个核心访问内存时可控。这利用了c++11的内存访问顺序可控。
#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>
using namespace std;
class Singleton
{
private:
static atomic<Singleton*> instance;
class rememberFree {
public:
rememberFree() {}
~rememberFree() {
Singleton* local_instance =
instance.load(std::memory_order_relaxed);
if (local_instance != nullptr) {
delete local_instance;
}
}
};
static rememberFree remember;
public:
static Singleton *getInstance()
{
Singleton* tmp = instance.load(std::memory_order_relaxed);
atomic_thread_fence(memory_order_acquire);
if (tmp == nullptr) {
static mutex mtx;
lock_guard<mutex> lock(mtx);
tmp = instance.load(memory_order_relaxed);
if (tmp == nullptr)
{
tmp = new Singleton();
atomic_thread_fence(memory_order_release);
instance.store(tmp, memory_order_relaxed);
}
}
return tmp;
}
};
atomic<Singleton*> Singleton::instance;
Singleton::rememberFree Singleton::remember;
int main()
{
Singleton * s2 = Singleton::getInstance();
}
RAII方式同步线程锁
通过C++的这种机制,我们可以很方便地处理C++中的加锁同步机制。把锁对象作为Guard对象的一个成员(m_lock),然后在Guard对象的构造中对m_lock进行加锁:m_lock.acquire(),在Guard对象的析构函数中进行解锁:m_lock.release()
template <class T>
class Guard
{
public :
Guard(const T & lock);
virtual ~Guard();
private:
const T & m_lock;
};
template <class T>
Guard<T>::Guard(const T & lock) :
m_lock(lock)
{
m_lock.acquire();
}
template <class T>
Guard<T>::~Guard()
{
m_lock.release();
}
我们可以在应用程序中这样使用它:
void testFunc(.....)
{
Guard<MutexWrapper> guard(mutex);
...
}
享元模式
运用共享技术有效的支持大量细粒度的对象
#include <string>
#include <map>
using namespace std;
class Font {
private:
string key;
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 != fontPool.end) {
return fontPool[key];
}
else {
Font* font = new Font(key);
fontPool[key] = font;
return font;
}
}
void clear() {
}
};
享元模式的优缺点
享元模式是一个非常简单的模式, 它可以大大减少应用程序创建的对象, 降低程序内存的占用, 增强程序的性能, 但它同时也提高了系统复杂性, 需要分离出外部状态和内部状态, 而且外部状态具有固化特性, 不应该随内部状态改变而改变, 否则导致系统的逻辑混乱。
享元模式使用场景
● 系统中存在大量的相似对象。
● 细粒度的对象都具备较接近的外部状态, 而且内部状态与环境无关, 也就是说对象没
有特定身份。
● 需要缓冲池的场景
对象创建模式组
简单工厂模式
//抽象类
enum SpliterType {
BinaryType = 0,
TxtYype = 1
};
class ISplitter {
public:
virtual void split() = 0;
virtual ~ISplitter() {}
};
//具体类
class BinarySplitter : public ISplitter {
public:
void split() {};
};
class TxtSplitter : public ISplitter {
public:
void split(){}
};
//工厂基类
class SplitterFactory {
public:
static ISplitter* CreateSplitter(SpliterType type)
{
switch (type)
{
case BinaryType:
return new BinarySplitter();
break;
case TxtYype:
return new TxtSplitter();
break;
default:
break;
}
}
};
抽象工厂模式
//数据库访问有关的基类
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;
};
建造者模式
建造者模式的优点:
封装性
建造者独立,易于扩展
便于控制细节风险
建造者模式使用的场景:
相同的方法, 不同的执行顺序, 产生不同的事件结果时, 可以采用建造者模式多个部件或零件, 都可以装配到一个对象中, 但是产生的运行结果又不相同时, 则可以使用该模式。产品类非常复杂, 或者产品类中的调用顺序不同产生了不同的效能, 这个时候使用建造者模式非常合适。
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;
};
class StoneHouse: public House{};
class StoneHouseBuilder: public HouseBuilder{
protected:
virtual void BuildPart1(){}
virtual void BuildPart2(){}
virtual void BuildPart3(){}
};
class HouseDirector{
public:
HouseBuilder* pHouseBuilder;
HouseDirector(HouseBuilder* pHouseBuilder){
this->pHouseBuilder=pHouseBuilder;
}
House* Construct(){
pHouseBuilder->BuildPart1();
pHouseBuilder->BuildPart2();
bool flag=pHouseBuilder->BuildPart3();
return pHouseBuilder->GetResult();
}
};
接口隔离模式组
facade(外观)模式
为系统中一组接口提供一个(稳定)的界面,Facade定义了一个高层接口,这个接口使得子系统更加容易复用。
门面模式的优点:
减少系统的相互依赖
提高了灵活性
提高安全性
门面模式的缺点:
门面模式最大的缺点就是不符合开闭原则, 对修改关闭, 对扩展开放, 看看我们那个门面对象吧, 它可是重中之重, 一旦在系统投产后发现有一个小错误, 你怎么解决? 完全遵从开闭原则, 根本没办法解决。 继承? 覆写? 都顶不上用, 唯一能做的一件事就是修改门面角色的代码, 这个风险相当大, 这就需要大家在设计的时候慎之又慎, 多思考几遍才会有好收获。
门面模式的应用场景:
1. 为一个复杂的模块或子系统提供一个供外界访问的接口
2. 子系统相对独立——外界对子系统的访问只要黑箱操作即可
3. 预防低水平人员带来的风险扩散
class ClassA {
public:
void doSomethingA() {
//业务逻辑
}
};
class ClassB {
public:
void doSomethingB() {
//业务逻辑
}
};
class ClassC {
public:
void doSomethingC() {
//业务逻辑
}
};
class Facade {
//被委托的对象
ClassA* a = new ClassA();
ClassB* b = new ClassB();
ClassC* c = new ClassC();
//提供给外部访问的方法
void methodA() {
a->doSomethingA();
}
void methodB() {
b->doSomethingB();
}
void methodC() {
c->doSomethingC();
}
}
代理者模式
模式定义
为其他对象提供一种代理控制(隔离,使用接口)对这个对象进行访问。
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();
}
};
class ISubject {
public:
virtual void process();
};
class RealSubject : public ISubject {
public:
virtual void process() {
//....
}
};
适配器模式
模式定义
将一类的接口转换成客户希望的另一个接口,Adaptor模式使得原本由于接口不兼容而不能工作的那些类可以一起工作。
适配器模式的优点
适配器模式可以让两个没有任何关系的类在一起运行, 只要适配器这个角色能够搞定他们就成。
增加了类的透明性
提高了类的复用程度
灵活性非常好
适配器模式的使用场景
你有动机修改一个已经投产中的接口时, 适配器模式可能是最适合你的模式。
适配器模式最好在详细设计阶段不要考虑它, 它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题, 没有一个系统分析师会在做详细设计的时候考虑使用适配器模式, 这个模式使用的主要场景是扩展应用中。
//目标接口(新接口)
class ITarget {
public:
virtual void process() = 0;
};
//遗留接口(老接口)
class IAdaptee {
public:
virtual void foo(int data) = 0;
virtual int bar() = 0;
};
//遗留类型
class OldClass : public IAdaptee {
void foo(int data){}
int bar(){}
};
//对象适配器
class Adapter : public ITarget { //继承
protected:
IAdaptee* pAdaptee;//组合
public:
Adapter(IAdaptee* pAdaptee) {
this->pAdaptee = pAdaptee;
}
virtual void process() {
int data = pAdaptee->bar();
pAdaptee->foo(data);
}
};
int main() {
IAdaptee* pAdaptee = new OldClass();
ITarget* pTarget = new Adapter(pAdaptee);
pTarget->process();
}
中介者模式
模式定义
用一个中介对象来封装(封装变化)一系列的对象交互。中介者使各对象不需要显式的相互依赖>运行时依赖),从而使其耦合松散(管理变化),而且可以独立地改变它们之间的交互。
中介者模式的优点
中介者模式的优点就是减少类间的依赖, 把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者, 减少了依赖, 当然同时也降低了类间的耦合。
中介者模式的缺点
中介者模式的缺点就是中介者会膨胀得很大, 而且逻辑复杂, 原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系, 同事类越多, 中介者的逻辑就越复杂。
中介者模式的实际应用
机场调度中心
MVC框架
媒体网关
中介服务
//通用抽象中介者
class Mediator {
//定义同事类
protected:
ConcreteColleague1 m_c1;
ConcreteColleague2 m_c2;
public:
ConcreteColleague1 getC1() {
return m_c1;
}
void setC1(ConcreteColleague1 c1) {
m_c1 = c1;
}
ConcreteColleague2 getC2() {
return m_c2;
}
void setC2(ConcreteColleague2 c2) {
m_c2 = c2;
}
//中介者模式的业务逻辑
virtual void doSomething1() = 0;
virtual void doSomething2() = 0;
};
//通用中介者
class ConcreteMediator : public Mediator {
public:
void doSomething1() {
m_c1.selfMethod1();
m_c2.selfMethod2();
}
void doSomething2() {
m_c1.selfMethod1();
m_c2.selfMethod2();
}
};
//抽象同事类
class Colleague {
protected:
Mediator* mediator;
public:
Colleague(Mediator* _mediator) {
mediator = _mediator;
}
};
//具体同事类
class ConcreteColleague1:public Colleague {
//通过构造函数传递中介者
public:
ConcreteColleague1(Mediator* _mediator):Colleague(_mediator){
}
public:
void selfMethod1() {
//处理自己的业务逻辑
}
//依赖方法 dep-method
void depMethod1() {
mediator->doSomething1();
}
};
class ConcreteColleague2:public Colleague {
public:
ConcreteColleague2(Mediator* _mediator):Colleague(_mediator) {}
public:
void selfMethod2() {
//处理自己的业务逻辑
}
//依赖方法 dep-method
void depMethod2() {
//处理自己的业务逻辑
//自己不能处理的业务逻辑, 委托给中介者处理
mediator->doSomething2();
}
};
组件协作模式
模板模式
模式定义
定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。 Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。
//程序库开发人员
class Library {
public:
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; //变化
};
策略模式
模式定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
策略模式的优点
算法可以自由切换
避免使用多重条件判断
扩展性良好
策略模式的缺点
策略数量众多
所有策略算法都需要对外暴露
策略模式的使用场景
多个类只有在算法或行为上稍有不同的场景
算法需要自由切换的场景。
需要屏蔽算法规则的场景。
class Context {};
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 StrategyFactory{
//工厂模式
public:
TaxStrategy* NewStrategy(){}
};
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);
}
};
观察者模式
模式定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新
要点总结
使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达致松耦合。
目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
观察者自己决定是否需要订阅通知,目标对象对此一无所知。
Observer模式是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分。
观察者模式的优点
观察者和被观察者之间是抽象耦合
建立一套触发机制
观察者模式的缺点
观察者模式需要考虑一下开发效率和运行效率问题, 一个被观察者, 多个观察者, 开发和调试就会比较复杂, 而且在Java中消息的通知默认是顺序执行, 一个观察者卡壳, 会影响整体的执行效率。 在这种情况下, 一般考虑采用异步的方式。多级触发时的效率更是让人担忧, 大家在设计时注意考虑。
观察者模式的使用场景
● 关联行为场景。 需要注意的是, 关联行为是可拆分的, 而不是“组合”关系。
● 事件多级触发场景。
● 跨系统的消息交换场景, 如消息队列的处理机制。
观察者模式的扩展:
观察者模式也叫做发布/订阅模型(Publish/Subscribe)
#include <string>
#include <map>
#include <list>
#include <iostream>
using namespace std;
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++;
}
};
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 << ".";}
};
单一职责模式组
装饰器模式
模式定义
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)
要点解析
通过采用组合而非继承的手法, Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
//业务操作
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 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(char data) {
//额外的加密操作...
stream->Write(data);//写文件流
}
};
void Process() {
//运行时装配
FileStream* s1 = new FileStream();
CryptoStream* s2 = new CryptoStream(s1);
}
桥接模式
模式定义
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
要点总结
Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。
桥梁模式的优点
抽象和实现相分离
优秀的扩充能力
实现细节对客户透明
桥梁模式的使用场景:
● 不希望或不适用使用继承的场景
例如继承层次过渡、 无法更细化设计颗粒等场景, 需要考虑使用桥梁模式。
● 接口或抽象类不稳定的场景
明知道接口不稳定还想通过实现或继承来实现业务需求, 那是得不偿失的, 也是比较失败的做法。
● 重用性要求较高的场景
设计的颗粒度越细, 则被重用的可能性就越大, 而采用继承则受父类的限制, 不可能出现太细的颗粒度。
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() {}
};
//平台实现
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() {}
};
class MessagerLite :public Messager {
public:
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:
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 MessagerPerfect(mImp);
}
行动变化模式组
命令模式
模式定义
将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排日志,以及支持可撤销的操作。
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();
}
访问者模式
模式定义
表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。
class Visitor;
class Element
{
public:
virtual void accept(Visitor& visitor) = 0; //第一次多态辨析
virtual ~Element() {}
};
class ElementA : public Element
{
public:
void accept(Visitor &visitor){
visitor.visitElementA(*this);
}
};
class ElementB : public Element
{
public:
void accept(Visitor &visitor){
visitor.visitElementB(*this); //第二次多态辨析
}
};
class Visitor{
public:
virtual void visitElementA(ElementA& element) = 0;
virtual void visitElementB(ElementB& element) = 0;
virtual ~Visitor() {}
};
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;
}
};
状态变化模式组
状态模式
模式定义
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。
状态模式的优点
结构清晰
遵循设计原则
封装性非常好
状态模式的缺点
状态模式既然有优点, 那当然有缺点了。 但只有一个缺点, 子类会太多, 也就是类膨胀。 如果一个事物有很多个状态也不稀奇, 如果完全使用状态模式就会有太多的子类, 不好管理, 这个需要大家在项目中自己衡量。
状态模式的应用场景
● 行为随状态改变而改变的场景
这也是状态模式的根本出发点, 例如权限设计, 人员的状态不同即使执行相同的行为结果也会不同, 在这种情况下需要考虑使用状态模式。
● 条件、 分支判断语句的替代者
在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰, 逻辑混乱, 使用状态模式可以很好地避免这一问题, 它通过扩展子类实现了条件的判断处理。
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 = OpenState::getInstance();
}
void Operation3() {
pNext = OpenState::getInstance();
}
};
class CloseState :public NetworkState {
static NetworkState* m_instance;
public:
static NetworkState* getInstance() {
if (m_instance == nullptr) {
m_instance = new CloseState();
}
return m_instance;
}
void Operation1() {
pNext = CloseState::getInstance();
}
void Operation2() {
pNext = OpenState::getInstance();
}
void Operation3() {
pNext = OpenState::getInstance();
}
};
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;
}
};
备忘录模式
模式定义
在不破坏封装性的前提下,捕获、企对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
备忘录模式的使用场景
● 需要保存和恢复数据的相关状态场景。
● 提供一个可回滚(rollback) 的操作; 比如Word中的CTRL+Z组合键, IE浏览器中的后
退按钮, 文件管理器上的backspace键等。
● 需要监控的副本场景中。 例如要监控一个对象的属性, 但是监控又不应该作为系统的
主业务来调用, 它只是边缘应用, 即使出现监控不准、 错误报警也影响不大, 因此一般的做法是备份一个主线程中的对象, 然后由分析程序来分析。
● 数据库连接的事务管理就是用的备忘录模式, 想想看, 如果你要实现一个JDBC驱
动, 你怎么来实现事务? 还不是用备忘录模式嘛
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;
Memento mem = orginator.createMomento();
//从备忘录中恢复
orginator.setMomento(mem);
}
解释器模式
模式定义
给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
解释器模式的优点
解释器是一个简单语法分析工具, 它最显著的优点就是扩展性, 修改语法规则只要修改相应的非终结符表达式就可以了, 若扩展语法, 则只要增加非终结符类就可以了。
解释器模式的缺点
● 解释器模式会引起类膨胀
● 解释器模式采用递归调用方法
● 效率问题
解释器模式的使用场景
● 重复发生的问题可以使用解释器模式
● 一个简单语法需要解释的场景
数据结构模式组
迭代器模式
迭代器模式(Iterator Pattern) 目前已经是一个没落的模式, 基本上没人会单独写一个迭代器, 除非是产品性质的开发, 其定义如下:
它提供一种方法访问一个容器对象中各个元素, 而又不需暴露该对象的内部细节。
组合模式
将对象组合成树形结构以表示“部分整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。
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() {
for (auto &e : elements)
e->process(); //多态调用
}
};
class Leaf : public Component {
string name;
public:
Leaf(string s) : name(s) {}
void process() {
}
};
void Invoke(Component & c) {
c.process();
}
int main()
{
Composite root("root");
Composite treeNode1("treeNode1");
Composite treeNode2("treeNode2");
Leaf leaf1("left1");
Leaf leaf2("left2");
root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode1.add(&leaf1);
root.add(&treeNode2);
Invoke(root);
}
责任链模式
模式定义
使多个对象都有机会处理逋求,丛而避免境求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
#include <iostream>
#include <string>
using namespace std;
enum class RequestType
{
REQ_HANDLER1,
REQ_HANDLER2,
REQ_HANDLER3
};
class Reqest
{
string description;
RequestType reqType;
public:
Reqest(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 Reqest & req)
{
if (nextChain != nullptr)
nextChain->handle(req);
}
protected:
virtual bool canHandleRequest(const Reqest & req) = 0;
virtual void processRequest(const Reqest & req) = 0;
public:
ChainHandler() { nextChain = nullptr; }
void setNextChain(ChainHandler *next) { nextChain = next; }
void handle(const Reqest & req)
{
if (canHandleRequest(req))
processRequest(req);
else
sendReqestToNextHandler(req);
}
};
class Handler1 : public ChainHandler {
protected:
bool canHandleRequest(const Reqest & req) override
{
return req.getReqType() == RequestType::REQ_HANDLER1;
}
void processRequest(const Reqest & req) override
{
cout << "Handler1 is handle reqest: " << req.getDescription() << endl;
}
};
class Handler2 : public ChainHandler {
protected:
bool canHandleRequest(const Reqest & req) override
{
return req.getReqType() == RequestType::REQ_HANDLER2;
}
void processRequest(const Reqest & req) override
{
cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
}
};
class Handler3: public ChainHandler {
protected:
bool canHandleRequest(const Reqest & req) override
{
return req.getReqType() == RequestType::REQ_HANDLER2;
}
void processRequest(const Reqest & req) override
{
cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
}
};
int main() {
Handler1 h1;
Handler2 h2;
Handler3 h3;
h1.setNextChain(&h2);
h2.setNextChain(&h3);
Reqest req("process task ... ", RequestType::REQ_HANDLER3);
h1.handle(req);
return 0;
}