0%

Head First - 设计模式 (JAVA)

阅读笔记及摘要

Chapter 1. 设计模式入门 - 策略模式 Strategy Pattern

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

设计原则

  1. 找出应用中可能会变化之处,封装起来,独立于不会变化的地方,使其不受影响。系统变得更有弹性。
  2. 针对接口(more specifically, SUPERTYPE, 可以指一个抽象类或者一个接口)编程,而不是针对实现编程。
  3. 多用组合(composition),少用继承

Chapter 2. 观察者模式 Observer Pattern

观察者模式定义了对象之间的一(Subject: 主题)对多(Observer: 观察者)依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

松耦合:当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。改变主题或者观察者其中一方,并不会影响另外一方。

【JAVA】

  • 使用观察者模式时,可以从被观察者处推push或者拉pull数据,推的方法被认为更正确。
  • 有多个观察者时,不可以依赖特定的通知次序。

设计原则

  1. 为了交互对象之间的松耦合设计而努力。对象间的互相依赖降到最低,建立有弹性的OO系统,能够应对变化。

Chapter 3. 装饰者模式 Decorator Pattern

通常,无法让设计的每个部分都遵循开闭原则。遵循开闭原则,通常会引入新的抽象层次,增加代码复杂度。在每个地方使用开闭原则是一种浪费,使代码变得复杂难以理解。应将注意力集中在设计中最有可能改变的地方,应用开闭原则。

允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为。这样的设计具有弹性可以应对改变,可以接受新的功能来应对变的需求。

装饰者模式动态地将责任附加到对象上。若想要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  • 装饰者与被装饰对象具有相同的超类型。
  • 可以用一个或者多个装饰者包装一个对象。
  • 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用修饰过的对象代替它。
  • 装饰者可以在所委托被装饰者行为之前/或之后,加上自己的行为,以达到特定的目的。
  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用喜欢的装饰者来装饰对象。

组合composition和委托delegation可用于在运行时动态地加上新地行为。

装饰器会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

设计原则

  1. ※类应该对扩展开放,对修改关闭。(开闭原则-Open Close Principle)

Chapter 4. 工厂模式 Factory Pattern

工厂处理创建对象的细节。

利用静态方法定义一个简单的工厂称为静态工厂。使用静态方法不需要使用创建对象的方法实例化对象,缺点是不能通过继承来改变创建方法的行为。

简单工厂并不是一个设计模式,更像一种编程习惯。

所有工厂模式都用来封装对象的创建。工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

  • Creator 创建者类:通常会包含依赖于抽象产品的代码,而这些抽象产品由子类创造。创造者不需要真的知道在制造哪种具体产品。
  • Product 产品类
  • 产品类与创建者类层级是平行的,因为它们都有抽象类。而抽象类都有许多具体的子类,每个子类都有自己特定的实现。

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法把实例化推迟到子类。

避免在OO设计中违反依赖倒置原则:

  • 变量不可以持有具体类的引用 - 用工厂模式避开“使用new就会持有具体类的引用”
  • 不要让类派生具体类 - 如果派生具体类,就会依赖具体类,请派生自一个抽象(接口或抽象类)
  • 不要覆盖基类中已实现的方法 - 如果覆盖基类已实现的方法,那么基类就不是一个真正适合被继承的抽象。基类中已经实现的方法,应该由所有的子类共享。

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道或关心实际产出的具体产品是什么。这样,客户就从具体的产品中被解耦。

  • 抽象工厂定义了一个接口,所有的具体工厂都必须实现此接口,这个接口包含一组方法用来生产产品。
  • 具体工厂实现不同的产品家族。要创建一个产品,客户只要使用其中的一个工厂而完全不需要实例化任何产品对象。
  • 每个具体工厂都能够生产一整组的产品。
  • 客户的代码中只需涉及抽象工厂,运行时将自动使用实际的工厂。

