C++——设计模式

1.设计模式简介

为了支持多态性,在vector里放指针类型

因为放的是指针,所以对象我们要用new的形式得到,得到堆对象的指针,而不能是栈对象

2. 面向对象设计原则

多态调用,各负其责

(1) 依赖倒置原则(DIP) 应该让不稳定的依赖于稳定的 -> 提出抽象类

  • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。

  • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。

    第一种:

    MainForm -> (依赖) Line、Rect (MainForm里包含 Line Rect)

    高层的稳定的依赖了底层的不稳定的,高层也变得不稳定了

​ 第二种:

​ MainForm -> Shape ( <- Line Rect) shape里并没有包含Line、Rect这两个对象,而是说,它俩是作为Shape的子类实现的,这个Shape类依旧是稳定的

抽象不应该依赖实现细节,否则它也变得不稳定了

​ 这里是Line 、Rect 不稳定的依赖稳定的,MainForm 和 Shape 是两个稳定的

(2)开放封闭原则(OCP) 用增加一个东西的形式来应对需求的变化,而不是满街的去改

  • 对扩展开放,对更改封闭。
  • 类模块应该是可扩展的,但是不可修改

(3)单一职责原则(SRP) 不要承担太多本不该自己承担的责任

  • 一个类应该仅有一个引起它变化的原因。
  • 变化的方向隐含着类的责任

(4)Liskov 替换原则(LSP)

  • 子类必须能够替换它们的基类(IS-A)。
  • 继承表达类型抽象。

(5)接口隔离原则(ISP)不要把不必要的方法public出去,无节制的用public 就会让客户产生依赖

  • 不应该强迫客户程序依赖它们不用的方法。
  • 接口应该小而完备。

**(6)优先使用对象组合,而不是类继承 **

  • 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。
  • 继承在某种程度上破坏了封装性,子类父类耦合度高。
  • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

**(7)封装变化点 **

  • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。

(8)针对接口编程,而不是针对实现编程 和依赖导致原则是相辅相成的,基本违背一个另一个也违背

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口。
  • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
  • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案。

在这里插入图片描述
上图是针对接口编程

下面两图明显是针对实现编程

在这里插入图片描述

在这里插入图片描述

3. 模版方法 Template Method(晚绑定代替早绑定)

在这里插入图片描述

Refactoring to Patterns 重构的方式得到模式

(1) 静态->动态
(2)早绑定->晚绑定
(3)继承->组合
(4)编译时依赖->运行时依赖
(5)紧耦合->松耦合

注意C++里任何一个基类的虚构函数都要写成虚函数,否则当你真正delete动态绑定创建出的指针对象时,是调不到它本应该调用的那个函数的

在这里插入图片描述

在这里插入图片描述

程序主流程(Run方法)是由Library开发人员写的了

第一种:

class Library{

public:
	void Step1(){
		//...
	}

    void Step3(){
		//...
    }

    void Step5(){
		//...
    }
};

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();

}

第二种:

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 Application : public Library {
protected:
	virtual bool Step2(){
		
    }

    virtual void Step4() {
		
    }
};


int main()
{
	Library* pLib=new Application();
    lib->Run();

	delete pLib;
}

在这里插入图片描述

Library是比你写的早的,一个晚的东西调用早的东西,就是早绑定

面向对象里面:一个早的东西调用晚的东西,就是晚绑定

延迟到子类,支持子类来变化

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

稳定中有变化

设计模式最大的作用就是在变化和稳定中间寻找一个点来管理这些变化,如果一切都是稳定的,那设计模式也没有意义了

在这里插入图片描述

红色是稳定的部分,蓝色是变化的部分

晚绑定不止是可以用虚函数,也可以使用函数指针。当然其实虚函数背后也是函数指针

一般来说,把被Template Method调用的虚方法设置为protected方法,一般不直接供外界调用

4. Strategy 策略模式 (类比代替if-else)

开放封闭原则:类模块尽可能要用扩展的方式去面对未来的变化,而不是找到源代码去更改它

(增加一个法国的枚举属性,并且在下面else if 里更改了加了法国)

(一个类,通过判断里面枚举的值,判断是计算哪国的税法,新的国家出现时要更改代码)

真正的复用是二进制意义的单位复用,不是源代码意义的复用

原来的代码应该是原封不动的,粘贴源代码不叫复用。

