扫码阅读
手机扫码阅读

小对象,大作用,好优雅!

351 2023-07-25

(笔者将以Java语言为例)

一:问题初现

在工作中,笔者发现,开发同学们或多或少都会遇到这些问题:

针对同种功能的字段,需要重复提供边界校验,甚至干脆不校验。很多常见的解决方式是这样的:校验用注解直接写在接受前端参数的“VO对象”里。

public class UserVO { @NotBlank(message = "name不能为空") private String name; @NotBlank(message = "描述不能为空") private String description;}

太不优雅,太不体面了!

作为追求工程卓越的精致打工人,就该高效优雅、又快又好地实现功能,然后合理的摸鱼。

就在笔者苦思冥想之际,突然一到灵光击中了脑壳,这不就是《重构》理论中的霰弹式修改吗!?

含义:
牵一发而动全身,每次遇到某种变化,都必须在不同的类中做出小修改
坏处:
代码散步各处,不利于扩展和阅读,增加代码修改难度及工作量
目标:
尽量使某类变化通过某个特定类来处理,避免修改过多类。提升代码可扩展性,减少不必要的工作量。
《重构》 ,关于霰弹式修改

于是,修修改改、一番折腾之后,小对象就诞生了:

public class Name { private String value;}
public class Description { private String value;}

二:“初见”小对象

面向对象九式中早有提及:“戒条三:封装所有的原生类型和字符串”。

基本类型的表达能力是非常有限的,

public class User { private String id; private String name; /** 密码 **/ private String pd;}

如果不看字段的名字,谁也不知道这个String字符串是干啥的,它只有作为通用字符串的标准行为。如果字段命名再不妥当一点,就得再加注释去弥补它、解释它,属实是有一点“叠床架屋”了。就例如这个pd。

有一句话说得好:“ 伟大的代码可以被一年级CS学生轻松读懂 ”,我们不谈伟大,好的代码也应该保证其相应的易读性。

大家都清楚,字段的命名很重要,好的命名是成功的一半,但同样的,其类型也同样重要。类型是对字段最强有力的解释,它可以生动的表达这个字段的意义。这样就可以不需要用注解去“欲盖弥彰”。

当我们把一个实体的所有字段全部用小对象封装起来后,这个对象就生动了起来,活生生地站在了我们面前。

public class User { private Id id; private Name name; private Password password;}
public class Id { private String value;}
public class Name { private String value;}
public class Password { private String value;}


三:小对象里的天地

小对象往往是面向对象的隐喻。开发者需要更专注于实体与其属性的状态与行为,而不是简单的纯数据处理——后者是过程化的思考。

小对象以及由小对象构成的实体、聚合,可以真实地对真实世界模型做很好的映射。

但是小对象里可不只是单纯的对基本类型的封装。一般整个业务系统里,名称的长度限制和编码的长度限制可能是不同的,但所有的名称的长度限制都是相同的,所有的编码的长度限制都是相同的。

所以,回到一开始的问题,倘若我们把字段的长度限制做到小对象里去呢?

public class Name { private String value;  public Name(String value) { this.value = value; verify(); }  private void verify() { if (StringUtils.isEmpty(value) || value.length > 10) { throw new IllegalArgumentException("名字不能大于10个字符。"); } }}
public class Password { private String value;  public Password(String value) { this.value = value; verify(); // encoding,略 }  private void verify() { if (StringUtils.isEmpty(value) || value.length > 16) { throw new IllegalArgumentException("密码不能超过16个字符。"); } }}

是不是问题就迎刃而解了?很直观,很优雅。

而且,它还可以顺带解决另一个问题;避免Utils的滥用。例如系统中有名字的概念,所有name字段都可以被输出成"姓 + 名"或"名 + 姓"两种方式,这种业务就可能会带来NameUtils的使用。但这个模型就很“贫血”。为什么Name自己不能按照格式输出,而是非要一个Utils帮忙做输出呢?Utils在系统中应该少用,最好不用。

如果有了个Name的小对象,Name自己就可以做到这个行为了,赞!


四:近而深究其意义

小对象可以组成对真实模型的隐喻,可以让实体更生动、活泼,更还有很多意义存在。

笔者试图总结了一下:

1. 它践行了分离关注的思想,把边界、异常处理的责任放在对应的业务对象里,把工作的重心放在需要交付的业务逻辑上。

2. 业务代码更集中,不会分散到controller(web层)和前端视图对象中。

3. 代码整洁干净,不会有一堆注解

4. 不依赖于框架(例如spring-validation)。团队升级框架或随时移除框架时不会影响任何业务代码。而使用框架可能会导致业务代码流失的情况

5. 提高交付速率



笔者也不是纸上谈兵、夸夸其谈,笔者也和团队伙伴在真实项目中实践,并证明了其卓有成效。这个理念已经融合进了瀚城软件研发的开源企业应用级框架里了,感兴趣的小伙伴可以clone下来看看????

https://github.com/highsoft-shanghai/hare

(基于Java+SpringBoot的实践)

原文链接: http://mp.weixin.qq.com/s?__biz=Mzg5NTAzNjU3Mg==&mid=2247483698&idx=1&sn=15664d14c1c1834d61dfd996f41b0068&chksm=c0173cc1f760b5d7fb8b76bca1af136af4f9c6009be7f29fdf858fb7751d7c11c956903dc823#rd

软件技术分享,包括敏捷、系统架构、软件技术、代码质量等内容。个人观点,不喜勿喷

1 篇文章
浏览 352
加入社区微信群
与行业大咖零距离交流学习
软件研发质量管理体系建设 白皮书上线