这些设计模式特别关注对象之间的通信。 增加了执行通信的灵活性。
- 简介
为请求创建了一个接收者对象的链。 通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
-
优点:
- 降低耦合度。它将请求的发送者和接收者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 增加新的请求处理类很方便。
-
缺点:
- 不能保证请求一定被接收。
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
- 可能不容易观察运行时的特征,有碍于除错。
-
使用场景:
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求。
-
示例
场景:请求接口数据, 缓存上未找到, 委托给数据库查询接口。
- 简介
请求以命令的形式包裹在对象中,并传给调用对象。 调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。 调用者→接受者→命令。
-
优点:
- 降低耦合度。
- 新的命令可以很容易添加到系统中去。
-
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类。
-
使用场景:
- GUI 中每一个按钮都是一条命令。
- 模拟 CMD。
-
示例
场景:发送消息的命令
- 简介
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
-
优点:
- 支持以不同的方式遍历一个聚合对象。
- 迭代器简化了聚合类。
- 在同一个聚合上可以有多个遍历。
- 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
-
缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
-
使用场景:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 需要为聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口。
-
示例
场景:遍历书单信息.
- 简介
用来降低多个对象和类之间的通信复杂性。 这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
-
优点:
- 降低了类的复杂度,将一对多转化成了一对一。
- 各个类之间的解耦。
- 符合迪米特原则。
-
缺点:
- 中介者会庞大,变得复杂难以维护。
-
使用场景:
- 系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
- 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
-
示例
场景:打印用户信息.
- 简介
保存一个对象的某个状态,以便在适当的时候恢复对象。
-
优点:
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
-
缺点:
- 消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。(为了节约内存,可使用原型模式+备忘录模式。)
-
使用场景:
- 需要保存/恢复数据的相关状态场景。
- 提供一个可回滚的操作。
-
示例
场景:一张票的状态.
- 简介
当对象间存在一对多关系时,则使用观察者模式. 比如,当一个对象被修改时,则会自动通知依赖它的对象。
-
优点:
- 观察者和被观察者是抽象耦合的。
- 建立一套触发机制。
-
缺点:
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
-
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
-
示例
场景:用户信息修改, 通知观察用户信息修改的观察者对象
-
简介 空对象模式不是GoF设计模式,在空对象模式中,一个空对象取代 NULL 对象实例的检查。 Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为
-
优点: 1.客户端代码得到简化 2.减少空指针异常的机会 3.更少的条件需要更少的测试用例
-
使用场景: 1.责任链模式中的空处理程序 2.命令模式中的空命令 3.空记录器或空输出可保留对象之间的标准交互方式
-
示例
场景:空日志通道
-
简介 类的行为是基于它的状态改变的。 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
-
优点: 1.封装了转换规则. 2.枚举可能的状态,在枚举状态之前需要确定状态种类。 3.将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4.允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5.可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
-
缺点: 1.状态模式的使用必然会增加系统类和对象的个数。 2.状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3.状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
-
使用场景: 1.行为随状态改变而改变的场景。 2.条件、分支语句的代替者。
-
示例
场景:订单状态流转
-
简介 在策略模式中,一个类的行为或其算法可以在运行时更改。 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
-
优点: 1.算法可以自由切换。 2.避免使用多重条件判断。 3.扩展性良好。
-
缺点: 1.策略类会增多。 2.所有策略类都需要对外暴露。
-
使用场景: 1.如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2.一个系统需要动态地在几种算法中选择一种。 3.如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
-
示例
场景:对 对象列表 进行排序,一种策略按日期排序,另一种策略按ID排序
-
简介 在模板模式中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
-
优点: 1.封装不变部分,扩展可变部分。 2.提取公共代码,便于维护。 3.行为由父类控制,子类实现。
-
缺点: 1.每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
-
使用场景: 1.有多个子类共有的方法,且逻辑相同。 2.重要的、复杂的方法,可以考虑作为模板方法。
-
示例
场景:一场旅行 (买票->到达->购物->享受时光->返程) 大致执行过程可以看作模板.