我们应该这样:


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); 
        //...
    }
    
};

在这里插入图片描述

红色:稳定的

蓝色:变化的

总结:

  • Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
  • Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
  • 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。

(一般来说,出现if-else就表示我们现在可以用strategy)

​ if-else是结构化分而治之的提现,如果后面还要扩展,肯定是strategy。那如果if-else只有固定的几种,比方说一周有七天,那么它不会改变了,就可以用if-else。

5. Observer观察者模式

加进度条:违背了依赖倒置原则

(我们讲的依赖都是编译时依赖)

我们在设计FileSpliter时里面加入了ProgressBar,这是一种实现细节。抽象不应该依赖于实现细节,也许我们后面会在界面上通过一个label展示出来。

高层不能依赖于低层,二者都应该依赖于抽象

抽象不能依赖于实现细节

那不依赖ProgressBar,我们用谁呢?它的基类ControlBase吗?它自己都没有设置进度条的方法诶。

所以我们要分析,它扮演的是什么角色?它扮演的是一个通知的角色,因此我们不一定只是要用控件去表示呢

所以我们建立一个类去表示一个抽象的通知机制

完成了从具体到抽象的跃迁

C++虽然不推荐使用多继承,但是他推荐一种:一个是主继承,其他是接口/抽象基类

如果有多个观察者呢?

我们定义为List<Iprogress*> m_iprogressList, 定义观察者数组

在构造函数里也没法直接构造了,而是定义一个void addIprogress()、removeIprogress()

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

在这里插入图片描述

Attach 相当于 addIprogress、Detach 相当于 removeIprogress

Notify 相当于 onProgress

可以把这三个方法再放到一个基类里面,让Filespliter再去继承这个基类(也就是图中画的Subject)

下面的ConcreteSubject (主体对象) 是我们的 Filespliter

ConcreteObserver是具体的观察者

你随便添加任何观察者,我这里是松耦合,都可以调用

Subject里有很多observer

要点总结

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

核心:抽象的通知依赖关系

class IProgress{
public:
	virtual void DoProgress(float value)=0;	//具体Observer的更新函数是在这里定义
	virtual ~IProgress(){}
};


class FileSplitter//(ConcreteSubject,这和Subject写一起了)
{
	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(){


		for (int i = 0; i < m_fileNumber; i++){
			//...

			float progressValue = m_fileNumber;
			progressValue = (i + 1) / progressValue;
			onProgress(progressValue);
		}

	}


	void addIProgress(IProgress* iprogress){	//Attach
		m_iprogressList.push_back(iprogress);
	}

	void removeIProgress(IProgress* iprogress){	//Detach
		m_iprogressList.remove(iprogress);
	}


protected:
	virtual void onProgress(float value){		//Notify
		
		List<IProgress*>::iterator itor=m_iprogressList.begin();

		while (itor != m_iprogressList.end() )
			(*itor)->DoProgress(value); 	//具体的DoProgress是在Observer里定义
			itor++;
		}
	}
};

两个Observer

这边随便添加多少观察者,Subject对象是稳定不变的,可以支持任何观察者往里放

// 继承了Form, 以窗口的形式出现
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 << ".";
	}
};

6. 装饰模式 (代码复用太高、不用继承用组合)

代码在被大量的重复,如何重构?

把继承拿下来,作为组合的形式

把编译时的东西变成了运行时的东西,把所有要组合的对象都变成基类的指针,下面去通过基类调用他们的方法

编译时让他们复用,运行时不一样

消除了代码的重复性

注意,虽然你不使用继承了,把他们变成了组合的形式。但是还是要继承他们最基础的那个类,才可以使用虚函数

非常特别地,既继承了基类,还有一个基类的字段成员

从编译时装配变为运行时装配

当所有子类都有同一个字段的时候,要把他们再抽象出一个基类出来,把字段给到他 -> 中间类

原来滥用继承导致复杂的,很多重复的代码:

class Stream{
publicvirtual 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);//写文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
};

把继承改成组合之后:

