代码诊所的第二次诊断

几年前,我有机会负责一个项目的咨询。团队很小,目标是对旧有系统的后端用Java改写,而团队的开发人员全为C程序员。我的工作职责是负责项目设计、开发,以及担任项目开发过程敏捷化的教练,并培养Java开发人员。

我在团队工作室的墙角落,开了一个小小的诊所,广而告之——“每日一贴,包治百病”。这是当时我在项目上的第二次诊断。

01

变量的声明应尽量与使用放在一起

本规则与代码的可读性有关,倘若方法还没有保持短小,这个问题就更要命。或许这是C语言开发者容易犯的毛病。当然也有许多Java程序员从前辈程序员处继承了这一陋习。我曾经在一个遗留项目中看到过一个长达几千行的Java方法,在方法头部堆砌了数十个变量定义,让人目不暇给!

除了影响代码的可读性之外,还可能导致隐藏的缺陷。很多程序员之所以习惯在一开始就声明变量,就是将这种局部变量当做了存储中间状态的容器,在方法内部反复使用该变量,这种中间结果的变迁未必符合开发者意图,又或者后来的代码维护者没有理清这种变化,从而做出变量值的误判。

02

对常量和枚举的使用

本规则本不足道,写在这里,为了进一步惊醒一下团队成员。在咨询过程中,我看到有这段代码:

Integer.parseInt(freeFlash, 16);

这个16,究竟是什么鬼?Magic Number,很多时候会让人感到困惑。

在JDK没有提供枚举之前,很多Java程序员喜欢使用接口类型来包装一大堆常量。如果常量存在内聚的分类意义,还是使用枚举为佳。

03

进行合理封装,避免方法调用顺序错误

封装是非常有必要的。有时候,暴露太多的细节会让调用者感到无可适从。

对于TelnetService类,我们需要依序调用connect()、login()、enterUShell(),然后在执行命令后,必须依序执行exitUShell(),disconnect()。这让我想起事务处理,FTP访问等与资源有关的逻辑,都需要在执行逻辑前后包裹一些基础设施的处理逻辑。为了避免在执行命令前后忘记连接或断开telnet,最好能将此过程封装。

这是从调用安全性来考虑。

如果从调用的简洁性考虑,封装亦有必要。当我们需要通过TelnetService发送telnet命令时,为何还需要了解内部的执行逻辑呢?

那么,该如何封装才能两全其美,既满足对执行逻辑顺序的重用,又满足对命令逻辑的扩展?

通常做法是将真正的执行逻辑提取为接口,如Java中Runnable的方式。这其实可以看作Command模式的运用。当然,我更愿意看做是对函数的封装,例如Guva中的tranform()、filter()之类的方法,接受更具有函数气质的Function或者Predicate接口(当时,Java 8还未问世呢)。

因此,我的做法如下:

public class TelnetService {
    public T withCommand(ExecutionCommand<T> command) {
        connect();
        login();
        enterUShell();
        T result = command.send();
        exitUShell();
        disconnect();
        return result;
    }
}

可以这样调用:

String result = telnetService.withCommand(new ExecutionCommand<String>() {
        @Override
        public String send() {
            return telnetService.transfer();
        }
    }
);

04

遵循异常处理的架构规则

团队成员对异常极为陌生,面对java的受控异常、非受控异常,不知如何选择;也不知道该何时捕获异常,何时抛出异常。因而我针对该项目确定了异常处理的架构原则,其目的是为了让整个架构变得更简单,让异常处理更加一致。

我的目的是减轻开发人员的负担,但同时又不降低代码质量,并利于未来对代码的维护。规则如下:

请输入标题 bcdef

同层之间的调用不做try-catch,上层调用下层的对象,必须try-catch。即使对象抛出了异常,只要不是checked exception(我们尽量避免使用checked exception,以避免它对接口的污染),就无需考虑去捕获这个异常。这样的设计并不会导致异常泄露,因为我们要求在上一层捕获。至于最顶端的Application Layer,则只做捕获异常的事儿,不干抛异常的活儿。

为各层(即领域层和基础设施层)定义各自的异常超类。其中,领域层定义的异常要求提供Error Code。Error Code并非我所愿,但对于本系统的上游系统,却需要该值,不得不为。

领域层。若是结合实际情况由自己抛出异常,则只需考虑异常消息和错误码;若是捕获了异常再抛出,则在捕获时记录日志,再度抛出的异常需要包裹原始异常对象。

请输入标题 abcdefg

在代码诊所中诊断出来的疾病,可以作为代码评审的一个标准,同时这些处方则可以当做团队内部分享与交流的知识库。长期累积下来,非常有利于团队成员编码能力的成长。

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2017-03-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序人生

重构:撰写合格的代码

在「代码重构之道」里,我犯了个懒,讨论了什么情况下需要考虑重构,以及工具和方法来促进重构,但对如何重构代码本身,或者说:如何把烂代码转化成好代码,或者至少是合格...

3438
来自专栏小红豆的数据分析

小蛇学python(8)pandas库之DataFrame

有数据的地方就有表格。无论是异常值处理,清除缺省值,还是增删改查,无论是csv还是mysql等各种数据库,无不是以表格的形式存储数据。表格在数据中成为了一个绕不...

902
来自专栏非著名程序员

编程内功心法:如何写代码?

写代码就是学一门语言然后开始撸代码吗?看完了我的《GoF 设计模式》系列文章的同学或者本身已经就是老鸟的同学显然不会这么认为。编程是一项非常严谨的工作!

59712
来自专栏数说工作室

4. call PRXPOSN() | 撕数据!

【SAS Says·扩展篇】撕数据! | 4. call PRXPOSN() 0. 前集回顾 1. 新的问题 2. 初识 PRXPOSN() 3. 问题解决 -...

3556
来自专栏企鹅号快讯

神级程序员教你如何写代码——十年编程内功心法

写代码就是学一门语言然后开始撸代码吗?看完了我一系列文章的同学或者本身已经就是老鸟的同学显然不会这么认为。编程是一项非常严谨的工作!虽然我们自嘲为码农,但是这工...

1885
来自专栏数说工作室

撕数据! |【SAS Says·扩展篇】

【SAS Says·扩展篇】撕数据! | 4. call PRXPOSN() 0. 前集回顾 1. 新的问题 2. 初识 PRXPOSN() 3. 问题解决 -...

3307
来自专栏姬小光

正则表达式是个啥

前些天有运营 MM 问小鸡君,正则表达式是个啥啊?懂技术的同学可能会想,你个运营管啥是正则表达式干啥?

422
来自专栏程序员互动联盟

【编程技巧】提高程序员技能的11招

1.清晰的分析问题 2.三思而后行如何解决这个问题 3.收集完整的需求。 花点时间,想好产品的目标形态和最终的用户群。在这个阶段思路清晰会给以后节省很多时间。 ...

3377
来自专栏逻辑熊猫带你玩Python

Python | “万年历——日期查询”

这是一个简单小程序,从这个程序说明,对于编程而言,有一定的数学基础是比较重要的,除此之外锻炼逻辑思维能力可以提高编程能力。

1121
来自专栏木子昭的博客

<进击的虫师>如何让程序"懂很多"?

? 最近在做一个有意思的小项目, 在一个聊天对话中, 你向电脑提出问题, 他会自动分词,然后根据关键字, 自动答复你 对所有的关键字做出解释, 工作量实在...

3638

扫码关注云+社区