理论篇|如何避免写出面条代码?
阿里妹导读
本文是“面条代码系列”文章的第一篇,在这个系列里,我将从理论开始,再通过实践和大家一起来探讨如何才能写好代码。
面条代码系列文章目的
什么是好代码
面条代码的由来
人员签到的需求:
* 当签到时间>班次开始时间N分钟后,签到状态为迟到
* N根据不同的业务可以灵活配置,比如:门店为5,配送为0
根据上面的需求,再来看一下我们的实现代码:
public void 人员签到(班次ID,人员ID) {
班次 = 班次Repo.获取班次(班次ID)
迟到可容忍时间 = 配置服务.获取可容忍时间(业务域);
签到状态 = null;
if( 签到时间 > (班次.开始时间 +迟到可容忍时间)) {
签到状态 = 迟到;
} else {
签到状态 = 正常;
}
//更新数据库状态
}
看完上面的实现代码,大家想一下,如果让你来写,你会怎么写呢?如果大家没有看出问题来,那很抱歉,我可以说大家平时就是在写面条代码。
迟到可容忍时间 = 配置服务.获取可容忍时间(业务域);
问题一:在签到方法内需要去查询配置服务,我相信这样的场景会出现在我们大量的方法中,有时候一个操作需要查询大量的其它服务来准备数据,慢慢的我们的方法会越来越长,即便我们可以把这些查询封装在一个方法中,但其实不解决实质性的问题。
if( 签到时间 > (班次.开始时间 +迟到可容忍时间)) {
签到状态 = 迟到;
} else {
签到状态 = 正常;
}
问题二:签到的业务逻辑揉合在签到方法中,形成了面向过程式的代码片段,随着业务的不断变化,签到方法有可能会越来越复杂。
如何解决面条代码的问题
值对象(Value Object)
无副作用方法(Side-Effect-Free Function)
语义化接口(Intention-Revealing Interface)
模式一:值对象(Value Object)
模式二:无副作用方法(Side-Effect-Free Function)
你说你的方法是无副作用方法,我凭什么相信你?
很多时候,一个看似“无副作用”的方法,调用了方法A,方法A再调用方法B,再调用方法C,方法D,方法E,最后在方法E中更新了一下数据库状态。所以你有什么底气回答上面的问题?凭什么?可能有读者觉得我是在抬杠,当然不是,我是在讲一个严肃的架构问题。好的架构,是能让你的系统中能产生一个像“重力”一样的东西,就像建筑学中的“重力”你必须得去遵守它。好的软件架构,也是一样的,能够创造出一个像“重力”一样的规则,让你不得不遵守它。请看下面的定义:
只有把无副作用方法,放到值对象上以后,它才是可被相信的“无副作用”方法。
值对象本身从严格意义上来说是一个不可变对象,我们只能替换它,而不能改变它。同时在值对象当中,我们是无法访问其它service的。基于这两个前提,放在值对象上的“无副作用”方法,就是完完全全可被信任的方法,这样的方法是最好的资产,可以被其它同学放心的复用。
public class 班次 {
班次配置;
开始时间;
结束时间;
签到状态判定(签到时间) {
if( 签到时间 > (开始时间 +班次配置.迟到可容忍时间)) {
return 迟到;
} else {
return 正常;
}
}
}
上面代码中的方法就是长在“值对象”上的一个“无副作用”方法,这样的方法是明确抽象了业务逻辑的方法,是最好的可复用资产。
模式三:语义化接口(Intention-Revealing Interface)
看到类或方法,别人就知道这个类或方法是做什么的
如何给方法取名字?如果你觉得是因为你英文不好导致的取不出好名字,那你又错了。取名字和英文好坏没关系,因为我可以很负责任的和你说,就算你用中文给方法取名字,你还是无法通过名称来表达出方法真正做的事情。因为这是人与人之间的共识问题,你不可能通过一个名称来和别人达成共识,达成共识是一个很复杂的过程,通常要经过沟通、确认,再沟通再确认,多次沟通后,人与人之间才能达成某种共识。在写代码时,我们不可能针对每一个我需要重用的方法,找写这个方法的人一遍又一遍的沟通和确认,那还不如我自己看一下方法里面的逻辑,或者自己重写一个,这才是我们今天面临的真正问题,因为无法达成共识,导致代码很难被重用。
要解决这个问题,我们要引入第三个模式,语义化接口(Intention-Revealing Interface),语义化接口是指用业务正在使用的语言来为我为接口或方法命名,而业务正在使用的语言,我们在MRD、PRD阶段已经为此达成过共识了,这是业务、产品、开发和测试都能理解的,你也可以把它理解成DDD中的“通用语言”。如果领域层的模型和模型上的方法都是“语义化”的,那我相信,这样的代码是可被理解,并且没有歧义的,使用方可以去放心使用的。
总结
模式一:值对象(Value Object)
模式二:无副作用方法(Side-Effect-Free Function)
模式三:语义化接口(Intention-Revealing Interface)
当然,这是一个非常简单的例子,我在这里只是通过这个例子表达我的观点,实际项目中的需求比这复杂的多的多的多,这是这个系列的第一篇,理论篇,后面我将会通过实际的例子,更加详细的说明我们是如何把这个理论运用到我们日常的代码中的。
微信扫码关注该文公众号作者