//业务操作
class Stream{

publicvirtual 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:
    //构造函数
    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(){
//编译时装配
    CryptoFileStream *fs1 = new CryptoFileStream();

    BufferedFileStream *fs2 = new BufferedFileStream();

    CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();

}

void Process(){
//运行时装配
    FileStream* s1=new FileStream();
    //给CryptoStream构造的时候,用FileStream来构造stream指针
    CryptoStream* s2=new CryptoStream(s1);
    //给BufferedStream构造的时候,用FileStream来构造stream指针
    BufferedStream* s3=new BufferedStream(s1);
   	/*
   		给s4构造的时候,用s2来构造,它是一个CryptoStream的对象,在调用它的Read函数时, 除了用了自己的额外Buffer部分,还用到了它的那个stream的Read函数, 它那是CryptoStream类型的Stream, 根据多态调用的是它自己的Read,它自己里本身除了有自己的Read外,还有FileStream的Read
   		
   	*/
    BufferedStream* s4=new BufferedStream(s2);

// 因为都是Stream的子类,所以可以一环套一换,调用函数的时候根据多态一直往下找
// 都是Stream的子类,所以就都能把指针赋给stream指针
}

提取出中间类,把有Stream指针的子类再抽象出一个基类出来,作为中间类DecoratorStream

//业务操作
class Stream{

publicvirtual 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){
        
    }
    //...
};

动机:

  • 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
  • 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

在这里插入图片描述

ConcreteComponent 和 ConcreteDecoratorA/B 都是不稳定的

前者代表 FileStream、NetworkStream、MemoryStream

A代表:CryptoStream

B代表:BufferedStream

要点总结

  • 通过采用组合而非继承的手法, Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。

    (同时继承又组合的,99%都是Decorator)、继承是为了接口、组合是为了实现

  • Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

主体操作和扩展操作应该分开 分支 继承!!!

7. 桥模式(一个类有多个变化维度)

一个类里面放了不同的虚函数,他们是两个方向的,一个是平台实现,一个是业务抽象。

两个不同的变化方向带动的行为多态的实现也应该往不同方向走,不应该把他们放在一个类里。

将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。

原来:

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();
        //........
    }
};

void Process(){
        //编译时装配
        Messager *m =
            new MobileMessagerPerfect();
}


改为:

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

//他们这些子类都有成员MessagerImp* messagerImp;
//所以把这个成员提到了父类去
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 Messager(mImp);
}

在这里插入图片描述

左边是业务抽象、右边是平台实现。

把业务抽象和平台实现从一个类里摘出来成为了两个类,相安无事互不影响。

抽象类里有一个实现类的指针,实现类由多种不稳定的实现继承

抽象类的继承:MessagerLite、MessagerPerfect

实现类的继承:MobileMessagerImp、PCMessagerImp

要点总结

  • Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
  • Bridge模式的应用一般在**“两个非常强的变化维度”**,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。

如果存在四个变化非常强的维度,那么把另外三个再次打包成基类,在用三个指针指向它即可了

8. Factory Method工厂方法

"对象创建"模式

通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。

依赖抽象,不应该依赖于实现细节

ISplitter* splitter = new BinarySplitter(filePath, number);

class MainForm : public Form
{
	TextBox* txtFilePath;
	TextBox* txtFileNumber;
	ProgressBar* progressBar;

public:
	void Button1_Click(){

		ISplitter * splitter=
            new BinarySplitter();//依赖具体类
        
        splitter->split();

	}
};

等号左边是依赖抽象,等号右边是依赖实现细节

在这里插入图片描述

如果是这样,MainForm 依赖 SplitterFactory, SplitterFactory依赖BinarySplitter,间接地,MainForm仍然要依赖BinarySplliter (编译时依赖,仍然绕不过)。

我们怎么改成运行时依赖?

把SplitterFactory里的Create函数改为虚函数,在定义一系列具体工厂去继承

在MainForm里定义 SplitterFactory* factory , 在构造函数里利用外接穿进来的指针初始化它。

class MainForm : public Form
{
    SplitterFactory*  factory;//工厂

public:
    
    MainForm(SplitterFactory*  factory){
        this->factory=factory;
    }
    
	void Button1_Click(){

        
		ISplitter * splitter=
            factory->CreateSplitter(); //多态new
        
        splitter->split();

	}
};

MainForm再也没有依赖具体类了,只是依赖了抽象基类,至于MainForm以外的,不归我管

抽象类、工厂基类、具体类、具体工厂

//抽象类
class ISplitter{
public:
    virtual void split()=0;
    virtual ~ISplitter(){}
};


