• 设计模式——《软件工程与计算》上课笔记


    设计模式

    1. 设计模式简介

    1.1 为什么需要设计模式?

    • OO软件的设计很难
    • 可复用OO软件的设计尤其难
    • 经验丰富的OO设计师能给出好的设计方案
    • 新手设计师往往把OO设计得没有OO
      • 拿着锤子的傻瓜仍然是傻瓜

    1.2 专家设计师是怎样做设计的?

    • 他们不会总是从头解决问题
    • 他们复用好方案
    • 设计模式是历尽检验,有复用价值,能够让设计方案更加灵活、优雅、可复用的经验解决
    • 复用
      • 机会主义复用:复制、黏贴
      • 传统复用:类库
      • 新型复用:
        • 代码复用:构件
        • 设计知识复用:框架
        • 设计+代码复用:框架
        • 体系结构复用:风格、参考体系结构
        • 需求复用:产品线

    1.3 设计模式

    “设计模式描述了一个在我们周围不断发生的问题,以及该问题的解决方案的核心” —————— Christopher Alexander,《建筑的永恒之道》
    是经过实践反复检验、能解决关键技术难题、有广泛应用前景和能够显著提高软件质量的有效设计经验总结。
    
    • 1
    • 2
    • 一个反复发生的设计结构的抽象
    • 由类和对象组成
      • 依赖dependencies
      • 结构structures
      • 交互interactions,
      • 约定conventions
    • 设计模式4要素
      • 模式名称 Name:需要被设计师熟记
      • 问题 Problem:意图、上下文、适用场景
      • 解决方案 Solution :UML 结构图、抽象代码、职责与协作
      • 效果Consequences:结果和折中

    1.4 23 种设计模式(全23种)

    1.4.1 设计模式的分类

    总体来说设计模式分为三大类:

    1. 创建型模式,共五种:
      1. 工厂方法模式
      2. 抽象工厂模式
      3. 单例模式
      4. 建造者模式
      5. 原型模式
    2. 结构型模式,共七种:
      1. 适配器模式
      2. 装饰器模式
      3. 代理模式
      4. 外观模式
      5. 桥接模式
      6. 组合模式
      7. 享元模式
    3. 行为型模式,共十一种:
      1. 策略模式
      2. 模板方法模式
      3. 观察者模式
      4. 迭代子模式
      5. 责任链模式
      6. 命令模式
      7. 备忘录模式
      8. 状态模式
      9. 访问者模式
      10. 中介者模式
      11. 解释器模式
    序号模式 & 描述包括
    1创建型模式
    这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
    工厂模式(Factory Pattern)
    抽象工厂模式(Abstract Factory Pattern)
    单例模式(Singleton Pattern)
    建造者模式(Builder Pattern)
    原型模式(Prototype Pattern)
    2结构型模式
    这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
    适配器模式(Adapter Pattern)
    桥接模式(Bridge Pattern)
    过滤器模式(Filter、Criteria Pattern)
    组合模式(Composite Pattern)
    装饰器模式(Decorator Pattern)
    外观模式(Facade Pattern)
    享元模式(Flyweight Pattern)
    代理模式(Proxy Pattern)
    3行为型模式
    这些设计模式特别关注对象之间的通信。
    责任链模式(Chain of Responsibility Pattern)
    命令模式(Command Pattern)
    解释器模式(Interpreter Pattern)
    迭代器模式(Iterator Pattern)
    中介者模式(Mediator Pattern)
    备忘录模式(Memento Pattern)
    观察者模式(Observer Pattern)
    状态模式(State Pattern)
    空对象模式(Null Object Pattern)
    策略模式(Strategy Pattern)
    模板模式(Template Pattern)
    访问者模式(Visitor Pattern)
    4J2EE 模式
    这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。
    MVC 模式(MVC Pattern)
    业务代表模式(Business Delegate Pattern)
    组合实体模式(Composite Entity Pattern)
    数据访问对象模式(Data Access Object Pattern)
    前端控制器模式(Front Controller Pattern)
    拦截过滤器模式(Intercepting Filter Pattern)
    服务定位器模式(Service Locator Pattern)
    传输对象模式(Transfer Object Pattern)

    1.5 设计模式的六大原则

    1.5.1 开闭原则(Open Close Principle)

    开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

    1.5.2 里氏代换原则(Liskov Substitution Principle)

    里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

    1.5.3 依赖倒转原则(Dependence Inversion Principle)

    这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

    1.5.4 接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

    1.5.5 迪米特法则,又称最少知道原则(Demeter Principle)

    最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

    1.5.6 合成复用原则(Composite Reuse Principle)

    合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

    2. 信息隐藏与策略模式Strategy

    2.1 信息隐藏

    • 一个模块只有一个秘密(需求、可变更)
    • 如果一个模块有一个可变更行为
      • 将可变更行为独立出来,单独封装出去
      • 按照DIP原则,为该封装出去的单位建立抽象接口

    2.2 策略模式Strategy

    • 对象创建与工厂模式Factory
    • 工厂方法模式Factory Method
    • 抽象工厂模式Abstract Factory
    • 单件Singleton
    • 封装与迭代器模式Iterator

    在这里插入图片描述

    • 很多相关类只有行为不一样。策略模式提供了一个很好的方式来配置某个类,让其拥有多种行为中的一个。

    • 一个类定义了多个行为, 这些行为出现在一个操作的对分支判定之中。策略模式可以消除这些分支选择

    • 需要同一个行为的不同实现(变体)

    • 算法需要用到一些不希望Client知道的数据。策略模式隐藏了数据。

    • 在这里插入图片描述

      • 上下文(Context) :
        • 被配置了具体策略ConcreteStrategy
        • 拥有Strategy对象的一个策略(Strategy) :声明了所支持策略的接口。Context利用这些被ConcreteStrategy定义的接口。
      • 具体策略(ConcreteStrategy) :实现了Strategy声明的接口,给出了具体的实现。
      • 上下文Context和Strategy的相互协作完成整个算法。Context可能会通过提供方法让Strategy访问其数据;甚至将自身的引用传给Strategy,供其访问其数据。Strategy 会在需要的时候访问Context的成员变量。
      • 客户(Client) 、Context、 Strategy 都可能创建ConcreteStrategy的对象,然后传递给Context来灵活配置Strategy接口。
    2.2.1 效果
    • 可修改性和可复用性增强
    • 易理解性降低,更加复杂了
      • 好的方面:消除了多分支判定
      • 坏的方面:逻辑被分散了
    2.2.2 例子

    在这里插入图片描述

    • 最差实现:

      • 在这里插入图片描述
    • 策略模式实现

      • 在这里插入图片描述
    2.2.3 策略模式深入分析
    2.2.3.1 可变更:信息隐藏
    • (M)实现的可修改性

      • 对已有实现的修改
      • 例如:修改现有促销策略
    • (E)实现的可扩展性

      • 对新的实现的扩展
      • 例如:增加一条新的促销策略
    • ©实现的灵活性

      • 对实现的动态配置
      • 例如:通过修改数据或配置更改某商品对应促销策略
    • 可修改性、可扩展性

      • 符合DIP原则的抽象类(继承)机制
        • 通过定义抽象类与继承(实现)它的子类强制性地做到:接口与实现的分离,进而实现上述质量
          • 强制性地使用抽象类起到接口的作用
          • 强制性地使用子类起到实现的作用
    • 灵活性

      • 组合(委托)机制
        • 动态调整所委托的类,实现灵活性
    2.2.4 Strategy体现的设计原则
    • SRP
      • 将变化和需求职责分离
    • 组合代替继承Favorite Composition over Inheritance
      • 实现灵活性
    • OCP,LSP,DIP
      • 将可修改和可扩展都统一为可扩展
    • 可能会违反:信息与行为集中原则
    2.2.5 Singleton 单件模式
    • 实例数量受限(场景1)

    • 只允许一个实例存在

      • 在有些场景中,对于某个类,在内存中只希望有唯一一个对象存在。每次想得到这个类的一个对象的引用的时候,都指向唯一的那个对象。无论创建多少次这个类的对象,事实上都只创建了一个对象。
      • 例子:键盘、银行账户、文件
      • 如果超过1个,会出错误或安全问题
    • 在这里插入图片描述

    • 在这里插入图片描述

    2.2.6 工厂模式Factory

    对象创建复杂问题2 类别差异

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    2.2.6.1 Factory 体现了哪些思想
    • 不要重复! Do not repeat !
    • 封装(信息隐藏):将对象创建抽象为单独职责,并隐藏创建细节
      • 有利于对象创建的变更
      • 特别是限制Conditional Statements
    2.2.6.2 创建差异类型的更复杂场景

    在这里插入图片描述

    在这里插入图片描述

    2.2.6.2.1 工厂方法Factory Method:意图
    • 定义用于创建对象的接口(替换构造方法),但让子类决定要实例化哪个类。让类将实例化推迟到子类。
    • 工厂方法不是简单的工厂!
    • 工厂方法=类似工厂的方法

    例子

    在这里插入图片描述

    在这里插入图片描述

    2.2.6.2.3 对象创建的更复杂问题3
    • 而在软件系统中,经常面临着“多种差异对象”的创建工作。

    • 比如汽车由引擎、轮胎、车身、车门等各部件组成。而每一部件都有很多种。

    • 如何解决该创建问题?

    • 超级工厂?

    • 在这里插入图片描述

    • 而在软件系统中,经常面临着“多种差异对象”的创建工作,由于需求的变化,多种对象的具体实现有时候需要灵活组合。

    • 汽车有很多类型,车的类型可以决定部件的类型

    • 改进超级工厂?

    • 在这里插入图片描述

    2.2.6.3 Abstract Factory:解决方案
    • 抽象工厂(AbstractFactory)声明了创建抽象产品的各个接口。
    • 具体工厂(ConcreteFactory)实现了对具体产品的创建。
    • 抽象产品(AbstractProduct)声明了一种产品的接口。
    • 具体产品(ConcreteProduct)定义了具体工厂中创建出来的具体产品,实现
    • 了抽象产品的接口。
    • 客户(Client)使用抽象工厂和抽象产品的类。使用抽象工厂的方法来创建产品
    • 在这里插入图片描述
    2.2.6.3.1 意图:
    • 抽象工厂模式可以帮助系统独立于如何对产品的创建、构成、表现。
    • 抽象工厂模式可以让系统灵活配置拥有某多个产品族中的某一个
    • 一个产品族的产品应该被一 起使用,抽象工厂模式可以强调这个限制。
    • 如果你想提供一个产品的库,抽象工厂模式可以帮助暴露该库的接口,而不是实现。

    在这里插入图片描述

    2.2.7 迭代器 Iterator

    提供按顺序访问聚合对象的元素而不公开其基础表示形式的方法

    • 访问一个聚合对象的内容而无需暴露它的内部实现
    • 支持对聚合对象的多种遍历
    • 为遍历不同的聚合结构提供一个统一的接口

    单值数据和集合数据

    在这里插入图片描述

    在这里插入图片描述

    1. 针对接口编程和信息隐藏
    2. 它支持聚合遍历中的变化。例如,代码生成可能会按序或预序遍历解析树。
    3. 迭代器使更改遍历变得容易。只需将迭代器实例替换为其他实例即可。
    4. 迭代器简化了聚合接口。迭代器的遍历接口避免了在聚合时需要类似的接口。一个聚合上可以挂起多个遍历。

    在这里插入图片描述

  • 相关阅读:
    leetcode:剑指 Offer 17. 打印从1到最大的n位数(python3解法)
    CefSharp.WinForms ChromiumWebBrowser 阻止打开新的窗口
    JDK安装详细教程
    万字长文带你了解 CloudOps 自动化运维的奥秘,助力云上业务高效稳定运行
    极客天成RDMA分布式存储加速自动驾驶仿真建模
    使用boost::geometry::partition的示例程序(C/C++)
    为什么鸿蒙调用弹窗组件(CommonDialog )却不展示或闪退?
    Python 操作Mysql
    coco格式转yolo格式,标注软件是旷世labelbee
    OPPO手机便签数据搬家到华为mate60Pro手机怎么操作
  • 原文地址:https://blog.csdn.net/qq_46371399/article/details/127805525