设计模式之美

历经两个月的时间,终于看完了王争的《设计模式之美》,共73篇文章。

这本书还是很值得一读的,作者对设计理念、设计模式思考的比较深,而且将这些思想运用到实际的工作中,使设计模式显得不那么空;另外这本书花了很大的篇幅来讲设计的理念,讲的比较通透,这是很难得,因为无论哪种设计模式,最终都会映射回理念,设计理念才是根本性的东西。

通过阅读设计模式相关的书籍,现在已经完成了5篇文章:

Go设计模式(5)-类图符号表示法

Go设计模式(4)-代码编写优化

Go设计模式(4)-代码编写

Go设计模式(3)-设计原则

Go设计模式(2)-面向对象分析与设计

Go设计模式(1)-语法

后面也会进入设计模式文章的书写,这么做虽然导致学习进度慢了,但是能够将这些知识掌握的更加扎实,那就是值得的。

下面是做的读书笔记,大家有兴趣可以看一下,如果觉得合适,可以去极客时间购买全本。

读书笔记

开篇词

只会写能用的代码,我们永远成长不成大牛,成长不成最优秀的那批人

写好的代码是为未来节省时间

为什么说每个程序员都要尽早地学习并掌握设计模式相关知识

应对面试中的设计模式相关问题;

告别写被人吐槽的烂代码;

提高复杂代码的设计和开发能力;

让读源码、学框架事半功倍;

为你的职场发展做铺垫

从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?

可维护性:所谓“代码易维护”就是指,在不破坏原有代码设计、不引入新的bug的情况下,能够快速地修改或者添加代码。

可读性:需要看代码是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合等等

可扩展性:代码的可扩展性表示,我们在不修改或少量修改原有代码的情况下,通过扩展的方式添加新的功能代码。

灵活性:如果一段代码易扩展、易复用或者易用,我们都可以称这段代码写得比较灵活。所以,灵活这个词的含义非常宽泛,很多场景下都可以使用

简洁性:代码简单、逻辑清晰,也就意味着易读、易维护

可复用性:尽量减少重复代码的编写,复用已有的代码

可测试性:代码的可测试性差,比较难写单元测试,那基本上就能说明代码设计得有问题

面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?

理论一:当谈论面向对象的时候,我们到底在谈论什么?

面向对象编程是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石。

面向对象编程语言是支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。

理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?

封装:也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。

抽象:人类处理复杂性的有效手段

继承:继承最大的一个好处就是代码复用,用来表示类之间的is-a关系

多态:提高代码的扩展性和复用性

理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?

面向对象编程比起面向过程编程,更能应对这种复杂类型的程序开发;面向对象编程相比面向过程编程,具有更加丰富的特性(封装、抽象、继承、多态),利用这些特性编写出来的代码,更加易扩展、易复用、易维护;

理论四:哪些代码设计看似是面向对象,实际是面向过程的?

面向过程编程风格符合人的流程化思维方式;而面向对象编程风格正好相反,是一种自底向上的思考方式。

滥用getter、setter方法

Constants类、Utils类的设计问题

基于贫血模型的开发模式

理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?

抽象类实际上就是类,只不过是一种特殊的类,这种类不能被实例化为对象,只能被子类继承。我们知道,继承关系是一种is-a的关系,那抽象类既然属于类,也表示一种is-a的关系。相对于抽象类的is-a关系来说,接口表示一种has-a关系,表示具有某些功能。对于接口,有一个更加形象的叫法,那就是协议(contract)。

抽象类更多的是为了代码复用,抽象类能够优雅的保证多态特性

接口就更侧重于解耦

如果我们要表示一种is-a的关系,并且是为了解决代码复用的问题,我们就用抽象类;如果我们要表示一种has-a关系,并且是为了解决抽象而非代码复用的问题,那我们就可以使用接口。

理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?

基于接口而非实现编程”这条原则的另一个表述方式,是“基于抽象而非实现编程”。

越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。

理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

继承有诸多作用,但继承层次过深、过复杂,也会影响到代码的可维护性

继承主要有三个作用:表示is-a关系,支持多态特性,代码复用。而这三个作用都可以通过组合、接口、委托三个技术手段来达成。除此之外,利用组合还能解决层次过深、过复杂的继承关系影响代码可维护性的问题。

实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?

只包含数据,不包含业务逻辑的类,就叫作贫血模型

数据和对应的业务逻辑被封装到同一个类中,叫充血模型

基于贫血模型的传统的开发模式,重Service轻BO;基于充血模型的DDD开发模式,轻Service重Domain

实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?