//工厂基类
class SplitterFactory{
public:
    virtual ISplitter* CreateSplitter()=0;
    virtual ~SplitterFactory(){}
};

//具体类
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();
    }
};

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟**(目的:解耦,手段:虚函数)**到子类。

在这里插入图片描述

Product -> ISplitter

ConcreteProduct -> BinarySp… VedioSp… ImageSp…

Creator -> SplitterFactory

ConcreteCreator -> BinarySplitterFactory、VedioSplitterFactory

要点总结

  • Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  • Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
  • Factory Method模式解决“单个对象”的需求变化。缺点在于要求创建方法/参数相同。

8. 抽象工厂

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。

比如要创建一组connection、command、reader,如果你要创建SQL Server的,就必须这三个都是SQL Server的,不能加一个Oracle的进来,一组必须是保持一致的

如果使用Factory Method 方法:


//数据库访问有关的基类
class IDBConnection{
    
};

class IDBCommand{
    
};


class IDataReader{
    
};

//数据库访问有关抽象工厂的基类
class IDBConnectionFactory{
public:
    virtual IDBConnection* CreateDBConnection()=0;
};

class IDBCommandFactory{
public:
    virtual IDBCommand* CreateDBCommand()=0;
};

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 OracleCommand: public IDBCommand{
    
};

class OracleDataReader: public IDataReader{
    
};



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 IDBFactory{
public:
    virtual IDBConnection* CreateDBConnection()=0;
    virtual IDBCommand* CreateDBCommand()=0;
    virtual IDataReader* CreateDataReader()=0;
    
};

class SqlDBFactory:public IDBFactory{
public:
    virtual IDBConnection* CreateDBConnection()=0;
    virtual IDBCommand* CreateDBCommand()=0;
    virtual IDataReader* CreateDataReader()=0;
 
};

class EmployeeDAO{
    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()){

        }

    }
};

提供一个接口(IDBFactory),让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

在这里插入图片描述

红色表示稳定的,蓝色表示第一种变化,绿色表示第二种变化

蓝色:SQL Server的具体工厂、具体类A1、具体类B1

绿色:Oracle Server的具体工厂、具体类A2、具体类B2

要点总结

  • 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。

  • “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。

  • Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。

    (你在抽象类内又加了新的虚函数,例如创建某DB的Writer、某DB的Listener…)

10. 原型模式 (把业务类和工厂类合并了)比较复杂的时候用

把工厂基类和抽象类合在一起了

通过克隆自己来创建对象

//抽象类
class ISplitter{
public:
    virtual void split()=0;
    virtual ISplitter* clone()=0; //通过克隆自己来创建对象(深克隆)
    virtual ~ISplitter(){}
};

利用拷贝构造函数(需要提前把拷贝构造函数定义好)

//具体类
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);
    }
};

MainForm:原来里面是放Factory,现在改为放一个原型对象

放了原型对象只是帮你克隆用的,真正使用的时候是要创建新的对象的!

class MainForm : public Form
{
    ISplitter*  prototype;//原型对象

public:
    
    MainForm(ISplitter*  prototype){
        this->prototype=prototype;
    }
    
	void Button1_Click(){

		ISplitter * splitter=
            prototype->clone(); //克隆原型
        
        splitter->split();
        
	}
};

在这里插入图片描述

当对象结构比较复杂的时候,工厂很难写,直接写原型更好

用工厂方法是很简单的几步就创建出来了,还是说很复杂

在真正的主类里面定义的是被调用的工厂/原型的指针,在它的函数里通过工厂/原型创建出要被调用的那个对象

11. 构建器 Builder (把对象的表示和对象的构建分离)

也是四种对象创建模式里的一种:

  1. Factory Method
  2. Abstract Factory
  3. Prototype
  4. Builder

动机:

  • 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
  • 如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?

很像之前的Template method

如果在构造函数里调用虚函数, C++默认是静态绑定

(因为子类的构造函数先调用父类的构造函数,如果允许它动态绑定,它先调用子类override的虚函数,会表现为:子类的构造函数还没完成,就先调用了它其他的虚函数,违背对象基本伦理)

