Skip to content

design_patten

Xiaolin Zhang edited this page Nov 5, 2019 · 3 revisions

设计模式

[toc]

设计模式的重要性是毋庸置疑的. 首先他确定了代码里是有固定的pattern的, 换句话说代码是有套路的. 而后他开创性的指出我们要给套路分门别类给出名字. 这样可以便于交流和分享.

link:

设计原则

  1. 依赖倒置原则(DIP)

  • 高层模块(比较稳定的逻辑)不依赖于底层模块(经常变化的逻辑). 二者都依赖于抽象
  • (抽象是从不稳定的里面抽出来的稳定形态). 抽象不依赖于实现细节(不可以在抽象类里面实现子类), 实现细节应该依赖抽象
  1. 开放封闭原则(OCP) (尽可能设计出来可扩展的,做不到的时候不要滥用)

  • 对扩展开放, 对更改封闭
  • 类模块应该是扩展的, 但是不可修改
  1. 单一职责原则(SRP)

  2. 一个类应该仅有一个引起他变化的原因

  3. 变化的方向隐含着类的责任

  4. Liskov 替换原则(LSP)

  5. 子类必须能够替换他们的基类(是IS-A的关系)

  6. 继承表达类型抽象

  7. 接口隔离原则 (ISP)

  8. 不应该强迫客户端程序依赖他们不用的方法

  9. (如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。)

  10. 一个类对另外一个类的依赖性应当是建立在最小的接口上的。

  11. 优先使用对象组合,而不是类继承

  12. 类继承通常为"白箱复用"(父类的细节对子类可见),对象组合通常为"黑箱复用"(只能通过接口访问父类)。

  13. 继承在某种程度上破坏了封装性,子类父类耦合度高。

  14. 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

父类通常定义了部分子类的具体表示, 而且Java中继承对子类揭示了其父类的实现细节, 所以继承通常被认为是"破坏封装性"(因为给了内部实现的细节). 父类和子类耦合太深了.父类发生变化子类一定会变化.

