设计模式学习笔记

  设计模式(Design Pattern)是软件开发过程中,前人对反复出现的各种问题提出的优秀解决方案。本文是呉真在学习《Head First设计模式》过程中总结的一些学习笔记。

设计原则

  • 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
  • 针对接口编程,而不是针对实现编程
  • 多用组合,少用继承
  • 为了交互对象之间的松耦合设计而努力
  • 类应该对扩展开放,对修改关闭
  • 要依赖抽象,不要依赖具体类
  • 最少知识原则:只和你的密友谈话
  • 好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你
  • 一个类应该只有一个引起变化的原因

策略(Strategy)模式

  策略模式定义了算法簇,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

举例:
  汽车中有三大件:发动机、变速箱、底盘,不同型号的汽车对于三大件乃至其他小部件均有不同的组合,为此我们可以将三大件中通用的部分提取出来,用于组合不同型号的汽车。

  • Engine(引擎接口)
    • EA211(大众发动机实现)
    • EA888(大众发动机实现)
  • Gearbox(变速箱接口)
    • 6AT(6档自动变速箱实现)
    • 6MT(6档手动变速箱实现)
    • CVT(无级变速变速箱实现)
  • Chassis(底盘接口)
    • FF(前置前驱实现)
    • FR(前置后驱实现)
    • 4WD(四轮驱动实现)

  有了这些具体实现后,进一步搭配组合,产生了桑塔纳和TT两种型号的汽车(配置不一定真实,仅为举例说明),各自搭配了不同型号的配件。

  • Car(汽车抽象类)
    • Engine(属性,声明为接口)
    • Gearbox
    • Chassis
  • Santana extends Car(大众·桑塔纳)
    • Engine: EA211
    • Gearbox: 6MT
    • Chassis: FF
  • TT extends Car(奥迪·TT)
    • Engine: EA888
    • Gearbox: 6AT
    • Chassis: 4WD

观察者(Observer)模式

  观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

举例:

  • 订阅某一本杂志时,出版社作为主题,而我们订阅者即为观察者,每发行一期期刊,出版社都会将最新的杂志通过邮政系统送到我们的手里,这是一种模式。
  • 关注某个公众号(主题)后,一旦这个公众号有更新,这个公众号的头像上就会出现一个小红点,此时我们(观察者)点击进入这个公众号后,即可拉取最新的文章,这是一种模式。

装饰器(Decorator)模式

  装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

举例:

  购买一点点奶茶时,我们要根据自身的需求,选择不同的糖分/加料/温度/大小,我们不可能针对每一种情况单独派生一个相应的子类,如此程序是相当难维护的。但是我们可以创建相应的糖分装饰器/调料装饰器,用来装饰每一种选择。

  • 饮料(抽象类)

    • 奶茶
    • 奶绿
    • 拿铁
    • 可可
    • 加料装饰器(抽象类)
      • 波霸
      • 布丁
      • 珍珠
    • 温度装饰器(抽象类)
      • 热/温/冰/少冰/去冰
    • 糖度装饰器(抽象类)
      • 常规/半塘/微糖/去糖
    • 大小装饰器(抽象类)
      • 小杯/中杯/大杯
  • (大杯(常规糖(冰(珍珠(可可))))):一杯用珍珠、冰、常规糖、大杯装饰的可可

  • (中杯(微糖(温(波霸(奶绿))))):一杯用波霸、温、微糖、中杯装饰的奶绿

工厂(Factory)模式

  简单工厂:直接将实例化具体类的过程单独封装为一个简单工厂类。
  静态工厂:利用一个静态方法返回已实例化的具体类。

  工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
  抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

单件(Singleton)模式

  单件模式确保一个类只有一个实例,并提供一个全局访问点。

命令(Command)模式

  命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

适配器(Adapter)模式

  适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

外观(Facade)模式

  外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

模板方法(Template)模式

  模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

迭代器(Iterator)模式

  迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

  • 迭代器允许访问聚合的元素,而不需要暴露它的内部结构
  • 迭代器将遍历聚合的工作封装进一个对象中
  • 当使用迭代器的时候,我们依赖聚合提供遍历
  • 迭代器提供了一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态机制

组合(Composite)模式

  组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

  • 组合模式提供一个接口,可同时包容个别对象和组合对象
  • 组合模式允许客户对个别对象以及组合对象一视同仁
  • 组合结构内的任意对象称为组件,组件可以是组合,也可以是叶结点
  • 在实现组合模式时,有许多设计上的折衷。你要根据需要平衡透明性和安全性

状态(State)模式

  状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

  • 和程序状态机(PSM)不同,状态模式用类代表状态

代理(Proxy)模式

  代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

  • 防火墙代理(Firewal Proxy):控制网络资源的访问,保护主体免于“坏客户”的侵害
  • 智能引用代理(Smart Reference Proxy):当主体被引用时,进行额外的动作,例如计算一个对象被引用的次数
  • 缓存代理(Caching Proxy):为开销大的运算结果提供暂时存储;它也允许多个客户共享结果,以减少计算或网络延迟
  • 同步代理(Synchronization Proxy):在多线程的情况下为主题提供安全的访问
  • 复杂隐藏代理(Complexity Hiding Proxy):用来隐藏一个类的复杂集合的复杂度,并进行访问控制
  • 写入时复制代理(Copy-On-Write Proxy):用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止

复合(Compound)模式

  复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。

其他模式

  • 桥接(Bridge):使用桥接模式不只改变你的实现,也改变你的抽象
  • 生成器(Builder):使用生成器模式封装一个产品的构造过程,并允许按步骤构造
  • 责任链(Chain Of Responsibility):当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式
  • 蝇量(Flyweight):如想让某个类的一个实例能用来提供许多“虚拟实例”,就使用蝇量模式
  • 解释器(Interpreter):使用解释器模式为语言创建监视器
  • 中介者(Mediator):使用中介者模式来集中相关对象之间复杂的沟通和控制方式
  • 备忘录(Memento):当你需要让对象返回之前的状态时,就使用备忘录模式
  • 原型(Prototype):当创建给定类的实例的过程很昂贵或很复杂时,就使用原型模式
  • 访问者(Visitor):当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式