class House{
  
public:
    void Init()	//不能写成构造函数
    {
     	this->BuildPart1();
        
        for (int i = 0; i < 4; i++){
            this->BuildPart2();
        }
        
        bool flag=this->BuildPart3();
        
        if(flag){
            this->BuildPart4();
        }
        
        this->BuildPart5();   
    }
    virtual ~House(){}
protected:
	virtual void BuildPart1()=0;
    virtual void BuildPart2()=0;
    virtual void BuildPart3()=0;
    virtual void BuildPart4()=0;
    virtual void BuildPart5()=0;
};

定义:将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。

现在构建过程是确定了的,你现在只要定义子类继承House并且重写,然后动态调用即可。

当对象复杂时,你把Init函数和其他函数搅在一起显得不太好,因此拆分出一个新的类HouseDirector, 它里面放一个HouseBuilder的指针

把对象的表示和对象的构建相分离。House:对象的表示,HouseBuilder:对象的构建
使得同样的构建过程可以创建不同的表示

Builder类里是放的单个的构建方法

把构建的函数的整体流程放到Director类里面

(Director 其实是可以和 Builder放一起的,根据复杂情况而定)

class House{
    //....
};

class HouseBuilder {
public:
    //通过Builder的GetResult得到结果
    House* GetResult(){
        return pHouse;
    }
    virtual ~HouseBuilder(){}
protected:
    //这里塞了一个House的指针(可以去访问到House的内容了)
    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(){
        
    }
    
};


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();
    }
};

在这里插入图片描述

12. 单件模式

“对象性能”模式

保证一个类只存在一个实例,才能确保逻辑正确性以及良好的效率

把构造函数、拷贝构造函数都设置成私有的,让外界不能调用,内界调用。

声明一个静态变量和静态的成员函数

在对对象进行初始化时,如果考虑多线程直接初始化,是不安全的,一个已经进来了,另一个还在判断。

所以使用到了锁。但是对于只是想读取的线程,加锁是没有必要的代价很高

**-> 双检查锁(锁前检查一次,锁后检查一次)**锁前:避免了读取操作时的代价过高 锁后:避免了两个线程前后进来

如果只是锁后检查,代价过高

如果是是锁前检查,是不对的(见双检查锁里的注释)

//线程安全版本,但锁的代价过高
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;	//a行
        //这里为什么还要再判断一次?
        //如果没有这个判断,两个线程一前一后进来a行,又会出现创建两次对象的情况
        //第一个已经创建了,它等的拿到锁之后又创建了一遍
        if (m_instance == nullptr) {
            m_instance = new Singleton();
        }
    }
    return m_instance;
}

reorder不安全:

常见的顺序:

先分配内存、再调用构造器、再把地址分配给m_instance

reorder之后:

先分配内存,再把地址给m_instance,再执行构造器

reorder之后,直接赋值了,另一个线程进来发现不是null,直接返回了,得到这个对象了,根本不能用

//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) {
            // 保证不会reorder
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);//释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

在这里插入图片描述

13. 享元模式 Flyweight

"对象性能"模式

运用共享技术有效地支持大量细粒度的对象

同一种Key,在对象池里只有一个对象

有就返回,没有就添加进去


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(){
        //...
    }
};

要点总结

在这里插入图片描述

14. 门面模式 Fac;ade

“ 接口隔离 ”模式

► 在组件构建过程中,某些接口之间直接的依赖常常会带来很多可题、 甚至根本无法实现。 采用添加一层间接(稳定)接口,来隔离 本来互相紧密关联的接口是一种常见的解决方案。(依赖倒置其实也是间接

典型模式

  • Fac;ade
  • Proxy
  • Adapter
  • Mediator

在这里插入图片描述

Facade 加一层接口 ,外界只和接口打交道 内部也只和接口打交道

无论内部怎么变,外部交互的时候是不变的

把变化和稳定隔离开来

15. Proxy 代理模式

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();
        
        //....
    }
};

上面这样的方式不合适,可能因为某种原因(性能 / 安全控制 / 分布式),压根都拿不到RealSubject

class ISubject{
public:
    virtual void process();
};


//Proxy的设计
class SubjectProxy: public ISubject{
    // 可能这里也拿不到RealSubject的对象
public:
    virtual void process(){
        //对RealSubject的一种间接访问
        //....
    }
};

class ClientApp{
    
    ISubject* subject;
    
public:
    
    ClientApp(){
        subject=new SubjectProxy();
    }
    
    void DoTask(){
        //...
        subject->process();
        
        //....
    }
};