Golang是没有继承的,全是组合(**委托**). 即使是嵌套结构体也只是语法糖而不是[继承](https://yar999.gitbooks.io/gopl-zh/content/ch6/ch6-03.html). 不过需要注意的是, 组合更需要定义良好的接口.
  1. 封装变化点

  • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。
  1. 针对接口编程, 而不是针对实现编程 (和DIP是相辅相成的)

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口。
  • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
  • 减少系统中各部分的依赖关系,从而实现"高内聚、松耦合"的类型设计方案。

概念/问题

复用

面向对象里的复用讲的是不该一个类的代码, 一个类常常在一个单独文件里, 也就是不用重新编译. 不该源代码完成扩展

switch case 看起来很美好, 但是很快就会破窗. 所以要延迟破窗点到垃圾代码里.

接口的好处

  1. 抽象: 隐藏底层细节(context.Context)
  2. 复用: 提高代码复用能力.
  3. 规范: 使得分工协作成为可能, 分工协作才能搞出来强盛的产业. 而接口是分工协作的基础. 代码也是这样的

应用程序 - 工具箱 - 框架的关系

  • 应用程序关注: 内部复用性(不要做多余的设计的实现), 可维护性(分层, 限制依赖关系), 扩充性(扩展层次结构, 复用对象)
  • 工具性: 预定义类库. 关注的是代码复用, 要没有依赖关系
  • 框架: 规定你的应用的体系结构, 定义整体流程结构. 类和对象职责, 分割与合作.以及公职流程. 应用者的不再需要做更多的决策. 关注的是设计复用.框架是主体, 要复用框架, 写框架调用的代码. 你不得不以特定的名字和调用约定来写操作实现.但这回减少你做设计决策的机会.

重构到模式

公认: 重构到模式. 提炼出来patten

分类

从目的上

  • 创建型
  • 结构型
  • 行为型

创建型 (绕开new方法,创建对象)

3.1 Factory

本质: 一种新的 new 对象的方法, 将创建对象延迟到运行时(以获得动态效果)

简单工厂

通过switch case 根据不同的参数返回不同的新对象,

缺点: 不符合开闭原则.

工厂方法

定义: 定义一个用来创建对象的接口(工厂接口), 让工厂的子类决定实例化哪一个产品类.

缺点: 要求创建方法/参数一样

抽象工厂

定义: 提供一个接口, 让该接口负责创建一系列"相关或者相互依赖的对象"

一个超级工厂关联多个工厂, 多个工厂生成相互关联的产品. 多个对象的创建延迟到方法调用

3.2 Builder

问题: 复杂对象的创建工作. 各个部分的子系统经常用一定的算法构成. 由于需求的变动, 这个复杂对象的各个部分经常发生剧烈的变化, 但是将他们组合在一起的算法相对稳定.

目的: 将一个复杂对象的构建和他的表示分离. 使用同样的构造过程,产生不同的表示.

它属于创建类模式,一般来说,如果一个对象的构建比较复杂,超出了构造函数所能包含的范围,就可以使用工厂模式和Builder模式,相对于工厂模式会产出一个完整的产品,Builder应用于更加复杂的对象的构建,甚至只会构建产品的一个部分。《effective-java》中第2条也提到:遇到多个构造器参数时,考虑用构建者(Builder)模式

3.4 Prototype

定义: 通过原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象

原因: 某些结构比较复杂的对象的创建工作.

  • 采用克隆的方式来创建一个新的对象. 可以灵活的拥有远对象的中间状态

结构型

通过类继承或者对象组合产生更加灵活的结构.

Template 模板模式

problem: 框架流程稳定, 框架需要调用的一些方法不稳定

定义: 定义各一个操作中的算法的骨架(稳定), 将一个步骤(变化)延迟到子类中.使得子类可以不改变(复用)一个算法的结构即可重定义(override重写)改算法的某些特定步骤

角色: 主流程, 子类方法.

改变: 主流程提前调用所有的字方法, 然后应用开发者填坑.

Strategy 策略模式(IF ELSE)

problem: 某些对象的算法可能多种多样, 经常改变, 如果都将这些算法编码对象编码到对象里,将会使得对象变得异常复杂; 有时候支持不使用的算法也是性能负担.

定义: 定义一系列算法. 将他们一个个封装起来, 并且使他们可以相互替代(变化). 该模块使得算法可以独立于其他的客户程序(稳定)而变化(扩展, 子类化)

node: 可以和工厂连用. 很多个算法是产品.本质就是将一个"算子"提取成抽象的接口.但是不光算子可以提取. 很多东西都可以提取呀. 还不如工厂模式广泛

Observer/Even (行为)

problem: 定义对象间的一种一对多的依赖关系, 使得一个对象的变更, 依赖他的对象都可以得到通知并自动刷新.

其实就是回调

引入这个是从MVC模式, 一个对象(model) 因为 controler 对其操作, 导致了状态变化, 这种变化自动的在View上也展示了出来. 这样就将view和模型分离.

Decorator 装饰器模式

问题: 我们需要用不同的手段"装饰"多个不同的类, 如果加N个子类继承然后挨个去改所有的代码很蠢- -! 本质上就是缺乏灵活性, 拓展只能通过增加新的子类.使得子类急速膨胀.

定义: 动态(组合)地给一个对象增加一些额外的职责. decorator模式更加灵活(消除重复代码, 已经庞大的子类数量)

  • 单一责任原则
  • 组合优于继承原则

装饰模式可以说就是在已有功能的基础上添加更多的功能,把每个要添加的功能都放在一个单独的类中,并让这个类包装被装饰对象,然后在执行特定任务时,客户端代码就可以在运行时根据需要自由组合,按顺序地使用这些装饰功能。

Bridge 桥模式

problem: 由于某些类型的固有实现逻辑, 使得他们具有两个变化的维度, 乃至多个维度非常强的变化.

解决办法: 将每个变化单独封装, 然后通过指针连在一起.

  • 单一职责原则: 变化的方向隐藏着责任. 一个类应该只有一个引起他变化的原因

Strategy (行为)

problem: 当你想静态的或者动态的替换一个算法, 或者你有很多不同的算法, 或者算法中包含你想要封装的复杂的数据结构.

对象性能模式

单例模式 (全局变量可破)

有一些特殊的类, 为了效率, 实现的正确性.只能有一个实例

这是类设计者的问题.而不是使用者的问题

享元模式

运用共享技术有效的支持大量细粒度的对象

各种池

接口隔离

通过在组件之间增加一个接口(稳定) 来隔离本来相互紧密关联的组件

Facade 门面模式思想

在整个子系统内部和外部之间创建一个接口. 将子系统的实现放在内部. 内部是不稳定的, 接口是稳定的.

  • 更注重从架构层次去看整个系统.而不是单个的类是一种架构设计模式.
  • 内部并不是一个集中箱,而是相互耦合关系比较大的组件

Proxy 代理模式

problem: 直接访问会给使用者.系统结构带来麻烦

比如我们的 external_catwalkriver_catwalk

代理模式就是为一个对象提供一个代理并控制着对这个对象的访问。并且被代理对象对于客户端代码是透明的。就像最后这个栗子中,客户端代码并不知道真实的追求者是谁。代理类控制着真实追求着的访问,当然也可以添加一些功能什么的(就像装饰模式那样)。

Adapter 适配器

将一个类的接口转换为客户希望的另外一个接口.可以使得原本由于接口不兼容而不能在一起工作的那些类一起工作.

Mediator 中介者

problem: 多个对象相互关联交互, 对象之间维持一种复杂的引用关系.

提出一个中介者, 所有的对象都与中介者相互依赖(双向绑定)

状态变化模式

某些对象的状态经常面临变化, 如何对这些变化进行有效的管理? 同时维持高层模块的稳定?

State

对象状态改变, 其行为(每个状态都有的行为)也会随之发生变化. 比如文档处于只读状态, 其支持的行为和读写的状态支持的行为就可能完全不同.

定义: 允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。 ----《设计模式》GoF

  • State模式将所有与一个特定状态相关的行为都放入一个State的子对象中,在对象状态切换时,切换相应的对象; 但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
  • 转换是原子性的
  • 与Strategy模式类似

Memento 其实就是深拷贝快照

动机(Motivation)
  • 某些对象的状态转换过程中,可能由于某中需要,要求程序能够回溯到对象之前处于某个点的状态。 如果使用一些公开接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
  • 如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性、
模式定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。 ----《设计模式》GoF

要点总结
  • 备忘录存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。
  • 有些过时。

数据结构模式

一些组件内部具有特定的数据结构.

Composite (结构)

problem: 将一些对象化为一组, 并将组对象也当做一个对象来使用

定义: 将对象组合成树形结构以表示"部分-整体"的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性(稳定)。 ----《设计模式》GoF

consequence: 可以创建类层次结构, 而且可以任意组合.

Iterator

Chain Of Resposibility

Clone this wiki locally