如何学习设计模式?

发布时间:
2023-08-24 12:45
阅读量:
18

我在Google等大厂工作多年,什么代码都见过,我个人觉得,在实际的项目开发中,设计模式是最没用的东西!初学者觉得设计模式是改善代码质量的杀手锏,实际在我看来,它应该是一把屠龙刀,用不好会适得其反!

我觉得,大家应该更加关注如何提高代码质量,这个更实用。从这几方面入手,任何一方面都比设计模式有用!

  • 面向对象
  • 设计原则
  • 设计模式
  • 编程规范
  • 重构技巧

  • 面向对象编程因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式等编码实现的基础。
  • 设计原则是指导我们代码设计的一些经验总结,对于某些场景下,是否应该应用某种设计模式,具有指导意义。比如,“开闭原则”是很多设计模式(策略、模板等)的指导原则。
  • 设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路。应用设计模式的主要目的是提高代码的可扩展性。从抽象程度上来讲,设计原则比设计模式更抽象。设计模式更加具体、更加可执行。
  • 编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范。
  • 重构作为保持代码质量不下降的有效手段,利用的就是面向对象、设计原则、设计模式、编码规范这些理论。

面向对象

现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程、面向对象和函数式编程。面向对象这种编程风格又是这其中最主流的。现在比较流行的编程语言大部分都是面向对象编程语言。大部分项目也都是基于面向对象编程风格开发的。面向对象编程因为其具有丰富的特性(封装、抽象、继承、多态),可以实现很多复杂的设计思路,是很多设计原则、设计模式编码实现的基础。

  • 面向对象的四大特性:封装、抽象、继承、多态
  • 面向对象编程与面向过程编程的区别和联系
  • 面向对象分析、面向对象设计、面向对象编程
  • 接口和抽象类的区别以及各自的应用场景
  • 基于接口而非实现编程的设计思想
  • 多用组合少用继承的设计思想
  • 面向过程的贫血模型和面向对象的充血模型

设计原则

设计原则是指导我们代码设计的一些经验总结。设计原则这块儿的知识有一个非常大的特点,那就是这些原则听起来都比较抽象,定义描述都比较模糊,不同的人会有不同的解读。所以,如果单纯地去记忆定义,对于编程、设计能力的提高,意义并不大。对于每一种设计原则,我们需要掌握它的设计初衷,能解决哪些编程问题,有哪些应用场景。只有这样,我们才能在项目中灵活恰当地应用这些原则。

对于这一部分内容,你需要透彻理解并且掌握,如何应用下面这样几个常用的设计原则。

  • SOLID原则-SRP单一职责原则
  • SOLID原则-OCP开闭原则
  • SOLID原则-LSP里式替换原则
  • SOLID原则-ISP接口隔离原则
  • SOLID原则-DIP依赖倒置原则
  • DRY原则、KISS原则、YAGNI原则、LOD法则


设计模式

设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路。大部分设计模式要解决的都是代码的可扩展性问题。设计模式相对于设计原则来说,没有那么抽象,而且大部分都不难理解,代码实现也并不复杂。这一块的学习难点是了解它们都能解决哪些问题,掌握典型的应用场景,并且懂得不过度应用。

经典的设计模式有23种。随着编程语言的演进,一些设计模式(比如Singleton)也随之过时,甚至成了反模式,一些则被内置在编程语言中(比如Iterator),另外还有一些新的模式诞生(比如Monostate)。

  • 创建型

常用的有:单例模式、工厂模式(工厂方法和抽象工厂)、建造者模式。

不常用的有:原型模式。

  • 结构型

常用的有:代理模式、桥接模式、装饰者模式、适配器模式。

不常用的有:门面模式、组合模式、享元模式。

  • 行为型

常用的有:观察者模式、模板模式、策略模式、职责链模式、迭代器模式、状态模式。

不常用的有:访问者模式、备忘录模式、命令模式、解释器模式、中介模式。


编程规范

