提升代码质量的方法:领域模型、设计原则、设计模式

简介: 我们可以列举出非常多质量差的代码的表现现象,其中最影响代码质量的两个表现是命名名不副实、逻辑可扩展性差,当一个新人阅读代码时,有时发现方法命名与实际逻辑对不上,这就让人感到非常疑惑,这种现象在平时工作并不少见;另一个就是逻辑扩展性差,一个新业务需求提出来后,发现要在多处改动,需要回归的业务逻辑比较多,造成研发效率不高。

image.png

作者 | 不拔
来源 | 阿里技术公众号

一 影响代码差的根因

1 差代码的体现

我们可以列举出非常多质量差的代码的表现现象,如名字不知所意、超大类、超大方法、重复代码、代码难懂、代码修改困难……其中最为影响代码质量的两个表现是命名名不副实、逻辑可扩展性差,当一个新人阅读代码时,有时发现方法命名与实际逻辑对不上,这就让人感到非常疑惑,这种现象在平时工作并不少见;另一个就是逻辑扩展性差,一个新业务需求提出来后,发现要在多处改动,需要回归的业务逻辑比较多,造成研发效率不高。

2 问题归纳

对第1节中提到的现象进行问题归纳整理,大致整理出6类问题,分别展开加以说明。

  • 命名问题:命名问题是一件非常头疼的事,想要取一个名副其实又好理解的名字并不那么容易。涉及到变量的命名、方法的命名、类命名,常见的命名问题有两种:一种是不知所云;另一种是名不副实。命名不知所云是一个人初一看,不知道它是什么意思,根本原因就是没有想到一个合适的词汇去抽象问题;命名名不副实是命名和实际逻辑想表达的意思不一样,这样的命名会误导人。
  • 代码结构问题:当一个人初看工程代码时,当还没有深入看代码逻辑时,从模块划分、类划分、方法划分整体上可以感受得出代码质量,如果一个类有几千行代码,一个方法有几百行,这样的逻辑相信没有多少人愿意去看,复杂度比较高。好的代码层次结构非常清晰,就像看一本优美的书一样有一种赏心悦目的感觉。
  • 编程范式问题:有三种编程范式:表模式、事务脚本模式和领域设计模式,大家用得最多的是事务脚本模式,这种模式最符合人做事的方法,step by step,这种模式最大的问题就是承担了不该自己承担的职责,看起来比较符合逻辑,实际上问题比较多,平时大家喜欢称之为"面条型代码"。
  • 可读性问题:代码除了实现业务功能外,还要具备良好的可读性,有的代码没有任何注释;有的代码格式不统一;有的是为了炫耀技术,大段大段的Lambda表达式(并不是说Lambda表达式不好,关键要控制层次深度),这样的代码看起来简洁,可读性并不太好。
  • 扩展性问题:可扩展性问题是一个老生常谈的问题,要实现良好的可扩展性并不那么容易,一般是没有抽象问题,如店铺在店招头展示Tab,面条型的代码就是直接定义一个List,然后往里面加Tab对象,如果需要再加一个Tab怎么办?典型的就是不满足开闭原则。
  • 无设计问题:整个代码看起来比较平淡,别人看了之后也从中学习不到内容。一般这种问题是没有深入分析问题,仅仅解决了问题,而没有考虑如何更好地解决问题,比如重复处理流程的工作是否可以抽象成一个通用的模板类、不同处理类是否可以通过工厂类去获取具体的策略、异步处理是否可以使用事件模式去处理、对于新增加的能力能否通过自动注册去发现……

3 根因分析

接下来分析下为什么会产生代码差的原因,这个问题有外部原因,也有内部原因。外部原因主要有:项目排期急,没有多少时间去设计;资源短缺,人手不够,只能怎么快怎么来;紧急问题修复,临时方案快速处理……。内部原因主要有:自身技能低,怎么技能没有掌握到,如Lamda表达式、常用的工具类、框架高级用法等;无极致追求的精神,仅仅完成需求就行,稳定性、可扩展性、性能、数据一致性等没有考虑……

