当前位置: 首页 > 科技观察

CodeClinic的二诊

时间:2023-03-13 11:40:52 科技观察

几年前,我有机会负责一个项目咨询。团队很小,目标是用Java重写旧系统的后台,团队的开发人员都是C程序员。我的工作职责是负责项目设计和开发,并在项目开发过程中担任敏捷教练,培训Java开发人员。我在团队工作室的一角开了一家小诊所,大肆宣传——“一日一贴,百病通”。这是我当时对该项目的第二次诊断。1、变量的声明尽量和使用放在一起。这个规则关系到代码的可读性。如果方法没有保持简短,这个问题就更严重了。或许这是C语言开发者容易犯的错误。当然,很多Java程序员都从他们的前辈那里继承了这个坏习惯。我曾经在一个遗留项目中看到一个Java方法有几千行。几十个变量定义堆在方法的头部,看得头晕目眩!除了影响代码的可读性之外,还可能造成隐患。很多程序员之所以习惯一开始就声明变量,是因为他们把这个局部变量当做一个存放中间状态的容器,在方法内部反复使用这个变量。这个中间结果的变化可能不符合开发者的意图,或者后来代码的代码维护者没有对这个变化进行梳理,从而对变量值进行了误判。2.常量和枚举的使用在这条规则中无足轻重,写在这里是为了进一步警醒团队成员。在咨询过程中,看到了这段代码:Integer.parseInt(freeFlash,16);这个16是什么鬼?MagicNumber,经常让人摸不着头脑。在JDK提供枚举之前,很多Java程序员喜欢用接口类型来包装大量的常量。如果常量具有内聚的绝对意义,最好使用枚举。3、进行合理的封装是非常有必要的,避免对方法调用的错误封装。有时,暴露太多细节会使来电者感到不知所措。对于TelnetService类,我们需要依次调用connect()、login()、enterUShell(),然后exitUShell()和disconnect()必须在执行命令后依次执行。这让我想起事务处理、FTP访问等资源相关的逻辑,都需要在执行逻辑前后包裹一些基础设施处理逻辑。为了避免执行命令前后忘记连接或断开telnet,最好将这个过程封装起来。这是从通话安全的角度考虑的。如果考虑调用的简单性,封装也是必要的。当我们需要通过TelnetService发送telnet命令时,为什么需要了解内部执行逻辑呢?那么,如何封装两全其美,既满足执行逻辑序列的重用又满足命令逻辑的扩展呢?通常的做法是将真正的执行逻辑抽取出来作为接口,比如Java中Runnable的方式。这其实可以看做是Command模式的使用。当然,我更愿意把它看成是对函数的封装,比如Guva中的tranform()、filter()等方法,接受更具函数气质的Function或Predicate接口(Java8还没出来)。因此,我的做法如下:可以这样调用:Stringresult=telnetService.withCommand(newExecutionCommand(){@OverridepublicStringsend(){returntelnetService.transfer();}});我不知道如何在受控异常和非受控异常之间做出选择;不知道什么时候捕捉异常,什么时候抛出异常。因此,我确定了这个项目异常处理的架构原则,目的是让整个架构更简单,让异常处理更一致。我的目的是减轻开发者的负担,同时又不降低代码的质量,方便以后对代码的维护。规则如下:同层之间的调用没有try-catch,上层调用下层对象需要try-catch。即使对象抛出异常,只要不是checkedexception(我们尽量避免使用checkedexception,以免其对接口造成污染),也不需要考虑捕获这个异常。这样的设计不会导致异常泄露,因为我们需要在上层捕获。至于最顶层的ApplicationLayer,它只做捕获异常的工作,不抛异常。为每一层(即领域层和基础设施层)定义单独的异常超类。其中,领域层定义的异常需要一个ErrorCode。ErrorCode不是我想要的,但是对于这个系统的上游系统来说,这个值是必须的,所以我不得不。域层。如果根据实际情况自己抛出异常,只需要考虑异常信息和错误码即可;如果捕获到异常再抛出,捕获到时记录日志,再次抛出的异常需要包装原来的异常对象。代码诊所诊断出的疾病可以作为代码审查的标准,这些药方可以作为团队内部分享和交流的知识库。长期积累,非常有利于团队成员编码能力的成长。【本文为专栏作家“张艺”原创稿件,转载请联系原作者】点此阅读更多该作者好文