设计模式--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;
};
posted @ 2021-09-08 17:11  荒唐了年少  阅读(166)  评论(0编辑  收藏  举报