创建型设计模式

发布于:2024-12-18 ⋅ 阅读:(5) ⋅ 点赞:(0)

一、设计模式介绍

1.设计模式是什么

设计模式是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案;

2.设计模式怎么来的?

满足设计原则后,慢慢迭代出来的。

3.设计模式解决什么问题?

前提:具体需求既有稳定点又有变化点

期待修改修改少量代码,就可以适应需求的变化;

4.设计模式基础

面向对象思想:封装、继承、多态;

设计原则;

5.设计原则有哪些?

5.1依赖倒置

高层模块不应该依赖底层模块,两者都应该依赖抽象;
抽象不应该依赖具体实现,具体实现应该依赖于抽象;
自动驾驶系统公司是高层,汽车生产厂商为低层,它们不应该互相依赖,一方变动另一方也会跟着变动;而应该抽象一个自动驾驶行业标准,高层和低层都依赖它;这样以来就解耦了两方的变动;自动驾驶系统、汽车生产厂商都是具体实现,它们应该都依赖自动驾驶行业标准(抽象);

5.2.开放封闭

一个类应该对扩展(组合和继承)开放,对修改关闭;

5.3.面向接口

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

5.4.封装变化点

将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离;

5.5.单一职责

一个类应该仅有一个引起它变化的原因;

5.6.里氏替换

子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;

5.7.接口隔离

不应该强迫客户依赖它们不用的方法;
一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;
客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

5.8.组合优于继承

继承耦合度高,组合耦合度低;

6.设计模式如何应用

6.1.明确目的:

在现有设计模式的基础上,扩展代码;
做功能抽象时,如何选择设计模式;

6.2.步骤分析

该设计模式需要解决的问题(稳定点、变化点);

该设计模式的代码结构是什么;

符合哪些设计原则;

如何扩展代码;

联想应用场景;

二、设计模式

1.模板方法

1.1.定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。——《设计模式》

1.2.背景

某个平拍动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代更新表演流程;

1.3.要点

最常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;

反向控制流程的典型应用;

父类protected保护子类需要复写的子流程;这样子类的子流程只能父类来调用;

1.4.本质

通过固定算法骨架来约束子类的行为;

稳定点:算法骨架,基类保持不变;

变化点:子类产生变化,重写父类方法;

1.5.结构图

1.6.代码结构

基类中有骨架流程接口;

所有子流程对子类开放并且是虚函数;

多态使用方式;

1.7.代码

#include <iostream>
using namespace std;

// 开闭
class ZooShow {
public:
    void Show() {
        // 如果子表演流程没有超时的话,进行一个中场游戏环节;如果超时,直接进入下一个子表演流程
        if (Show0())
            PlayGame();
        Show1();
        Show2();
        Show3();
    }
    
private:
    void PlayGame() {
        cout << "after Show0, then play game" << endl;
    }
    bool expired;
    // 对其他用户关闭,但是子类开放的
protected:
    virtual bool Show0() {
        cout << "show0" << endl;
        if (! expired) {
            return true;
        }
        return false;
    }
    virtual void Show2() {
        cout << "show2" << endl;
    }
    virtual void Show1() {

    }
    virtual void Show3() {

    }
};

// 框架
// 模板方法模式
class ZooShowEx10 : public ZooShow {
protected:
    virtual void Show0() {
        if (! expired) {
            return true;
        }
        return false;
    }
}

class ZooShowEx1 : public ZooShow {
protected:
    virtual bool Show0() {
        cout << "ZooShowEx1 show0" << endl;
        if (! expired) { // 里氏替换
            return true;
        }
        return false;
    }
    virtual void Show2(){
        cout << "show3" << endl;
    }
};

class ZooShowEx2 : public ZooShow {
protected:
    virtual void Show1(){
        cout << "show1" << endl;
    }
    virtual void Show2(){
        cout << "show3" << endl;
    }
};

class ZooShowEx3 : public ZooShow {
protected:
    virtual void Show1(){
        cout << "show1" << endl;
    }
    virtual void Show3(){
        cout << "show3" << endl;
    }
    virtual void Show4() {
        //
    }
};
/*
*/
int main () {
    ZooShow *zs = new ZooShowEx10; // 晚绑定还是早绑定
    // ZooShow *zs1 = new ZooShowEx1;
    // ZooShow *zs2 = new ZooShowEx2;
    zs->Show();
    return 0;
}

晚绑定:在编译时绑定;父类指针指向的子类对象会发生类型转换,变成基类对象。

早绑定:多态,运行时绑定。

多态组合:利用多态将原本耦合的基类和子类组合,只余下一个基类指针

1.8.符合那些设计原则

单一职责、开闭、依赖倒置(子类扩展时,需要依赖基类的虚函数实现;使用者只依赖接口)、封装变化点(protected)、接口隔离、最小知道原则。