工厂方法&抽象工厂

  • 都能将对象的创建封装起来,使应用程序解耦,并降低其对特定实现的依赖。

  • 工厂方法:

    • 通过子类创建对象 - 用户只需要知道所使用的抽象类型,子类负责决定具体类型。aka工厂方法负责将客户从具体类型中解耦。
    • 不需要大接口,只需要一个方法。
    • 抽象创建者中所实现的代码通常会用到子类所创建的具体类型。
  • 抽象工厂:

    • 提供一个用来创建一个产品家族的抽象类型。这个类型的子类定义了产品被产生的方法。要使用这个工厂,必须先实例化它,然后将它传入一些针对抽象类型所写的代码中。aka将客户从所使用的实际具体产品中解耦。
    • 可以将一群相关的产品集合起来。
    • 加入新产品就必须改变接口。需要一个大的接口因为它被用来创建整个产品家族。
    • 经常使用工厂方法实现具体工厂。

工厂帮助我们针对抽象编程,而不要针对具体类编程。

设计原则

  1. 要依赖抽象,不要依赖具体类。(依赖倒置原则-Dependency Inversion Principle)
    • 不能让高层组件依赖低层组件。不管高层低层,两者都应该依赖于抽象。
    • 所谓高层组件,是由其他底层组件定义的行为类。
    • 低层的组件依赖高层的抽象,高层组件也依赖相同的抽象。

Chapter 5. 单例模式 Singleton Pattern

单例模式用于创建独一无二的,只能有一个实例的对象。

单例模式确保一个类只有一个实例,并提供一个全局访问点。

把某个类设计成自己管理的一个单独实例,同时避免其他类再自行产生实例。想要取得单件实例,通过单件类使唯一的途径。

提供对这个实例的全局访问点。当需要实例时,向类查询,它会返回单个实例。前面的例子利用延迟实例化的方式创建单件,这种做法对资源敏感的对象特别重要。

JAVA实现单例模式需要私有的构造器、一个静态方法和一个静态变量

确定再性能和资源上的限制,小心地选择适当的方案实现单件,以解决多线程的问题。

使用多个类加载器,可能导致单例失效而产生多个实例。

Chapter 6. 命令模式 Command Pattern 封装调用

命令模式可将”动作的请求者“从”动作的执行者“对象中解耦。利用命令对象,把请求封装成一个特定对象。

在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作。

调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用。

调用者可以接受命令当作参数,甚至在运行时动态地进行。

加载调用者

  • 客户Client创建一个命令对象CommandObject。
  • 客户利用setCommand()将命令对象储存在调用者Invoker中。
  • 稍后...客户要求调用者执行execute命令。*一旦命令被加载到调用者,该命令可以被使用并丢弃,或者可以被保存下来并使用许多次。

命令模式将“请求”封装成对象,以便使用不用的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

当你不想返回一个有意义的对象时,空对象(null object)很有用。客户也可以将处理null的责任转移给空对象。在许多设计模式中,都会看到空对象的使用。甚至有些时候,空对象本身也被视为是一种设计模式。

宏命令是命令的一种简单延申,允许调用多个命令。宏方法也可以支持撤销。

实际操作时,很常见使用”聪明“命令对象,也就是直接实现了请求,而不是将工作委托给接收着。

接收者一定有必要存在吗?为何命令对象不直接实现execute()方法的细节? - 一般来说,我们尽量设计"傻瓜"命令对象,它只懂得调用另一个接收者的一个行为。然而,有许多”聪明“命令对象会实现许多逻辑,直接完成一个请求。当然你可以设计聪明的命令对象,只是这样一来,调用者和接收者之间的解耦程度是比不上”傻瓜“命令对象的,而且,你也不能够把接收者当参数传给命令。

命令可以将运算块打包(一个接收者和一组动作),然后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的特性衍生一些应用。例如: Scheduler、线程池、工作队列等。

工作队列和进行计算的对象之间是完全解耦的。工作队列对象不在乎到底做些什么,它们只知道取出对象,然后调用其execute()方法。类似地,它们只要是实现命令模式的对象,就可以放入队列里,当线程可用时,就调用此对象的execute()方法。

