当前位置:   article > 正文

设计模式_从模式的目的角度

从模式的目的角度

学习视频:https://www.bilibili.com/video/BV1kW411P7KS?p=1
学习资料:https://github.com/liu-jianhao/Cpp-Design-Patterns
本文主要记录了Template Method(模板方法模式)、Strategy(策略模式)、Observer/Event(观察者模式)、Factory Method(工厂方法模式)、Abstract Factory(抽象工厂模式)、Singleton(单件模式、单例设计模式)、Composite(组合模式)。

概述

设计模式

  • 每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用改方案而不必做重复劳动。

解决复杂性

  • 分解:分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题
  • 抽象:处理复杂性的一个通用技术,即抽象,去处理繁华和理想化了的对象模型

面向对象设计原则(8个)

  • 1.依赖倒置原则(DIP)

    • 高层模块(稳定)不应该依赖底层模块(变化),二者都应该依赖于抽象(稳定)
    • 抽象不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)
  • 2.开放封闭原则(OCP)

    • 对扩展开放,对更改封闭
    • 类模块应该是可扩展的,但是不可修改
  • 3.单一职责原则(SRP)

    • 一个类应该仅有一个引起它变化的原因
    • 变化的方向隐含着类的责任
  • 4.Liskov替换原则(LSP)

    • 子类必须能够替换他们的基类(IS-A)
    • 继承表达类型抽象
  • 5.接口隔离原则(ISP)

    • 不应该强迫客户程序依赖它们不用的接口
    • 接口应该小而完备
  • 6.优先使用对象组合,而不是类继承

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

    • 使用封装来创建对象直接按的分界层,让设计者可以在分解曾的一侧进行修改,而不会对另一侧产生不良的影响,从而实现曾创建的松耦合
  • 8.针对接口编程,而不是针对实现编程

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

重构关键技法

  • 静态—>动态
  • 早绑定—>晚绑定
  • 继承—>组合
  • 编译时依赖—>运行时依赖
  • 紧耦合—>松耦合

模式分类(从目的角度)

  • 创建型模式:主要解决对象创建的工作
  • 结构性模式:主要解决需求变化时对对象的结构带来的冲击
  • 行为型模式:主要解决多个类交互责任划分的问题

模式分类(从范围角度)

  • 类模式处理类与子类的静态关系
  • 对象模式处理对象间的动态关系

模式分类(从封装变化角度)

  • 组件协作(1~3)
    • Template Method(模板方法模式)
    • Strategy(策略模式)
    • Observer/Event(观察者模式)
  • 单一职责(4~5)
    • Decorator(装饰模式)
    • Bridge
  • 对象创建(6~9)
    • Factory Method(工厂方法模式)
    • Bridge
    • Abstract Factory(抽象工厂模式)
    • Prototype
    • Builder (少)
  • 对象性能(10~11)
    • Singleton(单件模式、单例设计模式)
    • Flyweight
  • 接口隔离(12~15)
    • Facede
    • Proxy(代理模式)
    • Mediator(少)
    • Adapter(适配器模式)
  • 状态变化(16~17)
    • Memento(少)
    • State
  • 数据结构(18~20)
    • Composite(组合模式)
    • Iterator(少)
    • Chain of Resposibility(少)
  • 行为变化(21~22)
    • Command(少)
    • Visitor(少)
  • 领域问题(23)
    • Interpreter(少)

1. Template Method(模板方法模式)

动机

  • 软件架构过程中,对于某一项任务,它常常有稳定的整体操作机构,但各个子步骤却有很多改变的需求。

定义

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

结构

在这里插入图片描述

要点总结

  • Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性) 为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
  • 除了可以灵活应对子步骤的变化外, “不要调用我,让我来调用你” 的反向控制结构是Template Method的典型应用。
  • 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法。

例子

程序库开发人员和应用程序开发人员

