【笔记】设计模式 | 5种设计模式笔记整理

跟着b站的设计模式教程学的,以下是目前学习了的5种设计模式的笔记整理

设计模式简介

软件设计的现状:由于客户需求等原因需要频繁的变更软件内部的代码。所以能否设计出复用性尽可能高的程序以解决软件设计的复杂性,是评判一个软件设计好不好的关键。

  • 如何解决复杂性?

    1. 分解:分解是人们的一种解决问题的常规思路,分而治之,将大问题–>小问题,复杂问题–>简单问题

    2. 抽象:忽视本质细节而去处理泛化和理想化的对象模型(;利用多态的方法就是一个例子)

面向对象的设计原则

理念:变化复用的天敌,而面向对象设计就是为了抵御变化

面向对象的特点

  • 各司其职(面向对象强调各个类各司其职,由于需求变化导致的新增类型不应该影响原来类型的实现)

  • 对象是什么

    • 语言层面:封装了代码和数据

    • 规格层面:是一系列可被使用的公共接口

    • 概念层面:是某种拥有独立 责任的抽象

原则1:依赖倒置原则(DIP)
  • 高层模块(稳定的)不应该依赖于低层模块(变化的、不稳定的),二者都应该依赖于抽象(稳定的)
  • 抽象(稳定)不应该依赖于实现细节(变化的),实现细节应该依赖于抽象(稳定的)

在这里插入图片描述

(上面这种模式会倒置了,变化会导致mainform不稳定)

在这里插入图片描述

(因为隔离了变化,发生变化是,mainform和shape依然稳定)

原则2:开放封闭原则(OCP)
  • 对拓展开放,对更改封闭:当有新的需求时,应采用增加来满足而不是通过更改来满足
  • 类模板应该时可拓展的,但不可修改
原则3:单一职责原则(SRP)
  • 一个类应该仅有一个引起它变化的原因(各司其职)
  • 变化的方向隐含着“类的责任”,而一个类不应该隐含多个责任
原则4:Liskov替换原则(LSP)
  • 子类必须能够替换它们的基类(IS-A)

  • 继承表达类型抽象

原则5:接口隔离原则(ISP)
  • 不应该强迫客户程序依赖它们不用的方法(区分清楚哪些时public暴露出去的,哪些时自用private的)

  • 接口应该小而完备

原则6:优先使用对象组合,而不是类继承
  • 类继承”白箱复用“;对象组合“黑箱复用”
  • 继承在某种程度上破坏了封装性,子父的耦合度较高
  • 对象组合只要求被组合的对象有良好定义的接口,耦合度低
原则7:封装变化点
  • 使用封装来创建对象之间的分解层,让设计者进行一侧的修改时不会对另一侧产生不良影响
原则8:针对接口编程,而不是针对实现编程(与原则1相辅相成)
  • 不将变量类型声明为某个特定的具体类,而是声明成接口
  • 客户程序无需获知对象的具体类型 ,只需知道对象所具有的接口
  • 减少系统各部分的依赖关系,实现“高内聚,松耦合”的类型设计方案

核心:接口标准化

设计原则–>设计经验

  • 设计习语(Design Idioms):描述与特定编程语言相关的低层模式,技巧,惯用法
  • 设计模式(Design Patterns):描述“类与相互通信的对象之间的组织关系,包括它们的角色,职责 ,协作方式等”(主要解决变化中的复用性问题
  • 架构模式(Architectural Patterns):描述系统中与基本结构组织关系密切的高层模式,包括子系统划分,职责,以及如何组织它们之间关系的规则

重构关键技法

  • 静态(绑定)—》动态(绑定)
  • 早绑定—》晚绑定
  • 继承—》组合
  • 编译时依赖—》运行时依赖
  • 紧耦合—》松耦合

策略模式

定义:将一系列算法封装起来,使它们可以相互替代(可以变化)。从而使得算法可独立于使用它的客户程序稳定的)而变化拓展,子类化)----》“用拓展的方式应对需求的变化”

结构:

在这里插入图片描述

