设计模式·面向对象的设计原则

 

设计模式的原则才是模式的原力,所有的模式都是通过原则推导出来的

设计模式的原则才是模式的原力,所有的模式都是通过原则推导出来的

为什么用OOP

  • 为了抵御变化
  • 将变化的范围降低到最小

重新认识面向对象

  • 隔离变化(宏观)
    • 将变化所带来的影响减为最小
  • 各司其职(微观)
    • 新增类型不应该影响原来类型的实现
    • 强调各个类的“责任”(功能)
    • 比如
      • 结构化设计中画画的责任是由mainForm来完成的
      • 抽象化设计中画画的责任是由各个对象自己完成的
      • 多态机制:接口一致但实现不同
  • 对象是什么
    • 封装了代码和数据
    • 对象是一系列可被使用的公共接口
    • 对象是某种拥有责任的抽象

设计原则

  • 原则比模式更重要
  • 通过原则实现模式
    • 两段非常像的代码可能表达非常不同的模式
    • 两段差异大的代码可能表达相同的模式
    • 评判的标准是设计原则而不是公式
    • 模式是由原则推导出来的

依赖倒置原则(DIP)

  • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)
    • 隔离了变化
    • MainForm不依赖Line、Rect,而依赖一个不变的Shape
  • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)
    • 不要在抽象中使用子类
  • 结构化设计
    • MainForm依赖于Line和Rect

    对象之间最弱的一种关联方式,是临时性的关联 代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系

  • 抽象化设计
    • 创建了Shape抽象类

开放封闭原则(OCP)

  • 对扩展开放,对更改封闭
  • 类模块应该是可扩展的,但是不可修改
    • 结构化设计
      • 每新增加一个形状,MainForm类就要修改
        • [修改] 增加MainForm中保存这类图形的vector
        • [修改] 在OnMouseUp抬起鼠标函数中,增加把这种图形加入vector的代码
        • [修改] 在OnPaint绘制函数中,增加遍历这类图形vector并绘制的代码
        • 每次修改需要重新编译这块代码
    • 抽象化设计
      • [增加/扩展] 一个新的图形类,这时增加到另外一个文件中
      • MainForm不需要修改类的声明
      • MainForm::OnMouseUp不需要修改
      • MainForm::OnPaint不需要修改
  • 当新增需求的时候,只要增加一个文件,原来的文件都不用修改

单一职责原则(SRP)

  • 一个类应该仅有一个引起它变化的原因
  • 变化的方向隐含着类的责任
    • 一个类含有7、80个方法
    • 这个类含了太多功能
    • 多个职责把类向多个方向拉扯
    • 容易出问题

Liskov 替换原则(LSP)

  • 子类必须能够替换它们的基类(IS-A)
    • 所有需要父类的地方,子类的对象都可以传过去使用(都可以用子类替换)
  • 继承表达类型抽象

接口隔离原则(ISP)

  • 不应该强迫客户程序依赖它们不用的方法
  • 接口应该小而完备
    • 一旦客户对类的接口有依赖
    • 类的这个接口必须保持不变
    • 尽量少的暴露接口个外界

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

  • 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
    • 继承应该理解成类属关系
      • SUV继承汽车
      • 小轿车继承汽车
      • 人继承动物
      • 动物继承生物
  • 继承在某种程度上破坏了封装性,子类父类耦合度高
    • 父类给子类暴露太多东西
  • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低

封装变化点

  • 使用封装来创建对象之间的分界层
    • 让设计者可以在分界层的一侧进行修改
    • 不会对另一侧产生不良的影响
    • 从而实现层次间的松耦合
  • 封装
    • 底层思维
      • 封装代码和数据
    • 高层次理解
      • 封装变化点
      • 一侧变化
      • 一侧稳定

针对接口编程,而不是针对实现编程

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

  • 结构化设计
    • MainForm依赖了Line、Rect等具体类型
  • 抽象化设计
    • MainForm依赖抽象接口Shape
    • MainForm::OnPaint执行Shape的绘制方法
  • 接口标准化的含义
    • 分工协作
    • 雕版印刷
    • 活字印刷

将8个设计原则提升为设计经验

  1. 设计习语 Design Idioms
    • Design Idioms 描述与特定编程语言相关的低层模式,技巧,惯用法。
  2. 设计模式 Design Patterns
    • Design Patterns主要描述的是类与相互通信的对象之间的组织关系
    • 包括它们的角色、职责、协作方式等方面。
  3. 架构模式 Architectural Patterns
    • Architectural Patterns描述系统中与基本结构组织关系密切的高层模式
    • 包括子系统划分,职责,以及如何组织它们之间关系的规则。

模式分类

重构获得模式 Refactoring to Patterns

  • 好的面向对象设计
    • 应对变化,提高复用
  • 设计模式的要点
    • 寻找变化点
      • 思维方式:把变化与稳定分开
      • 代码不能一视同仁
      • 软件系统有一些是变化的,有一些是不变的
    • 在变化点处应用设计模式
    • 更好地应对变化
    • 什么时候、什么地点应用设计模式理解设计模式结构本身更为重要
      • 理解设计模式的代码执行流程只是肤浅的认识
      • 真正是用设计原则重构代码
  • 设计模式的应用不宜先入为主
    • 不是用设计模式去开发
    • 而是在重构过程中推导出模式
    • 敏捷软件开发实践提倡重构到模式(Refactoring to Patterns)是目前普遍公认的最好的使用设计模式的方法
      • 先实现功能
      • 再分析代码违背了什么设计原则
      • 修正得到好的解决方案,碰巧是某种模式

重构关键技法

  • 静态 -> 动态
  • 早绑定 -> 晚绑定
  • 继承 -> 组合
  • 编译时依赖 -> 运行时依赖
  • 紧耦合 -> 松耦合

“组件协作”模式

  • 软件专业分工之后的第一个结果是
    • 框架与应用程序的划分
    • 我们开发程序,会使用到不同的有框架或库
    • 这就涉及到我们的代码与既有代码之间的协作方式
  • “组件协作”模式
    • 通过晚期绑定(回调)
    • 实现框架与应用程序之间的松耦合
    • 是二者之间协作时常用的模式
  • 典型模式
    • Template Method
    • Observer / Event
    • Strategy