编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具体、更加偏重代码细节。即便你可能对设计原则不熟悉、对设计模式不了解,但你最起码要掌握基本的编码规范,比如,如何给变量、类、函数命名,如何写代码注释,函数不宜过长、参数不能过多等等。

对于编码规范,考虑到很多书籍已经讲得很好了(比如《重构》《代码大全》《代码整洁之道》等)。而且,每条编码规范都非常简单、非常明确,比较偏向于记忆,你只要照着来做可以。它不像设计原则,需要融入很多个人的理解和思考。

除了编码规范,你还要了解一些代码的坏味道,让你知道什么样的代码是不符合规范的,应该如何优化。参照编码规范,你可以写出可读性好的代码;参照代码的坏味道,你可以找出代码存在的可读性问题。

代码重构

在软件开发中,只要软件在不停地迭代,就没有一劳永逸的设计。随着需求的变化,代码的不停堆砌,原有的设计必定会存在这样那样的问题。针对这些问题,我们就需要进行代码重构。重构是软件开发中非常重要的一个环节。持续重构是保持代码质量不下降的有效手段,能有效避免代码腐化到无可救药的地步。

而重构的工具就是我们前面罗列的那些面向对象设计思想、设计原则、设计模式、编码规范。实际上,设计思想、设计原则、设计模式一个最重要的应用场景就是在重构的时候。我们前面讲过,虽然使用设计模式可以提高代码的可扩展性,但过度不恰当地使用,也会增加代码的复杂度,影响代码的可读性。在开发初期,除非特别必须,我们一定不要过度设计,应用复杂的设计模式。而是当代码出现问题的时候,我们再针对问题,应用原则和模式进行重构。这样就能有效避免前期的过度设计。

对于重构这部分内容,你需要掌握以下几个知识点:

  • 重构的目的(why)、对象(what)、时机(when)、方法(how);
  • 保证重构不出错的技术手段:单元测试和代码的可测试性;
  • 两种不同规模的重构:大重构(大规模高层次)和小重构(小规模低层次)。

不仅仅是掌握一些重构技巧、套路,更重要的是建立持续重构意识,把重构当作开发的一部分,融入到日常的开发中。


有关设计模式、重构、编程规范等的经典书籍很多,基本上我都读过,下面这11本书,可谓是经典中的经典,如果你都能读完,代码能力会提高好几个层次!其中,一定要看到最后,最后一本最具实战性!

1.《设计模式》


学习设计模式,不知道GoF的《设计模式》估计会被人笑话的。这本书是设计模式的开山之作。经典的23种设计模式最早就诞生于这本书。这本书很薄,只有200多页。但是,我个人觉得,这本书还是比较晦涩难懂的。回想起来,我在读大学的时候,就读过几遍此书,但每次都是一知半解,读一遍忘一遍。如果你是设计模式的初学者,不建议从这本书看起。如果你对设计模式已经有所了解,还是蛮推荐你去看下这本经典书的。


2.《Head First设计模式》

如果说刚刚提到的《设计模式》是最经典的设计模式书籍,那《Head First设计模式》就是最通俗易懂的。这本书看起来很厚,但每页里的内容并没有那么密集。这本书最大的特点就是口语化、场景化。整本书围绕几个人的对话来展开。里面的例子比较脱离实践,但比较容易看懂。如果你之前对设计模式没有太多了解,这本书无疑是你的首选。


3.《Java与模式》

这本书可能并没有那么多人知道,出版的时间也比较久远,而且是跟Java语言比较紧耦合,里面很多例子都是在剖析JDK里的设计模式。不过这可能是最贴近实战的一本设计模式书籍了,里面包含的实战案例,应该是目前我读过的设计模式书籍中最多的。不过,我个人觉得文笔稍微有些晦涩,有些内容可能要反复读一下才能理解。如果你熟悉Java语言,在我推荐的其他书籍都看完的前提下,没事的时候可以看看这本书。


4.《深入浅出面向对象分析与设计》