笔者认为最为关键的是内部自身的问题,根因就两个:自我要求不高;无反馈通道。如果对自已要求不高,仅仅满足完成需求开发就止步了,很难写出高质量的代码,另外如果没有外部反馈,也难以提高自己的技能。笔者之前的主管非常严厉,对大家写的代码review比较仔细,一个变量名、一段逻辑的写法,反复让修改,这其实是提升技能最快的方法。

image.png

二 提升代码质量的方法

提升代码质量的方法,笔者喜欢用三个方法:领域建模、设计原则、设计模式,主要谈下如何使用。

  • 分析阶段:当拿到一个需求时,先不要着急想着怎么把这个功能实现,这种很容易陷入事务脚本的模式。分析什么呢?需要分析需求的目的是什么、完成该功能需要哪些实体承担,这一步核心是找实体。举个上面进店Tab展示的例子,它有两个关键的实体:导航栏、Tab,其中导航栏里面包含了若干个Tab。
  • 设计阶段:分析完了有哪些实体后,再分析职责如何分配到具体的实体上,这就要运用一些设计原则去指导,GRASP中提到一些职责分配的原则,感兴趣的同学可以去详细看看。回到上面的例子上,Tab的职责主要有两个:一个是Tab能否展示,这是它自己的职责,如上新Tab展示的逻辑是店铺30天内有上架新商品;另一个职责就是Tab规格信息的构建,也是它自己要负责的。导航栏的职责有两个:一个是接受Tab注册;另一个是展示。职责分配不适理,也就不满足高内聚、低耦合的特征。
  • 打磨阶段:这个阶段选择合适的模式去实现,大家一看到模式都会理解它是做什么的,比如看到模板类,就会知道处理通用的业务流程,具体变化的部分放在子类中处理。上面的这个例子,用到了2个设计模式:一个是订阅者模式,Tab自动注册的过程;另一个是模板模式,先判断Tab能否展示,然后再构建Tab规格信息,流程虽然简单,也可以抽象出来通用的流程出来,子类只用简单地重写2个方法。

image.png

三 领域模型的作用

领域建模的入门门槛比较高,包含了一些难理解的概念。本篇文章中并不会讲述如何进行建模(可以私下交流),笔者发现让大家接受领域建模远比知道如何建模更重要,当你知道了领域建模的作用后,自己会想各种办法去学习。下面通过笔者经历的一些实际案例进行阐述,让大家听起来并不感觉到那么空洞。

1 简化认识

笔者工作一年后加入到了一家金融公司,当时对金融一无所知,开始接触到标的、债权、债权转让、融资担保、非融资担保等名词后,一时感到无所适从,每天要学习非常多的新内容。

两个月后,我的主管给我们做了一次分享,就拿了一张ppt来讲,它里面包含了领域的实体,以及实体之间的关联关系,一下子我就知道了整个业务是怎么玩转的。模型的作用就是简化人对事物的认识,如果一开始我们就陷入到代码细节中,很难看到业务的全貌,而且代码是为了实现业务能力,当你知道了业务之后,再去看代码就会快得多。

2 统一认识

在公司里,有研发、产品、运营、测试……,当我们在一起交流的时候,大家默认的语言是不统一的,开发经常讲怎么操作这张数据库表,产品经常讲业务模式……这就导致大家的认识并不统一。

那是一个晚上,刚和交互同学确认完交互流程后,突然她问了一个问题:把相似的页面让卖家移到同一个文夹中,这个好实现吧?听完后告知不能,交互同学一听说这很合理呀,怎么实现不了?开始给她讲了下现有的系统流程,发现她听得一脸懵逼,马上发现问题了,我是用开发的语言在描述问题,立马换了一种方式,找了一支笔和一张纸,给交互同学画了我们的领域模型是什么,业务实体之间的交互是怎样的,一讲完后,交互同学马上明白了为什么不能实现的原因所在了。

3 指导设计