2.观察者模式

2.1.定义

定义对象间的一种一对多(变化)的依赖关系,一边当一个对象(Subject)的状态发生变化时,所有依赖于它的对象都得到通知并自动更新。——《设计模式》GoF

2.2.背景

气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到两个不同的显示设备(A和B);

2.3.要点

观察者模式使得我们可以独立地改变目标和观察者,从而使二者之间的关系松耦合;

观察者自己决定是否订阅通知,目标对象并不关注谁订阅了;

观察者不要依赖通知顺序,目标对象也不知道通知顺序;

常用在基于事件的ui框架中,也是MVC的组成部分;

常用在分布式系统中,actor框架中;

2.4.本质

触发联动

2.5.结构图

2.6.代码结构

稳定点:“一”对“多”的依赖关系,“一”变化“多跟着变化”;

变化点:“多”增加、减少;

2.7.代码

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
//
class IDisplay {
public:
    virtual void Show(float temperature) = 0;
    virtual ~IDisplay() {}
};

class DisplayA : public IDisplay {
public:
    virtual void Show(float temperature) {
        cout << "DisplayA Show" << endl;
    }
private:
    void jianyi();
};

class DisplayB : public IDisplay{
public:
    virtual void Show(float temperature) {
        cout << "DisplayB Show" << endl;
    }
};

class DisplayC : public IDisplay{
public:
    virtual void Show(float temperature) {
        cout << "DisplayC Show" << endl;
    }
};

class DisplayD : public IDisplay{
public:
    virtual void Show(float temperature) {
        cout << "DisplayC Show" << endl;
    }
};

class WeatherData {
};

// 应对稳定点,抽象
// 应对变化点,扩展(继承和组合)
class DataCenter {
public:
    void Attach(IDisplay * ob) {
        //
    }
    void Detach(IDisplay * ob) {
        //
    }
    void Notify() {
        float temper = CalcTemperature();
        for (auto &ob : obs) {
            ob->Show(temper);
        }
    }

// 接口隔离
private:
    WeatherData * GetWeatherData();

    float CalcTemperature() {
        WeatherData * data = GetWeatherData();
        // ...
        float temper/* = */;
        return temper;
    }
    std::list<IDisplay*> obs;
};

int main() {
    // 单例模式
    DataCenter *center = new DataCenter;
    // ... 某个模块
    IDisplay *da = new DisplayA();
    center->Attach(da);

    // ...
    IDisplay *db = new DisplayB();
    center->Attach(db);
    
    IDisplay *dc = new DisplayC();
    center->Attach(dc);

    center->Notify();
    
    //-----
    center->Detach(db);
    center->Notify();

    center->Notify();
    return 0;
}

3.策略模式

3.1.定义

定义一些列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。——《设计模式》

3.2.背景

某商场节假日固定促销活动,为了加大促销力度,现提升国庆节促销活动规格;

3.3.要点

策略模式提供了一系列可重用的算法,从而可以是的类型在运行时方便地根据需要在各个算法之间进行切换;

策略模式消除了条件判断语句;也就是在解耦合;

3.4.本质

分离算法,选择实现;

3.5.结构图

3.6.代码

class Context {

};

// 稳定点:抽象去解决它
// 变化点:扩展(继承和组合)去解决它
class ProStategy {
public:
    virtual double CalcPro(const Context &ctx) = 0;
    virtual ~ProStategy(); 
};
// cpp
class VAC_Spring : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){
    }
};

class VAC_Spring_v2 : public VAC_Spring {
public:
    virtual double CalcPro(const Context &ctx){
        //....
    }
};

class VAC_worker : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

// cpp
class VAC_QiXi : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1  : public VAC_QiXi {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
class VAC_GuoQing2 : public VAC_GuoQing {
public:
    virtual double CalcPro(const Context &ctx){}
};

class VAC_Shengdan : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

// 设计原则:接口隔离原则
// 组合、继承
// 组合基类指针
// 两种方法:1. 采用具体接口选择算法  2. 依赖注入
class Promotion {
public:
    Promotion(ProStategy *sss = nullptr) : s(sss){}
    ~Promotion(){}
    void Choose(ProStategy *sss) {
        // 条件选择
        if (sss != nullptr) {
            s = sss;
        }
    }
    double CalcPromotion(const Context &ctx){
        if (s != nullptr) {
            return s->CalcPro(ctx);
        }
        return 0.0L;
    }
private:
    ProStategy *s;
};

int main () {
    Context ctx;
    ProStategy *s = new VAC_QiXi1();
    Promotion *p = new Promotion(s);
    p->Choose(new VAC_GuoQing2());
    p->CalcPromotion(ctx);
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到