不使用模式

  • 程序库开发人员(Library)

    class Library{
    public:
        void step1(){... }
        void step3(){... }
        void step5(){... }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 应用程序开发人员(Application)

    class Application : public Library{
    public:
        bool step2(){... }
        bool step4(){... }
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • main

    int main(){
        Library lib;
    	Application app;
    
    	lib.Step1();
    	if (app.Step2()){
    		lib.Step3();
    	}
    	for (int i = 0; i < 4; i++){
    		app.Step4();
    	}
    	lib.Step5();
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

使用模板方法模式

  • 程序库开发人员(Library)

    class Library{
    public:
    	void run(){			//稳定 template method
            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 bool step4() = 0; //变化
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 应用程序开发人员(Application)

    class Application : public Library{
    protected:
        virtual bool step2(){... } //子类重写
        virtual bool step4(){... } //子类重写
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • main

    int main(){
        Library *p = new Application();
        p->run();
        delete p;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

2. Strategy(策略模式)

动机

  • 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。

定义

  • 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)

结构

在这里插入图片描述

要点总结

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

例子

销售订单的计算,需要考虑各个国家的汇率

不使用模式

  • 销售订单类SalesOrder

    enum TaxBase {
    	CN_Tax,
    	US_Tax,
    	DE_Tax
    };
    
    class SalesOrder{
        TaxBase tax;
    public:
        double calculateTax(){
            //...        
            if (tax == CH_Tax){
                //CH***********
            }
            else if (tax == US_Tax){
                //US***********
            }
            else if (tax == DE_Tax){
                //DE***********
            }
            //....
         }
        
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

使用策略模式

  • 基类(父类)TaxStrategy

    class TaxStrategy{
    public:
        virtual double calculate(const Context& context){ }
        virtual TaxStrategy(){ }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 派生类(子类)

    //中国
    class CHTax : 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){...}
    };
    //...当有新的国家的合作伙伴时,添加派生类即可
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 销售订单类SalesOrder

    class SalesOrder{
    private:
        TaxStrategy* strategy;
    public:
        SalesOrder(StrategyFactory* stratrgyFactory){
            this->stategy = strategyFactory->NewStrategy();	//此处使用了工厂生成模式,生成类
        }
        ~SalesOrder(){
        	delete this->strategy;
        }
        double calculate_tax(){
            strategy->calculate(context);
            //...
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

3. Observer(观察者模式)

动机

  • 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
  • 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

定义

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

结构

在这里插入图片描述

要点总结

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

例子

文件分割器,界面显示分割加载进度的显示

不使用模式

  • 文件分割类FileSplitter

    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);
    		}
    
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 界面显示(以进度条形式显示)

    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();
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

使用观察者模式

  • 文件分割类FileSplitter

    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++;
    		}
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 界面显示

    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 << ".";
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

4. Decorator(装饰模式)

5. 桥模式

6. Factory Method(工厂方法模式)

动机

  • 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?

定义

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

结构

在这里插入图片描述

要点总结

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

例子

还是文件分割的例子,分割的对象可以是二进制文件、文本文件、视频文件

不使用模式

  • 分割的文件类型

    class ISplitter {
    public:	
    	virtual void split() = 0;
        virtual ~ISplitter(){ }    
    };
    class BinarySplitter : public ISplitter{	//分割二进制文件
        
    };
    class TxtSplitter : public ISplitter{		//分割文本文件
        
    };
    class PictureSplitter : public ISplitter{	//分割视频
        
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 界面显示

    class MainForm : public Form{
    	TextBox* txtFilePath;
    	TextBox* txtFileNumber;
    	ProgressBar* progressBar;
    public:
    	void Button1_Click(){
    		BinarySplitter splitter = new BinarySplitter(); //依赖具体类
    		splitter->split();
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

使用工厂方法模式

  • 抽象的接口

    //抽象类
    class ISplitter {
    public:	
    	virtual void split() = 0;
        virtual ~ISplitter(){ }    
    };
    //工厂基类
    class SplitterFactory{
    public:
        virtual ISpplitter* createSplitter() = 0;
        virtual ~SplitterFactory(){ }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 具体的类和工厂

    //具体的类
    class BinarySplitter : public ISplitter{	//分割二进制文件
        
    };
    class TxtSplitter : public ISplitter{		//分割文本文件
        
    };
    class VideoSplitter : public ISplitter{		//分割视频文件
        
    };
    //具体的工厂
    class BinaryFactory : public SplitterFactory{
    public:
        BinarySplitter* createSplitte(){
            return new BinarySplitter();
        }
    };
    class TxtFactory : public SplitterFactory{
    public:
        TxtSplitter* createSplitte(){
            return new TxtSplitter();
        }
    };
    class VideoFactory : public SplitterFactory{
    public:
        VideoSplitter* createSplitte(){
            return new VideoSplitter();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 界面显示

    class MainForm : public Form{
    	SplitterFactory* m_factory;	//工厂
    public:
        MainForm(SplitterFactory* factory){
            m_factory = factory;
        }
    	void Button1_Click(){
            ISplitter* splitter = m_factory->createSplitter(); //多态new
    		splitter->split();
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

7. Abstract Factory(抽象工厂模式)

动机

  • 在软件系统中,经常面临着“一系列相互依赖的对象工作”;同时,由于需求的变化,往往存在更多系列对象的创建工作。
  • 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合。

定义

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

结构

在这里插入图片描述

要点总结

  • 如果没有应对”多系列对象创建“的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂(即工厂方法模板)即可。
  • ”系列对象“指的是在某一个特定系列的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
  • Abstract Factory模式主要在于应用”新系列“的需求变动。其缺点在与难以应对”新对象“的需求变动。

例子

数据访问层,访问不同数据库,connection、command、dataread

不使用模式

  • EmployeeDAO

    class EmploeeDAO{
    public:
        vector<EmployeeDAO> getEmployees(){
            SqlConnection* connection = new SqlConnection();	//绑定在了Sql上
            connection->ConnectionString = "...";
            SqlCommand* command = new SqlComand();
            command->CommandText = "...";
            SqlDataReader* reader = command ->ExecuteReader();
            while(reader->Read()){
    			
            }
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

使用工厂方法模式

  • EmployeeDAO

    class EmploeeDAO{
        IDBConnectionFactory* dbConnectionFactory;
        IDBCommandFactory* dbCommandFactory;
        IDataReaderFactory* dataReaderFactory;
    public:
        vector<EmployeeDAO> getEmployees(){
            IDBConnection* connection = dbConnectionFactory->CreateDBConnection();
            connection->ConnectionString = "...";
            IDBCommand* command = dbCommandFactory->CreateDBComand();
            command->CommandText = "...";
            command->SetConnection(connection); //关联性
            IDataReader* reader = command ->ExecuteReader();//关联性
            while(reader->Read()){
    			
            }
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 数据库访问有关的基类、子类

    //数据库访问有关的基类
    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 OracleCommand: public IDBCommand{
        //...
    };
    class OracleDataReader: public IDataReader{
        //...
    };
    //...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

使用抽象工厂模式

  • EmployeeDAO

    class EmploeeDAO{
        IDBFactory* dbFactory;
    public:
        vector<EmployeeDAO> getEmployees(){
            IDBConnection* connection = dbFactory->CreateDBConnection();
            connection->ConnectionString = "...";
            IDBCommand* command = dbFactory->CreateDBComand();
            command->CommandText = "...";
            command->SetConnection(connection); //关联性
            IDataReader* reader = command ->ExecuteReader();//关联性
            while(reader->Read()){
    			
            }
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 数据库访问有关的基类、派生类,工厂基类、派生类

    //数据库访问有关的基类
    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{
    
    };
    //...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

8. 原型模式

9. 构建器(少)

10. Singleton(单件模式、单例设计模式)

动机

  • 在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。
  • 如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?
  • 这应该是类设计者的责任,而不是使用者的责任。

定义

  • 保证一个类仅有一个实例,并提供一个该实例的全局访问点。

结构

在这里插入图片描述

要点总结

  • Singleton模式中的实例构造器可以设置为protected以允许子类派生。
  • Singleton模式一般不要支持拷贝构造函数和Clone接口,因为这有可能导致多个对象实例,与Singleton模式的初衷违背。
  • 如何实现多线程环境下安全的Singleton?注意对双检查锁的正确实现。

代码

  • Singleton类

    class Singleton{
    public:
        static Singleton* m_instance;
    private:
        Singleton();
        Singleton(const Singleton& other);
    public:
        static Singleton* getInstance();    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 线程非安全的版本:getInstance()

    Singleton* Singleton::getInstance(){
        if(m_instance == nullptr){
            m_instance = new Singleton();
        }
        return m_instance;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 线程安全,但锁代价过高:getInstance()

    Singleton* Singleton::getInstance(){
        Lock lock;
        if(m_instance == nullptr){
            m_instance = new Singleton();
        }
        return m_instance;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 双检查锁,但由于内存读写reorder不安全:getInstance()

    • 因为m_instance = new Singleton()的开辟内存、调用构造函数、返回地址这三个步骤的顺序或许会乱序,若先返回地址的步骤在调用构造函数步骤之前执行,则会出现问题
    Singleton* Singleton::getInstance(){
        if(m_instance == nullptr){
            Lock lock;
            if(m_instance == nullptr){
                m_instance = new Singleton();
            }        
        }
        return m_instance;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

11. 享元模式

12. 门面模式

13. Proxy(代理模式)

14. Adapter(适配器模式)

15. 中介者(少)

16. 状态模式

17. 备忘录(少)

18. Composite(组合模式)

动机

  • 客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象结构)的变化引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。
  • 如何将”客户代码与复杂的对象容器结构“解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

定义

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

结构

在这里插入图片描述

要点总结

  • Composite模式采用树性结构来实现普遍存在的对象容器,从而将”一对多“的关系转化为”一对一“的关系,使得客户代码可以一致地(复用)处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
  • 客户代码与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能”应对变化“。
  • Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技术来改善效率。

例子

  • Component基类

    class Component{
    public:
        virtual void process() = 0;
        virtual ~Component(){ }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • Composite派生类

    //树节点
    class Composite : public Component{
        string m_name;
        list<Component*> m_elements;
    public:
        Composite(const string &s):m_name(s){ }
        void add(Component* element){
            m_elements.push_back(element);
        }
        void remove(Component* element){
            m_elements.remove(element);
        }
        void process(){
            // 1. process current node
            // 2. process leaf node
            for(auto &e : m_elements)
                e->process();	//多态调用
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • Leaf派生类

    //叶子结点
    class Leaf : public Component{
    	string m_name;
    public:
        Leaf(string s):m_name(s){ }
        void process(){...}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • Invoke函数

    void Invoke(Component &c){
        //...
        c.process();
        //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • main

    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);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

19. 迭代器(少)

20. 职责链(少)

21. 命令模式(少)

22. 访问器(少)

23. 解析器(少)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/897270
推荐阅读
相关标签
  

闽ICP备14008679号