有的同学觉得领域建模偏空洞,比较虚,其实除了能够简化认识和统一认识外,领域建模还可能指导代码设计,比如上面举的店铺导航Tab的例子,笔者就是通过领域建模来设计的,虽然它是一个小的需求,并不妨碍领域建模的运用。在下图中,可以清晰的看到,导航栏包含了若干个Tab,一个Tab包含规格信息和点击操作信息。把这个业务模式画出来之后,对应的代码中也会有上面的概念,现实与代码之间存在映射关系,模型即代码,代码即模型。如果你的模型不能反映现实,模块只能算是一个花架子,范钢老师对此总结了三句话:现实有什么事物,对应有什么对象;现实事物有什么行为,对应对象有什么方法;现实事物有什么联系,对应对象有什么关联。

image.png

四 设计原则的底层逻辑

1 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,抽象不依赖于细节,而细节依赖于抽象。

2 为什么要有设计原则

我们对SOLID原则基本上听说过或者了解过,但为什么要有这些设计原则呢?为了回答这个问题,我们从目标往下推导下。软件开发的目标是高内聚、低耦合,这句挂在嘴边的话,发现很难衡量,比如要回答:什么样的叫高内聚?什么样的叫低耦合?高内聚要高到什么程度?低耦合要低到什么程度?这四个问题并不太好回答。

反过来想想,如果我们的代码不是高内聚和低耦合的会怎样?也即是低内聚和高耦合的场景。如果代码是低内聚和高耦合,则会出现修改一个逻辑,会导致多处代码要修改,这个并不是我们希望看到的,尤其在修改原有的逻辑,很容易出现bug,比如笔者之前修改一个问题,改了另外一处的规则,看起来是没有问题,结果影响到了一个业务方,这也是为什么开闭原则提出对修改关闭的原因,修改原有的逻辑是有风险的。

理想的情况是修改只限定在某个局部范围内,这样影响的范围有限,因此我们要求逻辑要单一,不要包含多个职责。再往下思考下:为什么我们要修改呢?除了原有逻辑有bug要修复、代码重构外,一个重要的原因是需求发生了变化,是变化导致我们要对原有的逻辑进行修改。如果没有修改的场景,也就没有所谓的高内聚、低耦合之说了。因此设计原则的底层逻辑就是让软件能够较好地应对变化,降本增效。

3 如何落地实践

设计原则只是一个指导的方针,离落地实践还有很大的一段距离,就像有些同学说设计原则我懂了,但我依然运用不到。实际上这个问题的本质还是对设计原则的底层逻辑没有理解,没有洞察出变化关注点,怎么解决这个问题呢?设计模式给出的答案:找到变化、封装变化。

image.png

五 设计模式的本质

设计模式请参考笔者之前写的文章。

image.png

六 案例实践

当调用的接口有不同的实现时(入参、出参、接口都不相同),需要抽象出一层防腐层,怎么去实现呢?接下来分别看2个案例,这2个案例的侧重点不一样,一个是偏行为的抽象,一个是偏结构的抽象。

1 店铺品牌查询

店铺需要查询店铺品牌信息,然而Lazada和AE的接口是不一样的,怎么抽象防腐层呢?

首先最简单的方案很容易想到,就是定义一个接口,然后有两个实现。它的优点是层次简单,大家基本看了就懂。它的缺点也是明显的,在两个实现类中,职责不一单一,承担了两个职责:一个是实现店铺品牌的查询,另一个是数据转换。

根据方案一提到的缺点,很容易想到使用适配器模式,将之前的类拆成两个类:一个类是调用对应的品牌服务;另一个类做数据适配转换。不过此时的方式还有一个缺点就是在国际化场景下,要考虑多租户之间的隔离,比如Lazada有多个站点,如何实现更细粒度的差异呢?方案三基于这些的思考就产生了。

方案三是引入了多租户框架,能够支撑多租户场景。

image.png

2 店铺优惠券查询

有一种"万金油"式开发模式:组装参数、调用接口、解析响应结果,你会发现这种模式太万能了,适合所有的场景,这样的开发模式也即是"事务脚本模式"或者"面条型代码"。

