1.1 谈谈设计模式

2021/11/20 系统设计 共 3058 字,约 9 分钟

谈谈设计模式

1. 面向对象设计原则与共识

1.1. 三大特性

  1. 继承
  2. 封装
  3. 多态

1.2. 六大原则

  • 开闭原则(Open Close Principle)

对扩展开放,对修改关闭

  • 里氏代换原则(Liskov Substitution Principle)
  • 依赖倒转原则(Dependence Inversion Principle)

依赖抽象而不依赖具体

  • 接口隔离原则(Interface Segregation Principle)

用多个隔离的接口,比使用单个接口要好

  • 迪米特法则(最少知道原则)(Demeter Principle)

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立

  • 合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承

2. 设计模式:可复用『面向对象』软件的基础

划重点:设计模式的前提是面向对象,简单和清晰的实现往往都会取代复杂的实现,多范式的编程语言可以使用不同的特性来简化设计并减少不必要的抽象

img

2.1. 23种设计模式

aa29438fe1ff4acaae312d221ecc1a8e

0dfa437757b1467e9fb734d9cf3a9aa1

9a20e8cbb80b4f3ea7cd80d6abce19b4

7a8b064ec3474a3482c1db4a2f8bf5d0

[注]迭代器模式应该是行为型模式而不是创建型模式

2.2. 设计模式之间的关系

design_pattern.jpg

[注]一张图无法完全阐述设计模式之间的关系,但是对于设计模式的实现组合也有有一定指导意义。

2.3. Design Patterns in the JDK

Adapter:

This is used to convert the programming interface/class into that of another.

java.util.Arrays#asList()
javax.swing.JTable(TableModel)
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()

Decorator:

Attach additional responsibilities to an object dynamically and therefore it is also an alternative to subclassing. Can be seen when creating a type passes in the same type. This is actually used all over the JDK, the more you look the more you find, so the list below is definitely not complete.

java.io.BufferedInputStream(InputStream)
java.io.DataInputStream(InputStream)
java.io.BufferedOutputStream(OutputStream)
java.util.zip.ZipOutputStream(OutputStream)
java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()

Observer:

Used to provide a way for a component to flexibly broadcast messages to interested receivers.

java.util.EventListener
javax.servlet.http.HttpSessionBindingListener
javax.servlet.http.HttpSessionAttributeListener
javax.faces.event.PhaseListener

更多,详见:design-patterns-in-jdk

3. 使用设计模式正确姿势

img

3.1. 引入设计 模式需要权衡的

  1. 需要额外引入哪些新的概念。要明白一段代码涉及的概念越多,就越难理解。
  2. 设计模式自身的实现复杂度
  3. 一旦引入后,为了保持设计所带来的灵活性,后续开发需要注意的地方。是否能通过代码层面的限制来保证灵活性不会被破坏。
  4. 团队中其他人对这个设计模式的理解程度
  5. 对排错调试,代码静态分析可能造成的影响 (例如Observer模式和Visitor模式往往会打乱静态分析,难以通过阅读代码确定执行状态)

3.2. 设计模式校验思路

如果用了一些设计模式,减少了继承,那大概率用对了;

大部分设计模式,是让你在在面向对象的基础上尽量消除继承的手段。所以,如果你用了一些设计模式,减少了继承,那你八成用对了。如果你用了一大堆设计模式,然而继承却越来越频繁,那你100%用错了。之所以说大部分,是因为个别设计模式(比如享元模式)是为了解决特殊场景特殊问题而生的。

如果用了一些设计模式,单元测试好写了,那大概率用对了;

一个设计合理的系统,因为解耦充分,各个模块独立性强,所以单元测试应该是比较容易写的。如果你用了一大堆设计模式,却发现给你写的类编写单元测试用例非常困难,那你一定是用错了。

如果用了一些设计模式,项目的if更少了,那大概率用对了;

多态的本质是运行期动态决定程序的分支走向,也就是“更好的if”,而设计模式,至少是《设计模式》那本书中提到的那些模式,基本上是基于多态的。所以如果你合理的利用设计模式,你设计出的代码应该有较少的if,如果你的代码越使用设计模式if越多,或者更直观地说,缩进越多,你一定犯了错误。

有些模式是很容易用错的,比如visitor模式;

有些模式是很容易用错的,比如visitor模式,其实是为了解决java不支持double dispatch而存在的,然而其逻辑很晦涩。所以当你还在怀疑自己是否用对了设计模式的时候,你不应该使用这样的模式。

4. 观点:

除非你还在写 Java,否则设计模式真没多大用处。

  1. 如果你用的语言能把类型像变量一样赋值并传来传去,很多创建型模式就没用了。
  2. 如果你用的语言能把函数像变量一样赋值并传来传去,很多行为模式就没用了。
  3. 如果你用的语言 style 反对叠床架屋的 class hierarchy,很多结构模式就没用了。
  4. 如果函数能像变量一样赋值和传递,Command、Strategy 等模式就没用了。
  5. EventHandler/ConcreteEventHandler 这种 callback API 风格在现代语言里基本绝迹,C++ 有 std::function、C# 有 delegate、连 Java 8 都有了 Lambda,更别说动态语言了。
  6. 有的语言有全局变量,Singleton 模式就没用了。Singleton 是最简单也最被滥用的模式。
  7. 有的语言有 multiple dispatch,Vistor 模式就没用了。其实 Vistor 模式一直就没啥用

作者:陈硕 链接:https://www.zhihu.com/question/23757906/answer/25567356 来源:知乎

Design pattern with FP

img

5. 总结

  1. 如果模式的使用违背了直觉,降低了代码的可读性(即使对不懂模式的人来说),那一定是不优雅的

  2. 设计是有成本的,为“使用”而使用设计模式,其实是反模式的

  3. 敏捷开发模式,有一句话:不要过度设计,第一次开发完成功能就好,第二次迭代开发再考虑扩展性

参考文档

知乎:如何正确的试用设计模式

设计模式目录

圣杯与银弹 · 没用的设计模式

design-patterns-in-jdk

Table of Contents