免费刷推广链接的网站,科技小巨人申报条件,网站开发课程介绍,百度收录文章简介#xff1a;提升代码质量的三个有效方法#xff1a;领域建模、设计原则、设计模式。 影响代码差的根因
差代码的体现
我们可以列举出非常多质量差的代码的表现现象#xff0c;如名字不知所意、超大类、超大方法、重复代码、代码难懂、代码修改困难……其中最为影响代码…简介提升代码质量的三个有效方法领域建模、设计原则、设计模式。 影响代码差的根因
差代码的体现
我们可以列举出非常多质量差的代码的表现现象如名字不知所意、超大类、超大方法、重复代码、代码难懂、代码修改困难……其中最为影响代码质量的两个表现是命名名不副实、逻辑可扩展性差当一个新人阅读代码时有时发现方法命名与实际逻辑对不上这就让人感到非常疑惑这种现象在平时工作并不少见另一个就是逻辑扩展性差一个新业务需求提出来后发现要在多处改动需要回归的业务逻辑比较多造成研发效率不高。
问题归纳
对前文提到的现象进行问题归纳整理大致整理出 6 类问题分别展开加以说明。
命名问题命名问题是一件非常头疼的事想要取一个名副其实又好理解的名字并不那么容易。涉及到变量的命名、方法的命名、类命名常见的命名问题有两种一种是不知所云另一种是名不副实。命名不知所云是一个人初一看不知道它是什么意思根本原因就是没有想到一个合适的词汇去抽象问题命名名不副实是命名和实际逻辑想表达的意思不一样这样的命名会误导人。代码结构问题当一个人初看工程代码时当还没有深入看代码逻辑时从模块划分、类划分、方法划分整体上可以感受得出代码质量如果一个类有几千行代码一个方法有几百行这样的逻辑相信没有多少人愿意去看复杂度比较高。好的代码层次结构非常清晰就像看一本优美的书一样有一种赏心悦目的感觉。编程范式问题有三种编程范式表模式、事务脚本模式和领域设计模式大家用得最多的是事务脚本模式这种模式最符合人做事的方法step by step这种模式最大的问题就是承担了不该自己承担的职责看起来比较符合逻辑实际上问题比较多平时大家喜欢称之为 面条型代码。可读性问题代码除了实现业务功能外还要具备良好的可读性有的代码没有任何注释有的代码格式不统一有的是为了炫耀技术大段大段的 Lambda 表达式并不是说 Lambda 表达式不好关键要控制层次深度这样的代码看起来简洁可读性并不太好。扩展性问题可扩展性问题是一个老生常谈的问题要实现良好的可扩展性并不那么容易一般是没有抽象问题如店铺在店招头展示 Tab面条型的代码就是直接定义一个 List然后往里面加 Tab 对象如果需要再加一个 Tab 怎么办典型的就是不满足开闭原则。无设计问题整个代码看起来比较平淡别人看了之后也从中学习不到内容。一般这种问题是没有深入分析问题仅仅解决了问题而没有考虑如何更好地解决问题比如重复处理流程的工作是否可以抽象成一个通用的模板类、不同处理类是否可以通过工厂类去获取具体的策略、异步处理是否可以使用事件模式去处理、对于新增加的能力能否通过自动注册去发现……
根因分析
接下来分析下为什么会产生代码差的原因这个问题有外部原因也有内部原因。
外部原因主要有项目排期急没有多少时间去设计资源短缺人手不够只能怎么快怎么来紧急问题修复临时方案快速处理……。内部原因主要有自身技能低怎么技能没有掌握到如 Lamda 表达式、常用的工具类、框架高级用法等无极致追求的精神仅仅完成需求就行稳定性、可扩展性、性能、数据一致性等没有考虑……
笔者认为最为关键的是内部自身的问题根因就两个自我要求不高无反馈通道。如果对自已要求不高仅仅满足完成需求开发就止步了很难写出高质量的代码另外如果没有外部反馈也难以提高自己的技能。笔者之前的主管非常严厉对大家写的代码 review 比较仔细一个变量名、一段逻辑的写法反复让修改这其实是提升技能最快的方法。 提升代码质量的方法
提升代码质量的方法作者喜欢用三个方法领域建模、设计原则、设计模式主要谈下如何使用
分析阶段当拿到一个需求时先不要着急想着怎么把这个功能实现这种很容易陷入事务脚本的模式。分析什么呢需要分析需求的目的是什么、完成该功能需要哪些实体承担这一步核心是找实体。举个上面进店 Tab 展示的例子它有两个关键的实体导航栏、Tab其中导航栏里面包含了若干个 Tab。设计阶段分析完了有哪些实体后再分析职责如何分配到具体的实体上这就要运用一些设计原则去指导GRASP 中提到一些职责分配的原则感兴趣的同学可以去详细看看。回到上面的例子上Tab 的职责主要有两个一个是 Tab 能否展示这是它自己的职责如上新 Tab 展示的逻辑是店铺 30 天内有上架新商品另一个职责就是 Tab 规格信息的构建也是它自己要负责的。导航栏的职责有两个一个是接受 Tab 注册另一个是展示。职责分配不适理也就不满足高内聚、低耦合的特征。打磨阶段这个阶段选择合适的模式去实现大家一看到模式都会理解它是做什么的比如看到模板类就会知道处理通用的业务流程具体变化的部分放在子类中处理。上面的这个例子用到了 2 个设计模式一个是订阅者模式 Tab 自动注册的过程另一个是模板模式先判断 Tab 能否展示然后再构建 Tab 规格信息流程虽然简单也可以抽象出来通用的流程出来子类只用简单地重写 2 个方法。领域模型的作用
领域建模的入门门槛比较高包含了一些难理解的概念。本篇文章中并不会讲述如何进行建模可以私下交流笔者发现让大家接受领域建模远比知道如何建模更重要当你知道了领域建模的作用后自己会想各种办法去学习。下面通过笔者经历的一些实际案例进行阐述让大家听起来并不感觉到那么空洞。
简化认识
笔者工作一年后加入到了一家金融公司当时对金融一无所知开始接触到标的、债权、债权转让、融资担保、非融资担保等名词后一时感到无所适从每天要学习非常多的新内容。
两个月后我的主管给我们做了一次分享就拿了一张 ppt 来讲它里面包含了领域的实体以及实体之间的关联关系一下子我就知道了整个业务是怎么玩转的。模型的作用就是简化人对事物的认识如果一开始我们就陷入到代码细节中很难看到业务的全貌而且代码是为了实现业务能力当你知道了业务之后再去看代码就会快得多。
统一认识
在公司里有研发、产品、运营、测试……当我们在一起交流的时候大家默认的语言是不统一的开发经常讲怎么操作这张数据库表产品经常讲业务模式……这就导致大家的认识并不统一。
那是一个晚上刚和交互同学确认完交互流程后突然她问了一个问题把相似的页面让卖家移到同一个文夹中这个好实现吧听完后告知不能交互同学一听说这很合理呀怎么实现不了开始给她讲了下现有的系统流程发现她听得一脸懵我立刻意识到我是用开发的语言在描述问题立马换了一种方式找了一支笔和一张纸给交互同学画了我们的领域模型是什么业务实体之间的交互是怎样的一讲完后交互同学马上明白了为什么不能实现的原因所在了。
指导设计
有的同学觉得领域建模偏空洞比较虚其实除了能够简化认识和统一认识外领域建模还可能指导代码设计比如上面举的店铺导航 Tab 的例子笔者就是通过领域建模来设计的虽然它是一个小的需求并不妨碍领域建模的运用。
在下图中可以清晰的看到导航栏包含了若干个 Tab一个 Tab 包含规格信息和点击操作信息。把这个业务模式画出来之后对应的代码中也会有上面的概念现实与代码之间存在映射关系模型即代码代码即模型。如果你的模型不能反映现实模块只能算是一个花架子范钢老师对此总结了三句话现实有什么事物对应有什么对象现实事物有什么行为对应对象有什么方法现实事物有什么联系对应对象有什么关联。 设计原则的底层逻辑
SOLID
对于设计原则一般我们会谈到 SOLID它包含了五个设计原则
单一职责原则A class should have one, and only one, reason to change一个类只能因为一个理由被修改。开闭原则Entities should be open for extension, but closed for modification对扩展开放对修改关闭。里氏替换原则Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it子类可以替换父类。接口隔离原则A client should not be forced to implement an interface that it doesn’t use不能强制客户端实现它不使用的接口应该把接口拆的尽可能小。依赖倒置原则Abstractions should not depend on details. Details should depend on abstractions抽象不依赖于细节而细节依赖于抽象。
为什么要有设计原则
我们对 SOLID 原则基本上听说过或者了解过但为什么要有这些设计原则呢为了回答这个问题我们从目标往下推导下。软件开发的目标是高内聚、低耦合这句挂在嘴边的话发现很难衡量比如要回答什么样的叫高内聚什么样的叫低耦合高内聚要高到什么程度低耦合要低到什么程度这四个问题并不太好回答。
反过来想想如果我们的代码不是高内聚和低耦合的会怎样也即是低内聚和高耦合的场景。如果代码是低内聚和高耦合则会出现修改一个逻辑会导致多处代码要修改这个并不是我们希望看到的尤其在修改原有的逻辑很容易出现 bug比如笔者之前修改一个问题改了另外一处的规则看起来是没有问题结果影响到了一个业务方这也是为什么开闭原则提出对修改关闭的原因修改原有的逻辑是有风险的。
理想的情况是修改只限定在某个局部范围内这样影响的范围有限因此我们要求逻辑要单一不要包含多个职责。再往下思考下为什么我们要修改呢除了原有逻辑有 bug 要修复、代码重构外一个重要的原因是需求发生了变化是变化导致我们要对原有的逻辑进行修改。如果没有修改的场景也就没有所谓的高内聚、低耦合之说了。因此设计原则的底层逻辑就是让软件能够较好地应对变化降本增效。
如何落地实践
设计原则只是一个指导的方针离落地实践还有很大的一段距离就像有些同学说设计原则我懂了但我依然运用不到。实际上这个问题的本质还是对设计原则的底层逻辑没有理解没有洞察出变化关注点怎么解决这个问题呢设计模式给出的答案找到变化、封装变化。 设计模式的本质 案例实践
当调用的接口有不同的实现时入参、出参、接口都不相同需要抽象出一层防腐层怎么去实现呢接下来分别看 2 个案例这 2 个案例的侧重点不一样一个是偏行为的抽象一个是偏结构的抽象。
店铺品牌查询
店铺需要查询店铺品牌信息然而 Lazada 和 AE 的接口是不一样的怎么抽象防腐层呢
首先最简单的方案很容易想到就是定义一个接口然后有两个实现。它的优点是层次简单大家基本看了就懂。它的缺点也是明显的在两个实现类中职责不一单一承担了两个职责一个是实现店铺品牌的查询另一个是数据转换。
根据方案一提到的缺点很容易想到使用适配器模式将之前的类拆成两个类一个类是调用对应的品牌服务另一个类做数据适配转换。不过此时的方式还有一个缺点就是在国际化场景下要考虑多租户之间的隔离比如 Lazada 有多个站点如何实现更细粒度的差异呢方案三基于这些的思考就产生了。
方案三是引入了多租户框架能够支撑多租户场景。 店铺优惠券查询
有一种万金油式开发模式组装参数、调用接口、解析响应结果你会发现这种模式太万能了适合所有的场景这样的开发模式也即是事务脚本模式或者面条型代码。 优惠券查询的案例用领域建模的模式首先思考有哪些实体。优惠券查询的本质通过 xx 条件查询返回满足条件的优惠券集合。对于优惠券来讲有两类信息至关重要。一个是优惠券的规格信息如优惠券名称、优惠金额、有效期等另一个是优惠券的限制条件。在查询的时候是查店铺优惠券还是查粉丝优惠券或者是查询商品优惠券……。因此分开两部分抽象优惠券一个是优惠券查询请求另一个是优惠券规格实体。
如果按照这样的设计有一个缺点是业务方理解复杂度会上升它是偏底层实现没有做到使用简单。优惠券偏产品交付而非仅仅功能交付。因此在底层实现之上再抽象出产品组件这样业务方使用起来就比较简单。
原文链接
本文为阿里云原创内容未经允许不得转载。