某些应用需要我们将所有的动作都记录在日志中,并能在系统死机后,重新调用这些动作恢复到之前的状态。通过新增两个方法(store(), load()),命令模式就能够支持这一点。在JAVA中,我们可以利用对象的序列化Serialization实现这些方法,但是一般认为序列化最好还是只用在对象的持久化Persistence上。有许多调用大型数据结构的动作的应用无法在每次改变发生时被快速地存储。通过使用记录日志,我们可以将上个checkpoint之后的所有操作记录下来,如果系统出状况,从checkpoint开始应用这些操作。比方说,对于电子表格应用,我们可能想实现的错误恢复方式是将电子表格的操作记录在日志中,而不是每次电子表格一有变化就记录整个电子表格。对于更高级的应用而言,这些技巧可以被扩展到事务Transaction中。也就是说,一整群操作必须全部进行完成,或者没有进行任何的操作。

Chapter 7. 适配器 Adapter Pattern 与外观模式 Facade Pattern

当需要使用一个现有的类而其接口并不符合你的需要时,就使用适配器。

客户和适配器是解耦的,一个不知道另一个。

客户使用适配器的过程如下:

  • 客户通过目标接口调用适配器的方法对适配器发出请求
  • 适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。
  • 客户收到调用的结果,但并未察觉这一切是适配器在起转换作用。

适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

适配器改变接口以符合客户的期望。

(对象)适配器模式充满着良好的OO设计原则:使用对象组合,以修改的接口包装被适配者。这种做法还要额外的优点,被适配者的任何子类,都可以搭配着适配器使用。

适配器模式把客户和接口绑定起来,而不是和实现绑定起来。我们可以使用无数个适配器,每一个都负责转换不同组的后台类。或者,也可以加上新的实现,只要他们遵守目标接口就可以。

适配器有两种形式,对象适配器和类适配器。”类“适配器需要多继承实现。类适配器不是使用组合来适配被适配者,而是继承被适配者和目标类。而对象适配器利用组合的方式将请求传送给被适配者。

当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观。外观将一个或数个类的复杂的一切都隐藏在背后,只显露一个美好的外观。

外观将客户从一个复杂的子系统中解耦。

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

外观没有封装子系统的类,外观只提供简化的接口。所以客户觉得有必要,依然可以直接使用子系统的类。外观在提供简化的接口的同时,依然将系统完整的功能暴露出来,以供需要的人使用。

可以为一个子系统实现一个以上外观。

适配器的意图时,改变接口符合客户的期望。而外观模式的意图时,提供子系统的一个简化接口。装饰器将一个对象包装起来以增加新的行为和责任。

模式 意图
装饰者 不改变接口,但加入责任
适配器 将一个接口转成另一个接口
外观 让接口更简单

最少知识原则方针:就任何对象而言,在对象的方法内,我们只应调用属于以下范围的方法。

  • 该对象本身
  • 被当作方法的参数而传递进来的对象
  • 此方法所创建或实例化的任何对象
  • 对象的任何组件

虽然最少知识原则较少了对象之间的依赖,减少软件的维护成本。采用这个原则会导致更多的“包装”类被制造出来,以处理和其他组件的沟通,可能会导致复杂度和开发时间的增加,并降低运行时的性能。

设计原则

  1. 只和你的密友谈话。(最少知识原则-Least Knowledge, AKA, Law of Demeter)
    • 减少对象间的交互,只留下几个”密友“。
    • 设计一个系统时,不管是任何对象,都要注意它所交互的类有哪些,并注意它和这些类时如何交互的。
    • 在设计中,不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他部分。如果许多类之间互相依赖,那么这个系统就会变成一个易碎的系统,需要花许多成本维护,也许会因为台服在而不容易被其他人了解。

Chapter 8. 模板方式模式 Modeling Method Pattern

模板方式模式在一个方法中定义一个算法的骨架,而把一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

钩子hook是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。

创建模板方法用抽象方法还是钩子?当子类必须提供算法中某个方法或者步骤的实现时,使用抽象方法。如果算法的这个部分是可选的,就用钩子。

钩子可以让子类实现算法中可选的部分,或者在钩子对于子类的实现并不重要时,子类可以对钩子置之不理。钩子也可以让子类能够有机会对模板方法中某些即将发生的(或刚刚发生的)步骤作出反应。钩子也可以让子类有能力为其抽象类做一些决定。

应该保持抽象方法的数目越少越好,否则,在子类中实现这些方法将会很麻烦。要想实现这一点,可以让算法内的步骤不要切割得太细,但是如果步骤太少得话,会比较没有弹性,要看情况折衷。将可选的步骤实现成钩子而不是抽象方法,可以让抽象类的子类的负荷减轻。

