Design Pattern
这段时间把 <Design Pattern> 这本书重新复习了一遍,把里面的模式用 Java 实现了一遍。这篇文章会把模式简单描述。
Wikipedia 有很好的资源,每一种模式都有详细的讲解,并且给出了示例。
关于在工作中使用设计模式的一点感想。
1. 模式不能生搬硬套。尽管《设计模式》这本书影响很大,提供了和总结很多思路。毕竟是 20 年前的老书了。在实际应用中,需要灵活的使用。现在大行其道的各种框架中都有原始的这些设计模式的影子。
2. 了解设计模式能够大大节省理解新框架和协同工作的时间。因为各种设计模式的流行,它们已经被使用在大量的现有代码中。了解这些模式,可以更快更好地理解这些代码。
3. 能启发改造和发明新模式的思路。在《设计模式》之后,行业中又发明了大量的新的设计模式,这些模式解决了很多过去和现行的问题。了解这些模式,知晓它们的起源和原理,可以为发明和改造新模式提供思路。
Factory
主要用来创建对象。对于 Client 来讲,传入不同的 Concrete Factory 就可以创建出不同的对象。常见的例子是 UI Style Button. 创建 OSXButton 和 WinButton (实现 Button),分别被 OSXFactory 和 WinFactory (实现 Factory)创建。
Builder
用来创建对象。把具体创建的 Object 的特征和传入创造函数的参数分离,方便表达。同时,加强了不可变性。Builder 本身可以被重用,增强了灵活性。Protobuf 和 AutoValue 等工具会自动生成数据类的对应 Builder。
Prototype
用于创建对象。好处是节省了创建对象所需要的资源。比如需要提供类默认值的场合,直接使用 Clone 是不错的选择。
Singleton
保证全局只有唯一实例的机制。常用于提供时间,提供网络访问类等等。在多线程情况下,可能要考虑同步锁。与 Dependency Injection 结合使用相得益彰。
Adapter
当需要不兼容的两端兼容时可以使用。在 A 类和 B 类中间加入 AB 类,BA 类,甚至双向调用的类,使两套不同的接口能够相互调用。实际应用中概念泛化了,比如 UI 里常用的 ListViewAdapter 其实是想定义一套接口,并要求 Client 实现。
Bridge
抽象与抽象交互。可能会定义 A B C 三种类,但它们全是抽象类,每种抽象类可以有很多种实现,这样就形成了很多组合。
Composite
为 Client 的调用提供了一套稳定的调用方法,相同的接口。Tree 里的每个 Node 就是一个例子。在具体项目中的例子包括 Flutter 框架里,WIdget 就是用 Composite 构建的。当 Flutter Engine 对 Widget 画图时,只关心它们的 RenderObject 和是否包含 Children。
Decorator
为已知类添加新功能的方法。相比于继承,Decorator 不需要改变原类,只是在原来的类外面包括一层,实现对原来类的功能的扩展。比如对于一个画正方形的类,可以在外包一层类,让其拥有画红色正方形的功能。
Facade
把底层的多个类的具体实现打包成一个接口,方便 Client 调用。比如当我说一句“去买外卖吧”,意味着小王去超市买吃的,小方是菜场买熟食,小李去饭店打包食物。而我得到了丰盛的外卖结果。
Flyweight
乍看起来和 Facade 很像,但是 Flyweight 的重点是可以重用 subsystem 的实例或者结果。因为创建 subsystem 的实例或者得到结果可能很耗时耗资源。所以这些 subsystem 可以在不同的顶层类间重用。
Proxy
为具体实现功能的系统提供了一个关口,外界只能通过这个关口访问功能。主要用于增加权限控制等。Client 调用时,常常不需要知道 Proxy 的存在。
Command
将需要执行的逻辑打包到一个 Command Object 中,由 Client 在适当的时候触发。用一个 UI 领域的例子。比如可以表单可能有两种操作,保存和验证内容,这两种都可以是 Command。当用户填完表单内容以后,表单会把内容传给这两个 Command,并执行它们的功能。Redux Thunk 也算是 Command 的一种。
Chain of responsibility
在 Command 的基础上,当一个 Command 需要被多个部分 Handle 时就需要 Chain of responsibility。比如在 Redux 中,一个 Action 可能会引发一系列的 Middleware,最后被 Reducer 给 handle。
Interpreter
其实就是自定义一套语言规则,这样能够将很复杂的操作用简单的语言来表达。我在工作中,常常把单元测试的不同的实例的创建用字符串来表达,这样测例的表达可以更加简洁。
Mediator
为了避免类之间的相互引用,把调用放到第三方的 Mediator。在 Android 中,EventBus 的概念有点像 Mediator。不过标准的 Mediator 是把类里调用之后 Trigger 的操作内容放到了 Mediator 的实现中。
Observer
用来管理类之间不同依赖关系的模式,和 Mediator 有点像。大致是多个 Subscriber 通过实现 Listener 接口来监听 subject 的变化。当 subject 改变时,需要调用 listeners 的 update 或者 notify 方法来通知 subscribers。不同的监听方式延伸出了针对事件编程的 reactive programming。
Memento
把当前类的实例的状态保存下来,并且提供根据保存的 Memento 恢复实例的能力。小到 Android 的 Intent,大到 Virtual Machine 做 Snapshot 都是一样的概念。
State
和 Memento 结合来看。Memento 其实就是 State 的简洁表达。假设类实例或者一个状态机,其行为是有当前的状态决定的,State 就是那个状态。
Strategy
Strategy 是将常用的实现打包成类供 Client 调用的方式。比如 Java 中对于 sort 可以定义不同类型的 Comparator 供 sort 使用。
Template
将固定化的流程共享,并且提供给用户(通常是子类)自定义部分实现的模式。从大处来讲,现在大行其道的各种 framework 就是 template,只需要用户填写自己需要控制的 business logic。
Iterator
用来遍历集合内容的模式,已经在不同的语言中大量使用了。每种集合类可以提供一个通用的 Iterator,Client 可以直接使用这个 Iterator 类去访问集合中的内容。这样一套 Client 的实现可以遍历不同类型的集合。
Visitor
为同一种类的类(实现了 accept visitor )提供新功能的方法,和 Iterator 结合起来使用相得益彰。比如,有的 Visitor 可以打印出类的内容,有的 Visitor 可以把类的内容求和等等。只需要写新的 Visitor,不需要改变被访问类,就可以添加新的访问方式。