优惠券查询的案例,用领域建模的模式,首先思考有哪些实体。优惠券查询的本质:通过xx条件查询返回满足条件的优惠券集合。对于优惠券来讲,有两类信息至关重要。一个是优惠券的规格信息,如优惠券名称、优惠金额、有效期等;另一个是优惠券的限制条件。在查询的时候,是查店铺优惠券,还是查粉丝优惠券,或者是查询商品优惠券……。因此分开两部分抽象优惠券:一个是优惠券查询请求;另一个是优惠券规格实体。

如果按照这样的设计,有一个缺点是业务方理解复杂度会上升,它是偏底层实现,没有做到使用简单。优惠券偏产品交付而非仅仅功能交付。因此在底层实现之上,再抽象出产品组件,这样业务方使用起来就比较简单。

image.png

原文链接
本文为阿里云原创内容,未经允许不得转载。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/512625.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SphereEx 完成近千万美元 Pre-A 轮融资,连接企业数据与应用,构建新一代数据库生态引擎

2022年1月4日,创新型数据库基础软件提供商 SphereEx 宣布完成近千万美元 Pre-A 轮融资,本轮融资由嘉御资本领投,红杉中国种子基金、初心资本、指数创投跟投。指数资本担任独家财务顾问。这是继 2021 年 5 月份以来,SphereEx 完成的…

Javascript 机器学习的四个层次

简介: Atwood定律说,凡是可以用Javascript实现的应用,最终都会用Javascript实现掉。作为最热门的机器学习领域,服务端是Python的主场,但是到了手机端呢?Android和iOS里默认都没有Python。但是有浏览器的地方…

运维监控再添新品,F5联合智维数据推出应用质量主动拨测解决方案

一直以来,业务的稳定性和客户体验是企业各个部门都关注的重点,也是企业数字化转型的重要支撑。但在实际的运维环境中,如果只从服务端的视角进行监控,往往会出现数据中心内部监控体系显示设备状态正常,但却收到了客户端…

排查指南 | 两个案例学会从埋点排查 iOS 离线包

简介: 首次打开离线包白屏以及报错“-1009”等该如何处理呢? 离线包原理 以一次启动离线包的流程为例,离线包的加载流程分为两种场景,第一种是离线包下载好的场景,流程如图1所示,第二种是离线包没下载好的…

如何设计可靠的灰度方案

简介: 一个较大的业务或系统改动,往往会影响整个产品的用户体验或操作流程。为了控制影响面,可以选取一批特定用户、流程、单据等,只允许这一部分用户或数据按照变更后的新逻辑在系统中流转,而另一部分用户仍然执行变更…

linux如何设置mac快捷键,在Ubuntu上使用macOS的快捷键

因为常用机一台Mac,一台Linux,都频繁使用,两个系统不同的快捷键已经让人精神分裂了!macOS几乎所有的快捷键都基于command键,全选(cmda)、复制(cmdc)、粘贴(cmdv)、开关标签页(cmdt/w)、切换窗口(cmdtab)、保存(cmds)、…

IDC报告:阿里云领跑中国数据库市场年度份额首超传统厂商

简介: IDC报告显示,2020年中国关系型数据库软件市场规模达到121.8亿元,同比增长36.5%。其中,以公有云模式部署的关系型数据库市场占比达到51.5%,首次超过传统线下部署模式市场规模,预计到2025年将继续走高&…

稳若磐石的「云上奥运」背后,是云计算新界面的崛起

简介: 今年奥运会首次采用阿里云支撑全球转播,这是奥运迈入数字时代的重要一步。对于这届特殊的奥运会来说,技术至关重要。我们相信,这次创造历史的实践,将为未来支撑更多体育爱好者把“云观赛”转变为参与国际体育赛事…

10分钟!构建支持10万/秒请求的大型网站