直接调Subject是不合适的,通过替代调用Proxy

在这里插入图片描述

增加间接层,实现一些不为外界所知/累的功能

16. Adapter

将一些现存的对象放在新的环境中使用

在这里插入图片描述

Adapter 类 继承自Target:我遵循你这个类定义的接口规范

Adapter 又还有组合,伸出来一个指针指向了Adaptee

Target -> 目标接口(新接口)

Adaptee -> 遗留接口(老接口)

Adapter 继承了 Target,也就是遵循了它接口的规范

拿一个旧的类塞到Adapter里面,当作面相新接口的实现

类似的,stack 和 queue 是直接把 deque放进去的(也是adapter,把一个老的接口转化成新的接口)

//目标接口(新接口)
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();
}

在这里插入图片描述

我们上面提到的是对象适配器,通过一个组合加继承实现的

类适配器是把组合的那个Adaptee通过protected继承来实现 (多继承)

public继承是公有接口,protected继承是实现继承,并没有继承接口,但是我用你的实现

如果这里只继承IAdaptee接口是不够的,因为它是虚的(纯虚接口)。所以我们需要继承一个明确的OldClass。

类适配器是定死的,没有灵活性了

//类适配器
class Adapter: public ITarget,
               protected OldClass{ //多继承      
}

17. 中介者模式 Mediator

多个对象互相关联,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。

中介对象来封装变化,各个对象不需要显示相互引用(编译时依赖->运行时依赖)

Colleague之间没有相互依赖了,

而是他们去依赖Mediator,Mediator内部也在依赖Colleague

Mediator 和 Colleague 之间是双向依赖的关系

在这里插入图片描述

Fac;ade 解决的是系统外和系统内的隔离

Mediator 解决的是系统内各个对象之间的隔离

在这里插入图片描述

Facade 解决系统内和系统外

Proxy 解决两个可以直接依赖的对象(由于性能、安全性、分布式)必须隔离拆开、A访问B改为A访问Proxy,在Proxy内部再实现B的那些分布式、高性能、安全,完成对B的调用

Adapter 老接口和新接口不Match

18. 状态模式

“ 状态变化 ”模式

把状态相关的操作全部编码到一个状态对象里面了,代替使用枚举状态(那个不符合开闭原则)

虚函数的本质 -> 一个运行时的if-else

和Strategy的好处是异曲同工的 当状态增加的时候

下一个状态由你自己指定

NetworkProcessor是不需要变的,你新增的Wait你自己在内部定义好你的Operation123,你的下一个状态是谁,你自己指定好,至于你的下一个状态是谁我不关心,我只知道你有下一个状态。

允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为

修改前:

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(){

    }
};

修改后:

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;
        //...
    }

};

在这里插入图片描述

只有一个方法的时候,和strategy是很像的

Context 是 Networkprocessor

State 是 NetworkState

ConcreteStateA/B 是 三种不同的状态:OpenState、CloseState、ConnectState

19. 备忘录 Memento

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。

(内存快照)-> 如果想实现高效的,需要序列化等技术

在这里插入图片描述

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状态
    
    //从备忘录中恢复
    orginator.setMomento(memento);

}

Memento模式的核心:信息隐藏,即Originator需要向外界隐藏信息,保持其封装性。但同时有需要将状态保持到外界(Memento)。

深拷贝的内存快照。如果里面有指针,指针里还有指针… 具体在深拷贝的时候就很复杂,就使用序列化代替Memento

其实说白了就是用一种序列化的方式来实现深拷贝

20. 组合模式 (把内部数据结构的访问封装进去)

“ 数据结构 ” 模式

把内部数据结构的访问封装进去

访问的时候:用多态的方式把树形结构的访问放在了树形结构的内部,而不是暴露在外面,外面的调用是一样的。在外面访问的时候,不论是根节点还是树节点还是叶子节点,我的处理是一样的。

将对象组合成树形结构以表示 ”部分-整体“ 的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性 (稳定)

#include <iostream>
#include <list>
#include <string>
#include <algorithm>

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(); //多态调用
        	//循环下来如果e还是树节点,就调用那个树节点的process方法
        	//如果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);
    process(leaf2);
    process(treeNode3);
  
}

在这里插入图片描述

关于Add(Component)、Remove(Component)、GetChild(int)放在父类里还是子类里是有争议的