低层组件并非不可调用高层组件中的方法,但要避免高层和低层组件之间有明显的环状依赖。

策略模式和模板模式都封装算法。一个用组合,一个用继承。

工厂方法是模板方法的一种特殊版本。

设计原则

  1. 好莱坞原则-别调用我们,我们会调用你。Hollywood Principle - “Don't call us, we'll call you.”
    • 防止依赖腐败-高层组件依赖低层组件,低层组件依赖高层组件,高层组件依赖边侧组件,边侧组件依赖底层组件。
    • 允许低层组件将自己挂钩到系统上,但高层组件会决定什么时候和怎样使用这些低层组件。

Chapter 9. 迭代器 Iterator Pattern 与组合模式 Composite Pattern

迭代器模式提供一种访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注在它所应该专注的事情上面(管理对象集合),而不便去理会遍历的事情。

内聚cohesion用来度量一个类或者模块紧密地达到单一目的地或责任。当一个模块或类被设计成只支持一组相关的功能时,我们说它具有高内聚;反之,当被设计成支持一组不相关功能时,我们说它具有低内聚。

组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象及对象组合。

组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。

使用组合结构,我们能把相同的操作应用在组合和个别对象上。AKA,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。

组合包含组件。组件有两种:组合与叶节点元素。

在实现组合模式时,有许多设计上的折衷。根据需要平衡透明性和安全性。组合模式以单一责任设计原则(管理层次结构+执行操作)换取透明性(通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户可以将组合和节点一视同仁)。为了保持透明性,组合内的所有对象都必须实现相同的接口。

设计原则

  1. 单一责任-一个类应该只有一个引起变化的原因。
  • 类的每个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。
  • 尽量让每个类保持单一责任。

Chapter 10. 状态模式 State Pattern

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

状态模式允许一个对象基于内部状态而拥有不同的行为。和PSM程序状态机不同,状态模式用类代表状态。

Context会将行为委托给当前的状态对象。

通过将每个状态封装进一个类,把之后要做的任何改变局部化。

一般来说,可以把策略模式想成是出来继承以外的一种弹性替代方式。如果使用继承定义了一个类的行为,你将会被这个行为困住,甚至要修改都很困难。有了策略模式,可以通过组合不同的对象来改变行为。策略模式通常会用行为或算法来配置Context类。

可以把状态模式想成是不用在context中放置许多条件判断的替代方案,通过将行为包装进状态对象中,可以通过在context内简单地改变对象状态来改变context的行为。状态模式允许Context随着状态的改变而改变行为。

状态转换可以由State类或Context类控制。

使用状态模式通常会导致设计中类的数目大量增加。

状态类可以被多个Context实例共享。

Chapter 11. 代理模式 Proxy Pattern

Java RMI (Remote Method Invocation)

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

使用代理模式创建代表representative,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。

远程代理:远程代理可以作为另一个JVM上的对象的本地代表。调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果转给客户。

虚拟代理:虚拟代理作为开销大的对象的对象的代表。虚拟代理经常直到真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。

代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式由许多种。

  • 远程代理管理客户和远程对象之间的交互
  • 虚拟代理控制访问实例化开销大的对象
  • 保护代理基于调用者控制对对象方法的访问

代理模式由许多变体,例如:缓存代理、同步代理、防火墙代理和写入时复制代理。

代理在结构上类似装饰者,但目的不同。装饰者模式为对象加上行为,而代理则时控制访问。

和其他的包装者wrapper一样,代理会造成设计中类的数目增加。

Chapter 12. 复合模式 Compound Pattern,MVC Model View Controller

模式通常被一起使用,并被组合在同一个设计解决方案中。复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。

MVC:

  • 视图:用来呈现模型。视图通常直接从模型中取得它需要显示的状态与数据
  • 控制器:取得用户的输入并解读其对模型的意思。
  • 模型:模型持有所有的数据、状态和程序逻辑。模型没有注意到试图和控制器,虽然它提供了操纵和检索状态的接口,并发送状态改变通知给观察者。

交互

  • 用户-和视图交互
  • 控制器要求模型改变状态
  • 控制器也可能要求视图做改变
  • 模型状态改变时,模型会告知视图
  • 视图向模型询问状态

MVC是复合模式,结合了观察者模式、策略模式和组合模式。模型使用观察者模式,以便观察者更新,同时保持两者之间解耦。控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为。视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板、框架和按钮。这些模式携手合作,把MVC的三层解耦,这样可以保持设计干净又有弹性。

设配器模式用来将新的模型适配成已有的视图和控制器。

Chapter 13. 真实世界中的模式

模式实在某情境context下,针对某问题的某种解决方案。

情境:某个模式的情况。这应该是不断出现的情况。

问题:想在某情境下达到的目标,但也可以是某情景下的约束。

解决方案:所追求的:一个通用的设计,用来解决约束,达到目标。

模式分类

  • 创建型:Singleton, Builder, Prototype, Abstract Factory, Factory Method

  • 行为型:Meditator, Visitor, Template Method, Iterator, Command, Memento, Interpreter, Observer, Chain of Responsibility, State, Strategy

  • 结构型:Proxy, Decorator, Facade, Composite, Flyweight, Bridge, Adapter

类或对象

  • 类:Template Method, Adapter, Factory Method, Interpreter
  • 对象:Composite, Visitor, Decprator, Command, Memento, Facade, Proxy, Observer, Strategy, Chain of Responsibility, Bridge, Mediator, State, Flyweight, Prototype, Abstract Factory, Builder, Singleton

用模式思考

  • 保持简单 KISS
  • 设计模式并非万灵丹
  • 知道合适需要模式
  • 重构的时间就是模式的时间
  • 删掉你不需要的,不要害怕将一个设计模式从设计中删除
  • 如果你现在不需要,就别做

过度使用设计模式可能导致代码被过度工程化。应该总是用最简单的解决方法完成工作,并在真正需要模式的地方才使用它。

模式应用领域

  • 架构模式
  • 应用模式
  • 领域特定模式
  • 业务流程模式
  • 用户界面设计模式

反模式:如何采用一个不好的解决方案解决一个问题。

模式 意图
装饰者 包装一个对象,以提供新的行为。
状态 封装了基于状态的行为,并使用委托在行为之间切换。
迭代器 在对象的集合之中游走,而不暴露集合的实现。
外观 简化一群类的接口。
策略 封装可以互换的行为,并使用委托来决定要使用哪一个。
代理 包装对象,以控制对此对象的访问。
工厂方法 由子类决定要创建的具体类是哪一个。
适配器 封装对象,并提供不同的接口。
观察者 让对象能够在状态改变时被通知。
模板方法 由子类决定如何实现一个算法中的步骤。
组合 用户用一致的方式处理对象集合和单个对象。
单例 确保有且只有一个对象被创建
抽象工厂 允许客户创建对象的家族,而无需指定他们的具体类。
命令 封装请求成为对象。

Reading List

  • The Timeless way of Building
  • A Pattern Language
  • The Portland Patterns Repository
  • The Hillside Group

Appendix. 剩下的模式

桥接 Bridge Pattern - 不止改变你的实现,也改变你的抽象

桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变。

优点:

  • 将实现予以解耦,让它和界面之间不再永久绑定。
  • 抽象和实现可以独立扩展,不会影响到对方。
  • 对于“具体的抽象类”所做的改变,不会影响到客户。

用途和缺点

  • 适合用在需要跨越多个平台的图形和窗口系统上。
  • 当需要用不同的方式改变接口和实现时,桥接模式很好用。
  • 缺点是增加了复杂度。

生成器 Builder Pattern - 封装一个产品的构造过程,并允许按步骤构造

优点:

  • 将一个复杂对象的创建过程封装起来。
  • 允许对象通过多个步骤来创建,并且可以改变过程。
  • 向用户隐藏产品内部的表现。
  • 产品的实现可以被替换,因为用户只看到一个抽象的接口。

用途和缺点:

  • 经常被用来创建组合结构。
  • 与工厂模式相比,采用生成器模式创建对象的客户,需要具备更多的领域知识。

责任链 Chain of Responsibility - 想要让一个以上的对象有机会能处理某个请求

通过责任链模式,可以为某个请求创建一个对象链。某个对象依序检查此请求,并对其进行处理,或者将它传给链中的下一个对象。

