“每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心,通过这种方式,我们可以无数次地重用那些已有的成功的解决方案,无须再重复相同的工作。”
——《建筑的永恒之道》
“设计模式是在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案。”
1.1软件设计模式分类
GoF提出的23种设计模式(23种 + “简单工厂模式” = 24种),可以分为3类
- 创建型(Creational)模式:如何创建对象;
- 结构型(Structural)模式:如何实现类或对象的组合;
- 行为型(Behavioral)模式:类或对象怎样交互以及怎样分配职责。
1.2 设计模式总览表
模式大类 | 模式名称 | 作用 |
---|---|---|
创建型模式 (6) | 单例模式 | 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 |
简单工厂模式 | 定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类。 | |
工厂方法模式 | 定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。 | |
抽象工厂模式 | 提供一个创建一系列相关或相互依赖的接口,而无需指定它们具体的类。 | |
原型模式 | 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 | |
建造者模式 | 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 | |
结构型模式 (7) | 适配器模式 | 将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 |
桥接模式 | 将抽象部分与实际部分分离,使它们都可以独立的变化。 | |
组合模式 | 将对象组合成树形结构以表示“部分-整体”的层次结构。使得用户对单个对象和组合对象的使用具有一致性。 | |
装饰模式 | 动态地给一个对象添加一些额外的职责。就增加功能来说,此模式比生成子类更为灵活。 | |
外观模式 | 为子系统中的一组接口提供了一个一致的界面,从模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 | |
享元模式 | 以共享的方式高效地支持大量的细粒度的对象。 | |
代理模式 | 提供一种代理来控制对其他对象的访问。 | |
行为型模式 (11) | 职责链模式 | 在该模式里,很多对象由每个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。 |
命令模式 | 将一个请求封装成一个对象,从而可用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 | |
解释器模式 |
如何为简单的语言定义一个语法,如何在语言中表示一个句子,以及如何解释这些句子。 | |
迭代器模式 | 提供了一种方法来顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。 | |
中介者模式 | 定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显式地相互调用,从而使其耦合性松散,而且可以独立改变它们之间的交互。 | |
备忘录模式 | 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 | |
观察者模式 | 定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 | |
状态模式 | 对象的行为,依赖于它所处的状态。 | |
策略模式 | 定义了一系列算法,并将每一个算法封装起来,而且使它们可以相互替换。 策略模式让算法独立于用户而独自变化。 | |
模板方法模式 | 定义一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 | |
访问者模式 |
如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一。 面向对象设计原则为支持可维护性和可复用性而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则。
1.3 面向对象设计原则
原则 | 定义 |
---|---|
单一职责原则 | 类的职责单一,对外只提供一种功能;引起类变化的原因应该只有一个。 |
开闭原则 | 类的改动是通过增加代码进行的,而不是修改代码;对扩展开放,对修改关闭。 |
里氏代换原则 | 任何抽象类出现的地方都可以用相应的实现类进行替换。 实际就是虚拟机制,语言级别实现面向对象功能。 |
依赖倒转原则 | 依赖于抽象(接口),不要依赖具体的实现(类)。 就是针对接口编程。 |
接口隔离原则 | 不应该强迫用户的程序依赖不需要的接口方法。一个接口应该只提供一种对外功能,不应该把所有操作都封装到一个接口中去。 |
合成复用原则 | 如果使用继承,会导致父类的任何变换都可能影响到子类的行为;如果使用对象组合,就降低了这种依赖关系。对于继承和组合,优先使用组合。 |
迪米特法则 | 一个对象应当对其他对象尽可能少的了解,从而降低各个对象之间的耦合,提高系统的可维护性。 |
1.4 设计模式详解
1.4.1 简单工厂模式
简单工厂模式 + “开闭原则” = 工厂方法模式
- 简单工厂模式的优缺点
优点:1)实现了对象创建与使用的分离;2)用户不关注类名,记住参数即可
缺点:1)工厂类职责过重,一旦不能工作,系统受到影响;2)系统中类的个数增多,代码
复杂度增加; 3)违反“开闭原则”,添加新产品需要修改工厂逻辑,工厂越来越复杂 - 适用场景
1)工厂类负责创建的对象比较少,不会造成工厂方法中的业务逻辑太过复杂;
2)用户只需传入工厂类的参数,不关注如何创建对象。
1.4.2 工厂方法模式
- 工厂方法模式的优缺点
优点:1)用户不关注具体类名和参数;2)实现对象创建与使用的分离;3)系统扩展性好
缺点:1)系统中类的个数增多,代码复杂度增加;2)系统的抽象性增加,理解难度加大 - 适用场景
1)用户无需知道所需对象的类;
2)抽象工厂类通过其子类来指定创建相应对象。
1.4.3抽象工厂模式
1.4.4 单例模式
- 实现单例的步骤:
1)构造函数私有化; 2) 增加静态私有的当前类的指针变量;3)提供静态对外接口,让用户可以获得单例对象
- 懒汉式与饿汉式
1)懒汉式:使用时才创建对象; 饿汉式:main函数运行前已经创建对象
2)多线程下,使用饿汉式是线程安全的;使用懒汉式需要额外加锁解锁
1.4.5 代理模式
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 即:代理模式:为其他对象提供一种代理以控制对这个对象的访问。
举例:1)没有权限直接访问网页时,通过代理服务器去访问
1.4.6 外观模式
1.4.7 适配器模式
1.4.8 模板方法模式
- 优点:
1)在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,并且子类调用算法时严格遵循算法定义的步骤执行次序;
2)模板方法模式是一种代码复用技术,提取出库类中的公共行为放于父类中,差异性行为放于子类实现,其鼓励开发者恰当使用继承来实现代码复用;
3)可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行;
4)通过子类覆盖父类的基本方法,不同的子类可以提供基本方法的不同实现,便于更换和增加新的子类,符合单一职责原则和开闭原则。
1.4.9 策略模式
1.4.10 命令模式
1.4.11 观察者模式
1.4.12 装饰模式
装饰模式又叫包装模式,通过一种对用户透明的方式来扩展对象功能,是继承关系的一种替代。
装饰模式就是把要附加的功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,用户可以有选择的、按顺序的使用装饰功能包装对象。