对于支付这样的类似转账的操作,我们在操作两个钱包账户余额之前,先记录交易流水,并且标记为“待执行”,当两个钱包的加减金额都完成之后,我们再回过头来,将交易流水标记为“成功”。在给两个钱包加减金额的过程中,如果有任意一个操作失败,我们就将交易记录的状态标记为“失败”。我们通过后台补漏Job,拉取状态为“失败”或者长时间处于“待执行”状态的交易记录,重新执行或者人工介入处理。

实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?

面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程(OOP),是面向对象开发的三个主要环节

实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?

划分职责进而识别出有哪些类:罗列名词或者功能点

定义类及其属性和方法:我们识别出需求描述中的动词,作为候选的方法,再进一步过滤筛选出真正的方法,把功能点中涉及的名词,作为候选属性,然后同样再进行过滤筛选。

定义类与类之间的交互关系:泛化、实现、关联、聚合、组合、依赖,只保留了四个关系:泛化、实现、组合、依赖

将类组装起来并提供执行入口

面向对象分析的产出是详细的需求描述

面向对象设计的产出是类

理论一:对于单一职责原则,如何判定某个类的职责是否够单一?

类中的代码行数、函数或者属性过多;

类依赖的其他类过多,或者依赖类的其他类过多;

私有方法过多;

比较难给类起一个合适的名字;

类中大量的方法都是集中操作类中的某几个属性。

单一职责原则:一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性

实际上,在真正的软件开发中,我们也没必要过于未雨绸缪,过度设计。所以,我们可以先写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。这就是所谓的持续重构。一些常用判断指标:

理论二:如何做到对扩展开放、修改关闭?扩展和修改各指什么?

开闭原则:添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。关于定义,我们有两点要注意。第一点是,开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发。第二点是,同样的代码改动,在粗代码粒度下,可能被认定为“修改”;在细代码粒度下,可能又被认定为“扩展”。

最常用来提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式

理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?

里氏替换原则:子类对象能够替换程序(program)中父类对象出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。

多态与里氏替换原则的区别:多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,是用来指导继承关系中子类该如何设计的,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。

理论四:接口隔离原则有哪三种应用?原则中的接口该如何理解?

接口隔离原则:客户端不应该强迫依赖它不需要的接口

接口隔离原则与单一职责原则的区别:单一职责原则针对的是模块、类、接口的设计。接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。

理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?

通过依赖注入的方式来将依赖的类对象传递进来,这样就提高了代码的扩展性,我们可以灵活地替换依赖的类

控制反转(IOC):框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程。程序员利用框架进行开发的时候,只需要往预留的扩展点上,添加跟自己业务相关的代码,就可以利用框架来驱动整个程序流程的执行。这里的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程可以通过框架来控制。流程的控制权从程序员“反转”到了框架。

依赖注入(DI):不通过new()的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。

依赖反转原则(DIP):高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象(abstractions)来互相依赖。除此之外,抽象(abstractions)不要依赖具体实现细节(details),具体实现细节(details)依赖抽象(abstractions)。

理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?

要去设计当前用不到的功能

不要去编写当前用不到的代码

不要做过度设计

不要使用同事可能不懂的技术来实现代码

不要重复造轮子,要善于使用已经有的工具类库

不要过度优化

KISS原则:尽量保持简单

YAGNI原则:你不会需要它

理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?

减少代码耦合

满足单一职责原则

模块化

业务与非业务逻辑分离

通用代码下沉

继承、多态、抽象、封装

应用模板等设计模式

代码的实现逻辑是相同的,但语义不同,我们判定它并不违反DRY原则

代码的实现逻辑不重复,但语义重复,也就是功能重复,我们认为它违反了DRY原则

代码中存在“执行重复”,违反了DRY原则

DRY原则:Don’t-Repeat-Yourself,不要写重复的代码

三种典型的代码重复情况:实现逻辑重复、功能语义重复和代码执行重复

提高代码复用性方法

RuleofThree:第一次编写代码的时候,我们不考虑复用性;第二次遇到复用场景的时候,再进行重构使其复用

理论八:如何用迪米特法则(LOD)实现高内聚、松耦合?

高内聚:就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中

松耦合:在代码中,类与类之间的依赖关系简单清晰

高内聚低耦合

迪米特法则:不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口

实战一(上):针对业务系统的开发,如何做需求分析和设计?

面向对象设计聚焦在代码层面(主要是针对类),那系统设计就是聚焦在架构层面(主要是针对模块),两者有很多相似之处。

实战一(下):如何实现一个遵从设计原则的积分兑换系统?

分层能起到代码复用的作用

分层能起到隔离变化的作用

分层能起到隔离



转载请注明地址:http://www.jiankongxingye.com/jkhyjs/26845057.html
  • 上一篇文章:
  • 下一篇文章: