创建型设计模式学习笔记

简述

  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;
}
优化后的代码
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;
}
要点
  • 常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;
  • 反向控制流程的典型应用;
  • 模板方法是最能体现设计模式的精髓,使用频率最高。
  • 父类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;
}
要点
  • 观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合
  • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了。
  • 观察者不要依赖通知顺序,目标对象也不知道通知顺序
  • 常用在基于事件的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) {

        }
     }
    
};

如果一个 只有稳定点 不需要设计模式
如果 全是变化点 怎么办? 如 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;
}

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

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

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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值