(叶子节点不可以Add、Remove、Get…)

我们上面实现的时候是通过给Composite加这个方法

但是未来要添加的时候,还得判断是不是Leaf,能不能用Add、… 也是不太好

个人理解放到下面是好的

注意在Composite里面的Operation 除了处理当前的子节点外还要处理它的子节点 (使用多态调用的方法)

在这里插入图片描述

注意这里的一对一和一对多

假如藏起来代码2

    void process(){
        
        //1. process current node
        //例如对当前节点数值进行某种处理
        
        隐藏这段
        /*2. process leaf nodes
        for (auto &e : elements)
            e->process(); //多态调用
        	//循环下来如果e还是树节点,就调用那个树节点的process方法
        	//如果e是叶子节点,就调叶子节点的process
        */
    }

那么在这里,就要实现,一会是一对一,一会是一对多。

要判断当前的Component是Leaf呢还是Composite呢?

如果是Composite,要取出它的子元素继续处理(一对多)

叶子节点,直接处理:一对一

这样还暴露了内部的数据结构

//客户程序
void Invoke(Component & c){
    //...
    c.process();
    //...
}

如果现在这样,永远是一对一的关系,客户代码可以一致地处理对象和对象容器,无需关心是处理的单个对象还是组合对象的容器

21. 迭代器 (过时)

" 数据结构 "模式

C++ STL 里已经有了迭代器

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()){
        cout << iter.current() << endl;
    }
    
}

泛型编程里的迭代器既可以++、–、还能+n

迭代器:通过迭代器来隔离算法和容器

要点总结

  • 迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
  • 迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
  • 迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。

22. 职责链 (过时)

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,知道有一个对象处理它为止。

#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; }
    //设置nextChain也可以用构造器实现
    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_HANDLER3;
    }
    void processRequest(const Reqest & req) override
    {
        cout << "Handler3 is handle reqest: " << req.getDescription() << endl;
    }
};

int main(){
    //建立了一个链表, h1指向h2, h2指向h3
    
    Handler1 h1;
    Handler2 h2;
    Handler3 h3;
    h1.setNextChain(&h2);
    h2.setNextChain(&h3);
    
    Reqest req("process task ... ", RequestType::REQ_HANDLER3);
    h1.handle(req);
    return 0;
}

在这里插入图片描述

逻辑是写在Handler里面,永远按链传播

运行时动态辨析哪一个处理者可以处理这个请求

23. 命令模式

” 行为变化 “ 模式

将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

在这里插入图片描述

将命令/请求 对象化 就可以撤销 等等。

在这里插入图片描述

函数对象,可以重载**()**操作符

#include <iostream>
#include <vector>
#include <string>
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();

在这里插入图片描述

Command 是有接口规范的,比如在Base类里定义了是

virtual void execute(),而且它要有额外开销,有性能损失

函数对象的话,没有接口规范,但是更灵活,性能更高(它利用了模版,是编译时的绑定 因为用了模版 其实也是编译时多态)

函数对象应用更广泛,性能优先

24. 访问器

在已经开发完成后,由于需求的变化,某些类层次结构常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。

Visitor 能预料到未来可能会给整个类层次结构添加新的操作,但是不知道要增加多少,增加什么操作。

double dispatch 二次多态辨析

表示作用于某个对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。

添加新的操作都是扩展,满足开闭原则

//扩展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;
    }
};

整体代码:

#include <iostream>
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;
}

缺点:Visitor稳定的基础是Element的子类都早已经固定

在这里插入图片描述

注意Visitor里有具体的VisitConcreteElementA/B/C的方法,因此,Element的子类也是固定的!

在定义Visitor的时候就定好了有几个方法(Element的子类)

这个子类常常是不能稳定地确定下来的

如果Element再加新的子类,那Visitor的基类也得改变,又打破了开闭原则

在这里插入图片描述

双重分发:

​ (1)调用accept,找子类的accept的实现 (第一次虚函数)

​ (2)进入accept后,又找到对应的visitor子类的实现,去调它的那个函数 (第二次虚函数)

25. 解析器模式

给定一种语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。

在这里插入图片描述

有终端表达式和中间节点表达式(非终端表达式)

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);
    }
    
};

26. 设计模式总结

在这里插入图片描述

直接组合一个对象,带来高度的紧耦合