这本书跟《Head Frist设计模式》都是出自“Head First”系列,写作风格也相同,不过也略显啰嗦。从书名中我们就可以看出,这本书主要是讲面向对象分析和设计。实际上,很多关于面向对象分析和设计书籍都侧重讲UML,讲得真的好的书籍却并不多。除此之外,我觉得面向对象分析和设计的理论知识并不多,关键还是实践。所以,如果你工作比较忙,看我的专栏就足够了。如果你特别想系统地学一下的话,看看这本书也完全足够了。


5.《代码大全》

这是一本有近千页大部头。不过,这本书讲的东西很杂,不是很聚焦,涵盖了软件开发方法、编程技巧、编码规范、重构等等诸多方面。书如其名,这本书其实更像是软件开发方面的工具类的百科全书。在读过我推荐的其他书籍之后,这本书快速地翻一遍就可以了。


6.《代码整洁之道》

这本书非常值得推荐。它主要是讲编码规范,除此之外,还讲到了一些有关设计原则、单元测试、并发编程的东西。因为内容比较侧重编码规范,所以每个知识点都非常明确,能够很容易落地指导你的开发,能够立竿见影地改善你的代码质量。


7.《编写可读代码的艺术》

从书名我们就可以看出,本书主要是教你如何写出可读性好的代码,实际上也是在讲比较偏向细节的编码规范。它的内容跟《代码整洁之道》有部分重复,但是推荐你也看一下。《代码整洁之道》《代码大全》《编写可读代码的艺术》三本书是讲编码规范方面的三大著作。看完这三本书,基本的编码规范你就掌握全了。


8.《重构》

这本书的作者是Martin Fowler,他写了很多跟软件开发相关的经典书籍。这本《重构》无疑是他最经典的作品。书中讲到了诸多代码的坏味道,并且给出了相应的改进方法,是作者一手开发经验的总结输出。我推荐本书的原因倒不是说书里面的内容有多真知灼见、让人耳目一新,而是这本书的内容总结得非常全面,很适合帮你去做一个整体、系统的梳理。


9.《重构与模式》

我们之前讲过,设计模式一个重要的应用场景就是代码重构。这本书主要讲如何应用设计模式来重构代码,改善代码设计。如果说《重构》是讲如何做低层次的重构,那这本书就是在讲如何做高层次的重构,也就是我们专栏中要讲到的小重构和大重构。这本书非常推荐你读一下,它能让你知道,为什么要用设计模式,如何有的放矢地应用设计模式,而非只是无痛呻吟。


10.《修改代码的艺术》

如果说《重构》那本书是从编码规范上来讲如何重构,《重构和模式》是从设计模式上来讲如何重构,那这本书可以粗鲁地归为从面向对象设计思想、设计原则上来讲重构。除此之外,这本书更偏向于教你如何来重构,不像上面两本书那样聚焦,所以是一个很好的补充。这三本书合起来称为重构“三部曲”。这本书的示例代码是用C++写的,不过也很容易看懂,很推荐你读一下。


11、《设计模式之美》

前Google工程师写的,也就是我啦,不装了~ 豆瓣评分9.2,好评如潮!囊括了编写高质量代码的方方面面,可以说是,一本在案,代码不烂!

尽管本书的书名为《设计模式之美》,但是书名有点“以偏概全”,因为本书不仅仅讲解设计模式,而是以编写高质量代码为主旨,系统讲解了与此有关的5个方面:面向对象编程范式、设计原则、代码规范、重构技巧和设计模式。

尽管市面上有很多讲解如何编写高质量代码的图书,但是大部分图书为了在简短的篇幅内将知识点讲清楚,大多选择比较简单的代码示例,这就导致很多读者在读完这些图书之后,虽然感觉理论知识都懂了,但仍然不知道如何将理论知识应用到真实的项目开发中。因此,在本书写作的过程中,我竭尽全力解让本书的讲解更加贴近实战。

