从最简单的规则开始设计
这篇文章大致讲一些自己对于代码设计的理解,总结一些经验做一个小组的分享。
导引
为什么从最简单的规则开始呢?
因为其实我是在代码设计方面没有特别深入的研究,单纯的拿《 head first 设计模式》中的案例来充当分享内容有点不妥,我自己最近也还在不断学习。
另外就是,正如书中所说:
使用模式最好的方式是:“把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用它们。”以往是代码复用,现在是经验复用。
如何把模式装到脑子里?
可以通过看书来达成。最好还是看 《Head First 设计模式》,不要从网上看现成的文档,不要死记硬背。
书的组织形式很好
- 针对设计模式的应用场景进行分类,从对象,到装饰,到松耦合 OO,到封装,到适配和外观,到集合,到状态控制,到访问控制,有一整个的故事线,很有意思。
- 从简单的例子,到多模式的组合进阶使用,由易到难,由 demo 到生产;

书的讲述形式很好
不断的 Q&A,不断的比较,容易引发自己的思考,印象也就更深刻;
你会发现,即使同一个模式,在不同场景下也是有不同的使用方案的,甚至这些方案在某些情况下看起来是违背了一些总结出来的原则,比如“优先使用组合而不是继承”,举个例子适配器模式;如果没有这样的思考,很容易对某一个模式产生偏见和错误理解。

在你的设计和已有的应用中寻找他们
我也没有再花时间在我们的业务代码中找到具体的案例。
- 我也是到分享的时候才发现,自己其实没有特别深入的学习过,脑海里一下子过不来这些东西;
- 时间的问题;
- 也觉得没有必要,自己去寻找,切身体会才是最好的。我分享的案例,只是我个人站在当初需求背景下的设计,而设计的好坏和需求又相关,在不同背景下的思考见仁见智。
总结经验
既然上述两部分,都有更好的方案,那我就只能总结一下我个人最简单的经验。从比较务实的角度出发,将上层的理论交给更专业的方案去做。
我有哪些经验呢?毕竟我也是菜鸡,
所以我的经验也都比较简单,所以,还是回到题目,从最简单的规则开始,看看代码设计。
减少重复代码
这个可以说是最简单的规则了,没有之一。
毕竟,没有设计模式之前,至少也在强调,要实现代码复用。
以往是代码复用,现在是经验复用。
出现了大量的重复代码,那就不能实现代码复用,维护起来就得到处改。
重复的代码分类:
- 方法出现重复的实现
这个其实很容易理解,一个实现里面或者几个实现中,总是出现同样的方法块,idea 甚至都会帮助你识别出来。
比如我们业务中的统计逻辑,有好几处都要进行对模板信息的统计,但是是因为不同时期的功能,抄来抄去,没有再去整理,出现了大量的重复代码,维护起来,要改都得改。

这些重复的逻辑甚至都出现在同样的方法中,几百行的方法,随着迭代越来越多,不断追加,没有时间去重构来减少重复代码。
- 某个接口大量的实现类
重复的方法块出现,肯定是封装出现了问题。还有一类重复,就是出现了大量的实现类。还拿埋点逻辑来说,切入点的选择和切入的实现 这个接口出现了大量的实现。

这当然也是一个设计上的问题。大量的实现类反映接口在易用性和抽象上的弊端。想要实现新的切入点的成本高昂。
- 解决方案
我们需要重新来设计,以实现减少重复代码的目的。
方法块的重复,在不同的场景下有多种选择。
- 至少可以抽成公共的静态方法,带来的是稍微好维护;
- 也可以简单地进行抽象,使用继承,使用模板方法来处理;
- 也可以完全推倒原本的设计,实现一个针对模板的 parser,使用多个 parser 的组合来实现模板的读取和信息收集,对于其中的特殊的 parser 甚至还可以额外的抽象,比如针对控件列表的过滤,有些 parser 需要统计控件类型,有些 parser 需要统计个数,要让彼此独立却又在同一个便利中完成,那么就需要一个额外的抽象。
- 也可以 2 和 3 结合起来实现。
接口大量的实现类的出现,要减轻这样的重复工作,就要抽象出一个更加合理的基本实现类,将原本的实现类变成一个个基本实现类的实例。
抽象
进行抽象的时候,要先有具象。
这个具象不一定要已经有了实现,而是至少在你思考的过程中已经出现了的。
真正在处理业务的时候,可不像 demo 中那么简单。
一个苹果,一个梨,那么自然会想到水果。
要思考自身的业务分为哪几部分、哪些是可以归结到一个合理的结构上的。
基于不同的业务场合得到的抽象肯定也是完全不同的。
一个苹果一个梨,可能得到的是:
一种植物。
也可能是
一种拥有外皮,可以吃的,长在对应果树上的东西。
要结合具体的业务场景,找到最本质的部分,舍弃不关注的部分,最终得到一个抽象的结果。
扩展性思考
分层次的来思考扩展。
- 有扩展性的必要么?
要结合对应的业务场景来思考。
通常来说,涉及一个独立模块的功能,肯定是要考虑扩展性的。
但一个模块中的原本就具有可扩展性的一个扩展实现,就不需要考虑那么多,容易造成过度设计。
- 有热替换的必要么?能不能实现热替换?
要以松耦合的方式来实现。要依赖倒置,是不是要引入工厂方法,至少要有一个管理类来实现更新。
- 能不能实现改动替换?改动替换时需要改哪些东西?
改动替换时,是重新实现一个子类,还是针对已有的进行装饰,还是弄一个适配器。
这些在需要扩展的设计场景下要尽可能的思考到。让后续的扩展改动最小。
使用约定减少特定
一些设计模式是有固定使用场景的。不要在这些固定的使用场景下搞特定的设计。
比如 单例模式,虽然单例模式的写法很多,各种懒汉饿汉、双检索、枚举,但是他们对应的利弊和使用的场景一定要搞清楚。
还有一些其他好的代码方面的建议也是类似的。
比如针对 bean 的处理,getter setter 方法,开放的无参的构造器等等。
用于数据网络传输的 bean 和代码调用间的 bean 中间做一层转换,减少改动影响的范围。
比如,针对实例化时参数特别多的类,比如各种 bean,使用 builder 加上链式调用的方式来完成。
固定的场景,选择固定的模式,使用固定的原则。
泛型
合理使用泛型,让编译器帮助你发现问题。
单元测试
借助单元测试来设计,朝着让单元测试更容易写的方向优化设计。
得到一个合理的抽象,合理的模块化拆分。
总结
与其说我是来分享的,不如说我是来劝学的。
Author: yaohwu
Link: https://notes.yaohwu.xyz/2021/12/15/the-easiest-way-to-design-code/
License: 知识共享署名-非商业性使用 4.0 国际许可协议