优点:

  • 将请求的发送者和接受者解耦。
  • 可以简化你的对象,因为它不知道链的结构。
  • 通过改变链内的成员或调动它们的次序,允许你动态地新增或者删除责任。

用途和缺点:

  • 经常被使用在窗口系统中,处理鼠标和键盘之类的事件。
  • 并不保证请求一定被执行;如果没有任何对象处理它的化,可能会落到链尾端之外。
  • 可能不容易观察运行时的特征,有碍于debug。

蝇量 Flyweight - 让某个类的一个实例能用来提供许多“虚拟实例”

优点:

  • 减少运行时对象实例的个数,节省内存。
  • 将许多“虚拟”对象的状态集中管理。

用途和缺点:

  • 当一个类有许多实例,而这些实例能被同一方法控制时,可以用蝇量模式。
  • 缺点在于,一旦实现,单个的逻辑将无法拥有独立而不同的行为。

解释器 Interpreter - 为语言创建解释器

优点:

  • 将每一个语法规则表示成一个类,方便于实现语言。
  • 因为语法由许多类表示,可以轻易地改变或扩展此语言。
  • 通过在类结构中加入新的方法,可以在解释的同时增加新的行为。

用途和缺点:

  • 当需要实现一个简单的语言时,使用解释器。
  • 有一个简单的语法,而且简单比效率更重要时,使用解释器。
  • 可以处理脚本语言和编程语言。
  • 当语法规则的数目太大时,这个模式可能会变得非常复杂。这种情况下,使用解释器/编译器的产生器可能更合适。

中介者 Mediator - 集中相关对象之间复杂的沟通和控制方式

每个对象都会在自己的状态改变时,告诉中介者。每个对象都会对中介者所发出的请求作出回应。

中介者包含了整个系统的控制逻辑。当某个装置需要一个新的规则时,或者是一个新的装置被加入系统内,其所有需要用到的逻辑也都被加进了中介者内。

优点:

  • 通过将对象彼此解耦,可以增加对象的复用性。
  • 通过将控制逻辑集中,可以简化系统维护。
  • 可以让对象之间所传递的消息变得简单而且大幅减少。

用途和缺点:

  • 常常被用来协调相关的GUI组件。
  • 缺点是,如果设计不当,中介者对象本身会变得过于复杂。

备忘录 Memento - 需要让对象返回之前的状态时

目标:

  • 储存关键对象的重要状态
  • 维护关键对象的封装

优点:

  • 将被储存的对象放在外面,不要和关键对象混在一起,可以帮助维护内聚。
  • 保持关键对象的数据封装。
  • 提供了容易实现的恢复能力。

用途和缺点

  • 备忘录用于存储状态。
  • 缺点:储存和恢复状态的过程可能相当耗时。
  • Java中可以考虑使用序列化Serialization机制存储系统的状态。

原型 Prototype - 创建给定类的实例的过程很昂贵或者复杂时

原型模式允许通过复制现有的实例来创建新的实例(Java中通常意味着使用clone()方法,或者反序列化)。重点在于,客户的代码在不知道要实例化何种特定类的情况下,可以创造出新的实例。

优点:

  • 向客户隐藏制造新实例的复杂性。
  • 提供让客户能够产生未知类型对象的选项。
  • 在某些环境下,复制对象比创建新对象更有效。

用途和缺点:

  • 在一个复杂的类层次中,当系统必须从其中的许多类型创建新对象时,可以考虑原型。
  • 缺点:对象的复杂有时相当复杂。

访问者 Visitor - 为一个对象的组合增加新的能力,且封装并不重要时

访问者必须参观组合内的每个元素;这样的功能是在导游Traverser对象中,访问者通过导游的引导,收集组合中所有对象的状态。一旦状态被收集了,客户就可以让访问者对状态进行各种操作。当需要新的功能时,只要加强访问者即可。

优点:

  • 允许对组合结构加入新的操作,且无需改变结构本身。
  • 想要加入新的操作,相对容易。
  • 访问者所进行的操作,其代码是集中在一起的。

用途和缺点:

  • 当采用访问者模式的时候,就会打破组合类的封装
  • 因为游走的功能牵涉其中,所以对组合结构的改变就更加困难。