美团:怎样写出漂亮整洁的代码?聊聊clean code的编码、重构技巧( 四 )


整个代码基于接口去组织,会很自然地变得非常清晰易读 。关注实现的人才去看实现,不是嘛?
正确使用继承和组合
这也是个在业界被讨论过很久的问题,也有很多论调 。最新观点是组合的使用一般情况下比继承更为灵活,尤其是单继承的体系里,所以倾向于使用组合,否则会让子类承载很多不属于自己的职能 。
个人对此观点持保留意见,在我经历过的代码中,有一个小规律,我分析一下 。
protected abstract 这种是最值得使用继承的,父类保留扩展点,子类扩展,没什么好说的 。
protected final 这种方法,子类是只能使用不能修改实现的 。一般有两种情况:

  1. 抽象出主流程不能被修改的,然而一般情况下,public final更适合这个职能 。如果只是流程的一部分,需要思考这个流程的类归属,大部分变成public组合到其他类里是更合适的 。
  2. 父类是抽象类无法直接对外提供服务,又不希望子类修改它的行为,这种大多数情况下属于工具方法,比较适合用另一个领域对象来承载并用组合的方式来使用 。
protected 这种是有争议的,是父类有默认实现但子类可以扩展的 。凡是有扩展可能的,使用继承更理想一些 。否则,定义成final并考虑成组合 。
综上所述,个人认为继承更多的是为扩展提供便利,为复用而存在的方法最好使用组合的方式 。当然,更为大的原则是明确每个方法的领域划分 。
代码复用技巧模板方法
这是我用得最多的设计模式了 。每当有两个行为类似但又不完全相同的代码段时,我总是会想到模板方法 。提取公共流程和可复用的方法到父类,保留不同的地方作为abstract方法,由不同的子类去实现 。
并在合适的时机,pull method up(复用)或者 pull method down(特殊逻辑) 。
最后,把不属于流程的、但可复用的方法,判断是不是属于基类的领域职责,再使用继承或者组合的方法,为这些方法找到合适的安家之处 。
extract method
很多复用的级别没有这么大,也许只是几行相同的逻辑被copy了好几次,何不尝试提取方法(private) 。又能明确方法行为,又能做到代码复用,何乐不为?
责任链
经常看到这样的代码,一连串类似的行为,只是数据或者行为不一样 。如一堆校验器,如果成功怎么样、失败怎么样;或者一堆对象构建器,各去构造一部分数据 。碰到这种场景,我总是喜欢定义一个通用接口,入参是完整的要校验/构造的参数,出参是成功/失败的标示或者是void 。然后有很多实现器分别实现这个接口,再用一个集合把这堆行为串起来 。最后,遍历这个集合,串行或者并行的执行每一部分的逻辑 。
这样做的好处是:
  1. 很多通用的代码可以在责任链原子对象的基类里实现;
  2. 代码清晰,开闭原则,每当有新的行为产生的时候,只需要定义行的实现类并添加到集合里即可;
  3. 为并行提供了基础 。
为集合显式定义它的行为
集合是个有意思的东西,本质上它是个容器,但由于泛型的存在,它变成了可以承载所有对象的容器 。很多非集合的类,我们可以定义清楚他们的边界和行为划分,但是装进集合里,它们却都变成了一个样子 。不停地有代码,各种循环集合,做一些相似的操作 。
其实很多时候,可以把对集合的操作显示地封装起来,让它变得更有血有肉 。
例如一个Map,它可能表示一个配制、一个缓存等等 。如果所有的操作都是直接操作Map,那么它的行为就没有任何语义 。第一,读起来就必须要深入细节;第二,如果想从获取配置读取缓存的地方加个通用的逻辑,例如打个log什么的,你可以想象是多么的崩溃 。
个人提倡的做法是,对于有明确语义的集合的一些操作,尤其是全局的集合或者被经常使用的集合,做一些封装和抽象,如把Map封装成一个Cache类或者一个config类,再提供GetFromCache这样的方法 。
经验总结本文从clean code的几个大前提出发,然后提出了实践clean code的一些手段,重点放在促成clean code的一些常用编码和重构技巧 。
当然,这些只代表笔者本人的一点点感悟 。好的代码,最最需要的,还是大家不断追求卓越的精神 。欢迎大家一起探索交流这个领域,为clean code提供更多好的思路与方法 。
侵权提醒必删

【美团:怎样写出漂亮整洁的代码?聊聊clean code的编码、重构技巧】


推荐阅读