简介: 应用网关作为应用的统一接入层,它的发展和演进也是伴随着应用架构的变化,大家都知道企业应用从最早期 SOA 时代发展到微服务的时代。在 SOA 时代,传统的企业服务总线承担了企业应用的统一接入层;但是发展到微服务…

服务实体经济、战略级行业再下一城,钉钉发布制造行业解决方案2.0

编辑 | 宋慧 出品 | CSDN 云计算 1月5日,钉钉召开主题为“数字新生”的2022制造业钉峰会。会上,钉钉正式发布制造行业解决方案2.0,该方案以“码上制造”产品为制造行业专属底座,提供设备上钉、计件日结等基础产品,同时…

阿里云边缘云全新架构升级,助力CDN操控新体验

简介: 本次升级根据上万企业客户的使用反馈和行业应用特征,从简单开通到个性化定制,从内容分发到边缘计算完整解决方案,对客户侧的使用体验进行了全局梳理和全链路优化,推进边缘云CDN操控革新,并逐步构建面…

key redis 遍历_快乐运维Redis大数据量查询与清理

一、 keys命令(生产环境禁止使用)简单粗暴,由于Redis单线程这一特性,keys命令是以阻塞的方式执行的,keys是以遍历的方式实现的复杂度是 O(n),Redis库中的key越多,查找实现代价越大,产生的阻塞时间越长。key…

linux部分基础命令总结,Linux常用基础命令总结

近期自己学习了一下Linux,写这篇博客以便于对自己的一个总结,记录自己的学习情况,奥利给!想对Linux熟练掌握,就必须学会它的操作命令,虽然可能会花费一些时间,不过从长远的角度来说,…

MaxCompute执行引擎核心技术DAG揭秘

简介: 作为业界少有的EB级数据分布式平台,MaxCompute每天支撑上千万个分布式作业的运行。这些作业特点各异,既有包含数十万计算节点的超大型作业,也有中小规模的分布式作业。不同用户对于不同规模/特点的作业,在运行时…

自定义导航页_带你回归“真”的导航页

曾经浏览器主页是我们流向各大网站的要道从这里我们可以轻而易举的去往想要去的网站特别是在信息和专业性越来聚集的今天传统的主页难免令人有些审美疲劳或者说花里胡哨hao1232345是这些老牌的主页网站有些审美疲劳随着导航越来越细节化广告也越来越多美感也更少360主页qq主页百…

Redis 压缩列表原理与应用分析

作者 | 西瓜来源 | JAVA架构进阶之路摘要Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件。它支持字符串、列表、哈希表、集合、有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支持不同的编码方式。…

基于 MaxCompute 的智能推荐解决方案

简介: 在互联网行业红利已过、在获客成本越来越高、在用户在线时长全网基本无增长以及信息大爆炸的情况下,如何更好的转化新用户和提升老用户粘性就变得至关重要,智能化的个性化推荐无疑是经过验证的重要手段之一,我们每天使用的移…

基于MaxCompute+开放搜索的电商、零售行业搜索开发实践

简介: 搜索一直是电商行业流量来源的核心入口之一,如何搭建电商行业搜索并提升搜索效果,一直是电商行业开发者努力攻克的难题。基于传统数据库或开源引擎虽然能够搭建基础搜索服务,但随着商品数据的增多和业务流量的增长&#xff…

快速搭建实验环境:使用 Terraform 部署 Proxmox 虚拟机

作者 | Addo Zhang来源 | 云原生指北自从用上 m1 的电脑,本地开发环境偶尔会遇到兼容性的问题。比如之前尝试用 Colima 在虚拟机中运行容器运行时和 Kubernetes,其实际使用的还是 aarch64 虚拟机,实际使用还是会有些差异。手上有台之前用的黑…

linux grub 下载,GRUB 2.04发布下载,附新功能介绍

GRUB 2.04版本发布了,它是在GRUB 2.02/2.00的基础上更新的,GRUB 2.02是目前使用得最多的多重启动管理器,全称为GRand Unified Bootloader,使用它可以引导几乎所有的操作系统,包括Unix、Linux、Windows,GRUB…