• 设计模式--Note2--组件协作类


    Template Method

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

    早绑定与晚绑定

    区分稳定与变化

    要点总结

    1. 非常基础的设计模式

    2. 最简洁的机制(虚函数的多态性)

    3. 提供扩展点(继承+多态)

    4. 反向控制结构(Lib控制App),Lib调用App

      上述实例:

      1. run()是固定的
      2. 运行时,Lib的run()调用App的Step2、Step4
    5. 具体实现中,被Template Method调用的方法可以有也可以没有具体实现,一般推荐设置为protected方法

    示例

    // 结构化
    class Library {
    public:
        Step1();
        Step3();
        Step5();
        ...
    };
    
    class Application {
    public:
        Step2();
        Step4();
    };
    
    int main() {
        Library lib;
        Application app;
        
        lib.Step1();
        
        if (app.Step2()) {
            lib.Step3();
        }
        
        for (...) {
            app.Step4();
        }
        
        lib.Step5();
        ...
    }
    
    // 面向对象
    class Library {
    public: 
        // 稳定中包含变化
        void Run() {
            Step1();
            
            if (Step2()) {  // 支持变化 虚函数多态调用
                Step3();
            }
            
            for (...) {
                Step4();// 支持变化 虚函数多态调用
            }
            
            Step5();
        }
        
        virtual ~Library();
    private:
        Step1();    
        Step3(); 
        Step5();
        
        virtual Step2();
        virtual Step4();
    };
    
    class Application : public Library {
    public:
        Step4();
        Step5();
    };
    
    int main() {
        Library* pLib = new Application;
        pLib->run();// run()并不是虚函数,此处调用基类的run(),但是在run()内部的Step2()、Step4()又是虚函数,调用的时Application的Step2()、Step4()
        delete pLib;
        ...
    }
    

    Strategy

    策略

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

    解决什么问题

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

    如何在运行时根据需求更透明的更改对象的算法?

    将算法与对象本身解耦,从而避免上述问题?

    结构

    要点总结

    1. Strategy及其子类为组件提供了一系列可重用的算法,从而使得类型在运行时方便的根据需要在各个算法之间切换
    2. Strategy提供了判断语句外的另一种选择
    3. 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省开销

    示例:税率问题

    // 结构化
    enum TaxBase { // 变化
        CN_Tax,
        US_Tax,
        DE_Tax,
        FR_Tax  // 增加需求
    };
    
    class SalesOrder {
    public:
        // 稳定
        double CalculateTax() {
            // ...
            
            if (tax == CN_Tax) {
                // ...
            }
            else if (tax == US_Tax) {
                
            }
            else if (tax == DE_Tax) {
                
            }
            else if (tax == FR_Tax) { // 增加 应该对扩展开放,对修改封闭
                
            }
            //...
        }
        
    private:
        TaxBase tax;
    };
    
    // Strategy
    class TaxStrategy {
    public:
        virtual double Calculate(const Context& context)=0;
        virtual ~TaxStrategy(){}// 基类最好都实现一个虚的析构函数
    };
    
    // 变化
    class CNTax : public TaxStratygy {
    public:
        virtual double Calculate(const Context& context) {
            // ...
        }
    };
    
    class USTax : public TaxStratygy {
    public:
        virtual double Calculate(const Context& context) {
            // ...
        }
    };
    
    class DETax : public TaxStratygy {
    public:
        virtual double Calculate(const Context& context) {
            // ...
        }
    };
    
    // 增加
    class FRTax : public TaxStratygy {
    public:
        virtual double Calculate(const Context& context) {
            // ...
        }
    };
    
    // 稳定
    class SalesOrder {
    public:
        SalesOrder(StrategyFactory* strategyFactory) {
            this->strategy = strategyFactory->NewStrategy();
        }
        
        ~SalesOrder() {
            delete this->strategy;
        }
        
        double Calculate() {
            // ...
            Context context();
            
            double val = strategy->Calculate(contex);
            
            // ...
        }
        
    private:
        TaxStrategy* strategy;
    };
    

    Observer

    观察者模式

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

    解决什么问题

    在软件构件过程中,我们需要为某些对象建立一种”通知依赖关系“,一个对象(目标对象)的状态发送改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于亲密,将使软件不能很好地抵御变化。

    使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

    结构

    要点总结

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

    示例:窗口消息通知

    // 最初实现
    class FileSplitter {
    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) {
                
            }
        }
        
    private:
        string m_filePath;
        int m_fileNumber;
    };
    
    class MainForm : public Form {
    public:
        void Button1_Click() {
            string filePath = txtFilePath->getText();
            int number = atoi(txtFileNumber->getText().c_str());
            
            FileSplitter splitter(filePath, number);
            
            splitter.split();
        }
    
    private:
        TextBox* txtFilePath;
        TextBox* txtFileNumber;
    };
    
    // 增加一个通知功能 显示文件切分进度
    class FileSplitter {
    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) {
                // ...
                
                if (m_progressBar != nullptr) {
                    float progressValue = m_fileNumber;
                    m_progressBar->setValur((i + 1) / progressValue);
                }
                
            }
        }
        
    private:
        string m_filePath;
        int m_fileNumber;
        
        ProgressBar* m_progressBar;// 通知的方式是变化的 当通知是稳定的 稳定不应该依赖变化,而应该依赖抽象
    };
    
    class MainForm : public Form {
    public:
        void Button1_Click() {
            string filePath = txtFilePath->getText();
            int number = atoi(txtFileNumber->getText().c_str());
            
            FileSplitter splitter(filePath, number, progressBar);
            
            splitter.split();
        }
    
    private:
        TextBox* txtFilePath;
        TextBox* txtFileNumber;
        
        ProgressBar* progressBar;
    };
    
    // 通知时,除了ProgressBar外,可能会使用其他方式
    // 间ProgressBar与FileSplitter解耦合
    class IProgress {
    public:
        virtual void DoProgress(float value) = 0;
        virtual ~IProgress() {}
    };
    
    class ProgressBar {
    public:
        void setValue(float value) {
            // ...
        }
    }
    
    class FileSplitter {
    public:
        FileSplitter(const string& filePath, int fileNumber, IProgress* iprogress) :
            m_filePath(filePath), 
        	m_fileNumber(fileNumber), 
            m_progressBar(iprogress) {
                
        }
        
        void split() {
            // 1.读取文件
            
            // 2.分批向小文件写入
            for (int i = 0; i < m_fileNumber; ++i) {
                // ...      
                float progressValue = m_fileNumber;
                progressValue = (i + 1) / progressValue;
                onProgress(progressValue);
                
            }
        }
    protected:
        void onProgress(float value) {
            if (m_iprogress != nullptr) {
            	m_iprogress->DoProgress(value);
            }
        }
        
        
    private:
        string m_filePath;
        int m_fileNumber;
        
        //ProgressBar* m_progressBar;// 具体通知控件
        IProgress* m_iprogress;   // 抽象通知
    };
    
    class MainForm : public Form, public IProgress {
    public:
        void Button1_Click() {
            string filePath = txtFilePath->getText();
            int number = atoi(txtFileNumber->getText().c_str());
            
            FileSplitter splitter(filePath, number, this);
            
            splitter.split();
        }
        
        virtual void DoProgress(float value) {
            progressBar->setValue(value);
        }
    
    private:
        TextBox* txtFilePath;
        TextBox* txtFileNumber;
        
        ProgressBar* progressBar;
    };
    
    // 支持出现变化时通知多个对象
    class IProgress {
    public:
        virtual void DoProgress(float value) = 0;
        virtual ~IProgress() {}
    };
    
    class ProgressBar {
    public:
        void setValue(float value) {
            // ...
        }
    }
    
    class ConsoleNotifier : public IProgress {
    public:
        virtual void DoProgress(float value) {
            // ...
        }
    };
    
    class FileSplitter {
    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:
        void onProgress(float value) {
            for (auto iprogress : m_iprogressList) {
            	m_iprogress->DoProgress(value);
            }
        }
        
        
    private:
        string m_filePath;
        int m_fileNumber;
        
        //ProgressBar* m_progressBar;// 具体通知控件
        //IProgress* m_iprogress;   // 抽象通知
        vector<IProgress*> m_iprogressList;  // 支持多个观察者
    };
    
    class MainForm : public Form, public IProgress {
    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();
        }
        
        virtual void DoProgress(float value) {
            progressBar->setValue(value);
        }
    
    private:
        TextBox* txtFilePath;
        TextBox* txtFileNumber;
        
        ProgressBar* progressBar;
    };
    
    转载请注明出处
  • 相关阅读:
    跨域资源共享 CORS 详解
    C# 每月第一天和最后一天
    jexus http to https
    ASP.NET MVC 路由学习
    jexus
    centos7 添加开机启动项
    jexus docker
    HTTP2.0新特性
    jexus配置支持Owin
    Autofac Named命名和Key Service服务
  • 原文地址:https://www.cnblogs.com/lnlin/p/15243626.html
Copyright © 2020-2023  润新知