总结

  • Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便得根据需要在各个算法间切换(多态调用
  • 当程序中出现很多条件判断(if else/switch case)的场景,且后续可能还有其它情况需要增加时,想到用Strategy模式(除非确定可能性不再变化)
  • 如果Strategy对象没有实例变量,那么各个上下文可共享同一个Strategy对象,从而节省开销

观察者模式

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

结构

在这里插入图片描述

总结

  • Observer模式使得我们可以独立地改变目标与观察者,使二者做到松耦合
  • 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播
  • 观察者自己决定是否需要订阅通知,目标对象对此一无所知
  • Observer模式是基于时间的UI框架中非常常用的设计模式,也是MVC(Model-View-Controllor)模式的重要组成部分

桥模式

定义:将抽象部分(如:业务功能:简介版,经典版,豪华版…)与实现部分(如:不同平台的实现:PC,PE…)分离,使它们可以独立地变化(拓展时不相互影响)

结构

在这里插入图片描述

总结

  • Bridge模式使用”对象间的组合关系“解耦了抽象和实现之间固有绑定关系,使得抽象和实现可以沿着各自的维度来变化,即“子类化”它们
  • Bridge模式有时类似于多继承方案,但多继承方案往往违背了单一职责原则(即一个类只有一个变化的原因),复用性差。而Bridge模式是更好的解决方法
  • Bridge模式的应用一般在”两个非常强的变化维度“,有时一个类也有多于两个的变化维度,这时可用Bridge的扩展模式(将聚合度较高的一些变化打包(抽象)成一个基类,再用抽象指针)

工厂模式

对象创建”模式:通过”对象创建“模式绕开new,来避免对象创建(new)的过程中导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作

定义:定义一个用于创建对象的接口,让子类决定实例化哪个类。从而使得一个类的实例化延迟(目的:解耦(解除new后面具体类的耦合 );手段:虚函数)到子类

结构

在这里插入图片描述

总结

  • Factory 模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
  • Factory模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好的解决了紧耦合关系
  • Factory模式解决“单个对象”的需求变化。(缺点在于要求创建方法/参数相同)

单件模式

对象性能”模式:面向对象解决了”抽象“的问题,但是不可避免要付出一定的性能代价(如虚函数),虽然通常情况来讲,面向对象的成本大都可以忽略不计(尤其得益于c++语言自身良好的性能),但某些情况,面向对象所带来的成本问题需要谨慎处理

需求:在软件系统中,常有一些特殊的类,需要保证它们在系统只存在一个实例,才能确保它们的逻辑正确性,以及良好的效率。那么,如何绕过常规的构造器,通过提供一种机制来保证一个类只有一个实例?(即限制使用者创建多个类)

定义:保证一个类仅有一个实例,并提供一个该实例的全局访问点

结构

在这里插入图片描述

实例

  1. 线程非安全版本:单线程没问题,但多线程时可能会出现多个对象实例出现的问题

  2. 线程安全版本:加一个“锁”(在一个线程中的锁的实例还未被析构时,其它线程无法进入)可以解决多线程的问题,保证只有一个实例对象存在,但锁的代价过高(”锁“其实只有在创建(写)的时候需要来限制多进程,但是在(读)对象时,是不会出现创建了多个实例对象的问题,所以已经不需要锁了)

  3. 双检测锁:锁前锁后都判断是否已存在一个唯一的实例对象(但由于内存读写reorder(CPU内部处理顺序与我们设想的顺序不同)而不安全)

    • eg:m_instance = new Singleton();这一步我们设想的分解步骤是(分配内存–》构造–》指针指向内存)。而实际上可能会出现reorder的现象,即(分配内存–》指针指向内存–》构造)。而如果此时其它线程进入并判定m_instance不为,就可能直接使用了m_instance,从而出现安全隐患。
  4. 双检测锁防reorder实现

在这里插入图片描述

总结

  • Singleton模式中的实例构造器可以设置为protected以允许子类派生
  • Singleton模式一般不要支持拷贝构造函数和Clone接口,因为可能导致出现多个对象实例
  • Singleton模式的重点和难点就是双检查锁的正确实现

有一些目前还不是很理解,如果有出错的请斧正,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值