它们的内存模型都是被组合(继承)的在前面

如果放一个组合的对象,B就没有变化了,只能是它自己,不能是它的子类。如果传子类会产生对象切割

通过指针指向一个多态对象来达到松耦合

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 序言 前言 读者指南 第 1 章 引言 1 1.1 什么是设计模式 2 1.2 Smalltalk MVC 中的设计模式 3 1.3 描述设计模式 4 1.4 设计模式的编目 5 1.5 组织编目 7 1.6 设计模式怎样解决设计问题 8 1.6.1 寻找合适的对象 8 1.6.2 决定对象的粒度 9 1.6.3 指定对象接口 9 1.6.4 描述对象的实现 10 1.6.5 运用复用机制 13 1.6.6 关联运行时刻和编译时刻的结构 15 1.6.7 设计应支持变化 16 1.7 怎样选择设计模式 19 1.8 怎样使用设计模式 20 第 2 章 实例研究:设计一个文档编辑器 22 2.1 设计问题 23 2.2 文档结构 23 2.2.1 递归组合 24 2.2.2 图元 25 2.2.3 组合模式 272.3 格式化 27 2.3.1 封装格式化算法 27 2.3.2 Compositor 和 Composition 27 2.3.3 策略模式 29 2.4 修饰用户界面 29 2.4.1 透明围栏 29 2.4.2 Monoglyph 30 2.4.3 Decorator 模式 32 2.5 支持多种视感标准 32 2.5.1 对象创建的抽象 32 2.5.2 工厂类和产品类 33 2.5.3 Abstract Factory 模式 35 2.6 支持多种窗口系统 35 2.6.1 我们是否可以使用 Abstract Factory 模式 35 2.6.2 封装实现依赖关系 35 2.6.3 Window 和 WindowImp 37 2.6.4 Bridge 模式 40 2.7 用户操作 40 2.7.1 封装一个请求 41 2.7.2 Command 类及其子类 41 2.7.3 撤消和重做 42 2.7.4 命令历史记录 42 2.7.5 Command 模式 44 2.8 拼写检查和断字处理 44 2.8.1 访问分散的信息 44 2.8.2 封装访问和遍历 45 2.8.3 Iterator 类及其子类 46 2.8.4 Iterator 模式 48 2.8.5 遍历和遍历过程中的动作 48 2.8.6 封装分析 48 2.8.7 Visitor 类及其子类 51 2.8.8 Visitor 模式 52 2.9 小结 53 第 3 章 创建型模式 54 3.1 Abstract Factory(抽象工厂)—对象创建型模式 57 3.2 Builder(生成器)—对象创建型模式 633.3 Factory Method(工厂方法)—对象创建型模式 70 3.4 Prototype(原型)—对象创建型模式 87 3.5 Singleton(单件)—对象创建型模式 84 3.6 创建型模式的讨论 89 第 4 章 结构型模式 91 4.1 Adapter(适配器)—类对象结构型模式 92 4.2 Bridge(桥接)—对象结构型模式 100 4.3 Composite(组成)—对象结构型模式 107 4.4 Decorator(装饰)—对象结构型模式 115 4.5 FACADE(外观)—对象结构型模式 121 4.6 Flyweight(享元)—对象结构型模式 128 4.7 Proxy(代理)—对象结构型模式 137 4.8 结构型模式的讨论 144 4.8.1 Adapter 与 Bridge 144 4.8.2 Composite、 Decorator 与 Proxy 145 第 5 章 行为模式 147 5.1 CHAIN OF RESPONSIBIL ITY(职责链)—对象行为型模式 147 5.2 COMMAND(命令)—对象行为型模式 154 5.3 INTERPRETER(解释器)—类行为型模式 162 5.4 ITERATOR(迭代器)—对象行为型模式 171 5.5 MEDIATOR(中介者)—对象行为型模式 181 5.6 MEMENTO(备忘录)—对象行为型模式 188 5.7 OBSERVER(观察者)—对象行为型模式 194 5.8 STATE(状态)—对象行为型模式 201 5.9 STRATEGY(策略)—对象行为型模式 208 5.10 TEMPLATE METHOD(模板方法)—类行为型模式 214 5.11 VISITOR(访问者)—对象行为型模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第 6 章 结论 232 6.1 设计模式将带来什么 2326.2 一套通用的设计词汇 23

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值