• 创建型设计模式学习笔记


    简述

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

    2. 创建型结构型设计模式

    3. 怎么学习设计模式

      1. 稳定点和变化点,把变化点隔离出来,也就是解耦合(注意不是消除耦合)
      2. 先满足设计原则,慢慢迭代出设计模式
    4. 耦合表示两个子系统(或类)之间的关联程度

    5. 编程在于抽象和分治思维。

    设计原则

    依赖倒置
    • 高层模块不应该依赖于底层模块,两者都应该依赖抽象
    • 抽象不应该依赖具体实现,具体实现应该依赖于抽象。(这话是真抽象~~)

    在这里插入图片描述

    开放封闭
    • 一个类应该对扩展(组合与继承开放),对修改关闭。

    在这里插入图片描述

    在这里插入图片描述

    面向接口
    • 不将变量类型声明为某个特定的具体类,而是声明为某个接口
    • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
    • 减少系统中各部分的依赖关系,从而实现高内聚、松耦合的类型设计方案。
    封装变化点
    • 稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离。
    单一职责
    • 一个类应该仅有一个引起它变化的原因。
    里氏替换
    • 子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责
    接口隔离
    • 不应该强迫客户依赖于它们不用的方法;
    • 一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责
    组合优于继承
    • 继承耦合度高,组合耦合度低

    模板方法

    定义:

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

    例子

    某个品牌动物园,有一套固定的表演流程(稳定点),但是其中有若干个表演子流程(变化点)可创新替换,以尝试迭代更新表演流程;

    有缺陷的代码:
    #if 0
    class ZooShow {
    public:
        void Show0() {
            cout << "show0" << endl;
        }
        void Show2() {
            cout << "show2" << endl;
        }
    };
    
    class ZooShowEx {
    public:
        void Show1() {
            cout << "show1" << endl;
        }
        void Show3() {
            cout << "show3" << endl;
        }
    };
    
    // 不满足单一职责 , 开放扩展封闭修改 原则
    // 动物园固定流程,迭代创新
    // 稳定和变化   一定的方向上变化
    #else if 2
    class ZooShow {
    public:
        ZooShow(int type = 1) : _type(type) {}
    
    public:
        void Show() {
            if (Show0())
                PlayGame(); // 里氏替换
            Show1();
            Show2();
            Show3();
        }
    
    // 接口隔离 不要让用户去选择它们不需要的接口
    private:
        void PlayGame() {
            cout << "after Show0, then play game" << endl;
        }
    
    private:
        bool Show0() {
            cout << _type << " show0" << endl;
            return true;
        }
    
        void Show1() {
            if (_type == 1) {
                cout << _type << " Show1" << endl;
            } else if (_type == 2) {
                cout << _type << " Show1" << endl;
            } else if (_type == 3) {
    
            }
        }
    
        void Show2() {
            if (_type == 20) {
                
            }
            cout << "base Show2" << endl;
        }
    
        void Show3() {
            if (_type == 1) {
                cout << _type << " Show1" << endl;
            } else if (_type == 2) {
                cout << _type << " Show1" << endl;
            }
        }
    private:
        int _type;
    };
    
    #endif
    
    int main () {
    #if 0
        ZooShow *zs = new ZooShow;
        ZooShowEx *zs1 = new ZooShowEx;
        zs->Show0();
        zs1->Show1();
        zs->Show2();
        zs1->Show3();
    #else if 2
        ZooShow *zs = new ZooShow(1);
        zs->Show();
    #endif
        return 0;
    }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    优化后的代码
    class ZooShow {
    public:
        void Show() {
            if (Show0())
                PlayGame();
            Show1();
            Show2();
            Show3();
        }
        
    private:
        void PlayGame() {
            cout << "after Show0, then play game" << endl;
        }
    
    protected:
        virtual bool Show0(){
            cout << "show0" << endl;
            return true;
        }
        virtual void Show2(){
            cout << "show2" << endl;
        }
        virtual void Show1() {
    
        }
        virtual void Show3() {
    
        }
    };
    //重写父类的方法
    class ZooShowEx1 : public ZooShow {
    protected:
        virtual bool Show0(){
            cout << "show1" << endl;
            return true;
        }
        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 ZooShowEx3;
        // ZooShow *zs1 = new ZooShowEx1;
        // ZooShow *zs2 = new ZooShowEx2;
        zs->Show();
        return 0;
    }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    要点
    • 常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;
    • 反向控制流程的典型应用;
    • 模板方法是最能体现设计模式的精髓,使用频率最高。
    • 父类protected保护子类需要复写的子流程,这样子类的子流程只能父类调用
    本质

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

    注解:
    • 单一职责、里氏替换、接口隔离都是来佐证开闭原则
      里氏替换:开放扩展,继承父类的职责

      单一职责:对修改封闭

      接口隔离:对修改封闭

    观察者模式

    定义:

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

    例子:

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

    即数据中心接收到数据(信号),就要进行处理并发送给所有终端(槽函数)

    满足观察者模式的demo
    class IDisplay {
    public:
        virtual void Show(float temperature) = 0;
        virtual ~IDisplay() {}
    };
    
    class DisplayA : public IDisplay {
    public:
        virtual void Show(float temperature);
    private:
        void jianyi();
    };
    
    class DisplayB : public IDisplay{
    public:
        virtual void Show(float temperature);
    };
    
    class DisplayC : public IDisplay{
    public:
        virtual void Show(float temperature);
    };
    
    class WeatherData {
    };
    
    class DataCenter {
    public:
        void Attach(IDisplay * ob);
        void Detach(IDisplay * ob);
        void Notify() {
            float temper = CalcTemperature();
            for (auto iter = obs.begin(); iter != obs.end(); iter++) {
                (*iter)->Show(temper);
            }
        }
    
    // 接口隔离
    private:
        virtual WeatherData * GetWeatherData();
    
        virtual float CalcTemperature() {
            WeatherData * data = GetWeatherData();
            // ...
            float temper/* = */;
            return temper;
        }
        //存放订阅者(观察者)
        std::vector<IDisplay*> obs;
    };
    
    int main() {
        DataCenter *center = new DataCenter;
        IDisplay *da = new DisplayA();
        IDisplay *db = new DisplayB();
        IDisplay *dc = new DisplayC();
        //"订阅"
        center->Attach(da);
        center->Attach(db);
        center->Attach(dc);
    
        //"发布"
        center->Notify();
        
        //-----
        center->Detach(db);
        center->Notify();
        return 0;
    }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    要点
    • 观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合
    • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了。
    • 观察者不要依赖通知顺序,目标对象也不知道通知顺序
    • 常用在基于事件的ui框架中,也是MVC的组成部分;
    • 常用在分布式系统中、actor框架中。

    观察者模式还用在:zk、etcd、kafka、redis、分布式锁、
    公平锁(互斥锁、会阻塞导致线程切换)、
    非公平锁(自旋锁)
    队列操作:用自旋锁
    重io操作(磁盘操作、关闭大文件):用互斥锁
    锁粒度: 操作临界资源的时长

    本质:触发联动
    结构图

    在这里插入图片描述

    心得
    • 学了信号槽、以及C语言中信号注册回调函数知识,使得这里观察者模式的理解柳暗花明。

    策略模式

    定义

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

    背景

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

    未使用策略模式的代码
    enum VacationEnum {
    	VAC_Spring,
        VAC_QiXi,
    	VAC_Wuyi,
    	VAC_GuoQing,
        VAC_ShengDan,
    };
    
    class Promotion {
        VacationEnum vac;
    public:
        double CalcPromotion(){
            if (vac == VAC_Spring) {
                // 春节
            }
            else if (vac == VAC_QiXi) {
                // 七夕
            }
            else if (vac == VAC_Wuyi) {
                // 五一
            }
    		else if (vac == VAC_GuoQing) {
    			// 国庆
    		}
            else if (vac == VAC_ShengDan) {
    
            }
         }
        
    };
    
    • 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

    如果一个 只有稳定点 不需要设计模式
    如果 全是变化点 怎么办? 如 c++ 游戏开发 ,则使用脚本语言

    采用策略模式的代码
    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){}
    };
    // 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_Shengdan : public ProStategy {
    public:
        virtual double CalcPro(const Context &ctx){}
    };
    
    class Promotion {
    public:
        Promotion(ProStategy *sss) : s(sss){}
        ~Promotion(){}
        double CalcPromotion(const Context &ctx){
            return s->CalcPro(ctx);
        }
    private:
        ProStategy *s;
    };
    
    int main () {
        Context ctx;
        ProStategy *s = new VAC_QiXi1();
        Promotion *p = new Promotion(s);
        p->CalcPromotion(ctx);
        return 0;
    }
    
    • 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
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    在时间判断中,时间if else 判断是否是某个节日----稳定点

    根据节日,使用if else选择某个促销规格----变化点

    要点
    • 策略模式提供了一系列可重用的算法,从而使得类型在运行时方便地根据需要在各个算法之间切换
    • 策略模式消除了条件判断语句;也就是在解耦合。
    本质
    • 分离算法,选择实现
    结构图

    在这里插入图片描述

  • 相关阅读:
    vue-生命周期钩子函数-axios-$refs-$nextTick的使用
    2022-08-05 学习日记(25th day)集合框架---List
    InnoDB和MyISAM的区别
    【前端】Vue实现个人空间
    FPGA 学习笔记:Vivado 2019.1 工程创建
    Python带你入门安全测试
    【毕业设计】深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv
    .Net Core中无处不在的Async/Await是如何提升性能的?
    chmod文档权限
    JVM及其垃圾回收机制(GC)
  • 原文地址:https://blog.csdn.net/qq_42120843/article/details/126824813