在权衡篇幅和学习效果的情况下,对于每个知识点,我都结合真实的项目代码来做讲述,并且,侧重讲解本质的或贴近应用的知识,比如,为什么会有这种设计模式?它用来解决什么样的编程问题?在应用时有何利弊需要权衡?等等。让读者知其然,知其所以然,并学会应用。

《设计模式之美》目录
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
设计模式学习导读(3)
1. 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
2. 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
3. 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想(37)
面向对象
4. 理论一:当我们在谈论面向对象的时候,我们到底在谈论什么?
5. 理论二:封装、抽象、继承、多态分别能解决哪些编程问题?
6. 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
7. 理论四:有哪些代码设计看似是面向对象,实际是面向过程风格的?
8. 理论五:接口vs抽象类的区别?如何用普通类语法模拟接口和抽象类?
9. 理论六:为何要基于接口而非实现编程?有必要为每个类都定义接口吗?
10. 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11. 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12. 实战一(下):如何用基于充血模型的DDD开发一个虚拟钱包系统?
13. 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14. 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则
15. 理论一:对于单一职责原则,如何判定一个类的职责是否够”单一“?
16. 理论二:如何做到”对扩展开放、修改关闭“?如何设计可扩展代码?
17. 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18. 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19. 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20. 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21. 理论七:重复代码就一定违背DRY吗?如何提高代码的复用性?
22. 理论八:如何利用迪米特法则(LOD)实现“高内聚、松耦合”?
23. 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24. 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25. 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26. 实战二(下):如何实现一个支持各种统计规则的性能计数器?
规范与重构
27. 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28. 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29. 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30. 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31. 理论五:让你最快速地改善代码质量的20条编程规范(上)
32. 理论六:让你最快速地改善代码质量的20条编程规范(中)
33. 理论七:让你最快速地改善代码质量的20条编程规范(下)
34. 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35. 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36. 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37. 实战二(下):重构ID生成器项目中各函数的异常处理代码
总结课
38. 总结回顾:面向对象、设计原则、编程规范、重构技巧的核心要点
39. 用学过的设计原则和思想完善之前讲的性能计数器项目(上)
40. 用学过的设计原则和思想完善之前讲的性能计数器项目(下)
设计模式与范式(34)
创建型
41. 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?
42. 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?
43. 单例模式(下):如何实现多例模式和集群下的分布式单例模式?
44. 工厂模式(上):我为什么说没事不要随便使用工厂模式创建对象?
45. 工厂模式(下):如何设计实现一个Dependency Injection框架?
46. 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式
47. 原型模式:如何最快速地clone一个HashMap散列表?
结构型
48. 代理模式:代理模式在RPC、缓存、监控等场景中的应用
49. 桥接模式:如何实现支持不同类型和渠道的消息推送系统?
50. 装饰器模式:通过剖析Java IO类库源码来学习装饰器模式
51. 适配器模式:代理、适配器、桥接、装饰,这四个模式有何区别?
52. 门面模式:如何设计合理的接口粒度以兼顾接口的易用性和通用性?
53. 组合模式:如何设计实现支持递归遍历的文件系统目录树结构?
54. 享元模式(上):如何利用享元模式优化文本编辑器的内存占用?
55. 享元模式(下):剖析享元模式在Java Integer、String中的应用
行为型
56. 观察者模式(上):详解各种应用场景下观察者模式的不同实现方式
57. 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?
58. 模板模式(上):剖析JDK、Servlet、Junit,学习模板模式的两大作用
59. 模板模式(下):模板模式与callback回调函数有何区别和联系?
60. 策略模式(上):如何避免冗长的if-else/switch分支判断代码?
61. 策略模式(下):如何实现一个可以给不同大小文件排序的小程序?
62. 职责链模式(上):如何实现可灵活扩展算法的敏感信息过滤框架?
63. 职责链模式(下):框架中常用的过滤器、拦截器是如何实现的?
64. 状态模式:游戏、工作流引擎中常用的状态机是如何实现的?
65. 迭代器模式(上):相比直接遍历集合数据,使用迭代器有哪些优势?
66. 迭代器模式(中):在遍历集合的同时为什么不能增删集合元素?
67. 迭代器模式(下):如何设计实现一个支持”快照“功能的iterator?
68. 访问者模式(上):手把手带你还原访问者模式诞生的思维过程
69. 访问者模式(下):为什么支持双分派的语言就不需要访问者模式?
70. 备忘录模式:对于大对象的备份和恢复,如何优化内存和时间的消耗?
71. 命令模式:如何利用命令模式加轮训机制搭建手游后端架构?
72. 解释器模式:如何设计实现一个自定义接口告警规则功能?
73. 中介模式:什么时候用中介模式?什么时候用观察者模式?
总结课
74. 总结回顾:23种经典设计模式的原理、核心思想和应用场景
75. 在实际的项目开发中,如何避免过度设计?又如何避免设计不足?
开源与项目实战(26)
开源实战
76. 开源实战一(上):通过剖析Java JDK源码学习灵活应用设计模式
77. 开源实战一(下):通过剖析Java JDK源码学习灵活应用设计模式
78. 开源实战二(上):从Unix开源开发学习应对大型复杂项目开发
79. 开源实战二(中):从Unix开源开发学习应对大型复杂项目开发
80. 开源实战二(下):从Unix开源开发学习应对大型复杂项目开发
81. 开源实战三(上):借Google Guava学习发现和开发通用功能模块
82. 开源实战三(中):剖析Google Guava中用到的几种设计模式
83. 开源实战三(下):借Google Guava学习三大编程范式中的函数式编程
84. 开源实战四(上):剖析Spring框架中蕴含的经典设计思想或原则
85. 开源实战四(中):剖析Spring框架中用来支持扩展的两种设计模式
86. 开源实战四(下):总结Spring框架中用到的11种设计模式
87. 开源实战五(上):MyBatis如何权衡易用性、性能和灵活性?
88. 开源实战五(中):如何利用职责链与代理模式实现MyBatis Plugin?
89. 开源实战五(下):总结MyBatis框架中用到的10种设计模式
项目实战
90. 项目实战一:设计实现一个支持各种算法的限流框架(分析)
91. 项目实战一:设计实现一个支持各种算法的限流框架(设计)
92. 项目实战一:设计实现一个支持各种算法的限流框架(实现)
93. 项目实战二:设计实现一个通用的接口幂等框架(分析)
94. 项目实战二:设计实现一个通用的接口幂等框架(设计)
95. 项目实战二:设计实现一个通用的接口幂等框架(实现)
96. 项目实战三:设计实现一个支持自定义规则的灰度发布组件(分析)
97. 项目实战三:设计实现一个支持自定义规则的灰度发布组件(设计)
98. 项目实战三:设计实现一个支持自定义规则的灰度发布组件(实现)
总结课
99. 总结回顾:在实际软件开发中常用的设计原则和思想、模式和范式
100. 如何将设计模式、设计原则、设计思想等理论知识应用到实战?
专栏加餐(10)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍和资料推荐
加餐三 | 聊一聊Google是如何做Code Review的?
加餐四 | 聊一聊Google那些让我快速成长的地方
加餐五 | 听一听小争哥对Google工程师文化的解读
加餐六 | 什么才是所谓的编程能力?如何考察一个人的编程能力?
加餐七 | 基础学科知识如何转化成实际的技术生产力?
加餐八 | 程序员怎么才能让自己走得更高、更远?
加餐九 | 作为面试官或候选人,如何面试或回答设计模式问题?
加餐十 | 如何接手一坨烂业务代码?如何在烂业务代码中成长?
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
结束语(1)
结束语 | 聊一聊能力、机遇、运气、方向、努力!

辛苦码了这么多字,如果对你有帮助,点赞收藏一下呗,这是我努力输出的动力!

欢迎大家关注我,一周三更(数据结构与算法、刷题、面试、Java、系统设计、架构),关注我不错过精彩内容

@前Google工程师小争哥

END