设计模式
模式的诞生与定义
- 模式起源于建筑业而非软件业
模式要解决的三个问题:Context,Theme/problem,solution
- 模式可使用的前提条件
- 在特定条件下要解决的目标问题
- 对目标问题求解过程中各种物理关系的记述
模式是在特定环境下人们解决某类重复出现问题的一套
成功或者有效的解决方案。A pattern is a successful or efficient solution to a recurring problem within a context.
软件模式概述
- 四人组在1994年归纳发表了23种在软件开发中使用频率较高的设计模式,旨在用模式来统一沟通面向对象方法在分析、设计和实现间的鸿沟
- 软件模式:在一定条件下的软件开发问题及其解法
- 问题描述
- 前提条件(环境或者约束条件)
- 解法
- 效果
- 大三律:只有经过三个以上不同类型的系统的校验,一个解决方案才能从候选模式升格为模式
设计模式的定义
- 一套被反复使用的、被多人知晓的、经过分类编目的、代码设计经验的总结
- 是一种用于对软件系统中不断重现的设计问题的解决方案进行文档化的技术
- 是一种共享专家设计经验的技术
- 目的:为了可重用代码、让代码更容易被他人理解、提高代码的可靠性
- 定义:设计模式是在特定环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的相互作用。
Design patterns are descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.
设计模式的基本要素
- 设计模式一般包括模式名称、问题、目的、解决方案、效果、实例代码和相关设计模式等基本要素,4个关键要素如下:
- 模式名称(Pattern Name)
- 问题(Problem)
- 解决方案(Solution)
- 效果(Consequences)
设计模式的分类
根据目的(模式是用来做什么的)可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三类:
- 创建型模式主要用于创建对象
- 结构型模式主要用于处理类或对象的组合
- 行为型模式主要用于描述类或对象如何交互和怎么样分配职责
根据范围,即模式主要是处理类之间的关系还是处理对象之间的关系,可分为类模式和对象模式两种:
- 类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,是一种静态关系
- 对象模式处理对象间的关系,这些关系在运行时变化,更具动态性
创建型模式:
- 抽象工厂模式
- 建造者模式
- 工厂方法模式
- 原型模式
- 单例模式
结构型模式:
- 适配器模式
- 桥接模式
- 组合模式
- 装饰模式
- 外观模式
- 享元模式
- 代理模式
行为型模式:
- 职责链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 策略模式
- 模板方法模式
- 访问者模式
设计模式的优点
- 融合了众多专家的经验,并以一种标准的形式供广大开发人员所用
- 提供了一套通用的设计词汇和一种通用的语言,以方便开发人员之间进行沟通和交流,使得设计方案更加通俗易懂
- 让人们可以更加简单方便地复用成功的设计和体系结构
- 使得设计方案更加灵活,且易于修改
- 将提高软件系统的开发效率和软件质量,且在一定程度上节约设计成本
- 有助于初学者更深入地理解面向对象思想,方便阅读和学习现有类库与其他系统中的源代码,还可以提高软件的设计水平和代码质量
面向对象设计原则
面向对象设计原则概述
- 可维护性(Maintainability):指软件能够被理解、改正、适应及扩展的难易程度
- 可复用性(Reusability):指软件能够被重复使用的难易程度
- 面向对象设计的目标之一在于支持可维护性复用,一方面需要实现设计方案或者源代码的复用,另一方面要确保系统能够易于扩展和修改,具有良好的可维护性
- 面向对象设计原则为支持可维护性复用而诞生
- 为指导性原则而非强制性原则
- 每一个设计模式都符合一个或多个面向对象设计原则,面向对象设计原则是用于评价一个设计模式的使用效果的重要指标之一
单一职责原则
- 单一职责原则是最简单的面向对象设计原则,用于控制类的粒度大小
定义:一个对象应该只包含单一的职责,并且该职责被完整的封装在一个类中。(引起一个类发生变化的原因至多只有一个)
Single Responsibility Principle(SRP):Every object should have a single responsibility,and that responsibility should be entirely encapsulated by the class.
单一职责原则是实现高内聚、低耦合的指导方针
开闭原则
- 开闭原则是面向对象的可复用设计的第一块基石,是最重要的面向对象设计原则
定义:软件实体应当对扩展开放,对修改关闭。
Open-CLosed Principle(OCP):Software entities should be open for extension,but closed for modification.
在开闭原则的定义中,软件实体可以是一个软件模块、一二由多个类组成的局部结构或一个独立的类
- 开闭原则是指软件实体应尽量在不修改原有代码的情况下进行扩展
- 抽象化是开闭原则的关键
- 相对稳定的抽象层+灵活的具体层
- 对可变性封装原则:找到系统的可变因素并将其封装起来
里氏代换原则
详细定义:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换o2时,使得程序P的行为没有变化,那么类型S是类型T的子类型
Liskov Substitution Principle(LSP):If for each object o1 of types S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
精简定义:所有引用基类的地方必须能透明地使用其子类的对象。
Liskov Substitution Principle(LSP):Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立。如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象
- 在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型
依赖倒转原则
定义:高层模块不应该依赖底层模块,他们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
Dependency Inversion Principle(DIP):Hign level modules should not depend upon low level modules,both should depend upon abstractions.Abstractions should not depend upon details,details should depend upon abstractions.
要针对接口编程,不要针对实现编程
- 在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中
- 在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类。即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等
- 针对抽象层编程,将具体类的对象通过依赖注入的方式注入到其他对象
- 构造注入
- 设值注入(setter)
- 接口注入
接口隔离原则
定义:客户端不应该依赖那些它不需要的接口
Interface Segregation Principle(ISP):Client should not be forced to depend upon interfaces that they do not use.
当一个接口太大时,需要将它分割成一些更细小的接口
- 使用该接口的客户端仅需知道与之相关的方法即可
- 不干不该干的事,该干的事都要干。接口承担的是一种相对独立的角色
合成复用原则
定义:优先使用对象组合,而不是继承来达到复用的目的。
Composite Reuse Principle(CRP):Favor composition of objects over inheritance as a reuse mechanism.
通过关联关系(包括组合和聚合)来使用一些已有的对象,使之成为新对象的一部分
- 新对象通过委派调用已有对象的方案达到复用功能的目的
- 复用时要尽量使用组合/聚合,少用继承
迪米特法则
- 最少知识原则
定义:每一个软件单位对其他单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
Law of Demeter(LoD):Each unit should have only limited knowledge about other units:only units “closely” related to the current unit.
要求一个软件实体应该尽可能少地与其他实体发生相互作用
- 应用迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系
- 通过引入一个合理的“第三者”来降低现有对象之间的耦合度