《代码整洁之道》- 代码标准

每次看以前写的代码,都想删掉从新写%>_<%

引言

代码越写越多,从一开始的能跑通就行,到如今对本身的要求愈来愈高,不只要留下好的注释,对于函数和变量的命名也要合乎其名,慢慢融入学到的设计模式,还有优化逻辑来提升性能。前端

因此看完这本《代码整洁之道》,发现获益良多,将书中最后一章的启示贴一下,用来提醒本身之后编码的要求~java


启示(约定规则)


一个源文件中减小存在多种语言

这个问题应该是存在于之前的JSP开发,如今通常都是先后端分离,后端代码中,基本没有前端代码的存在了算法


要实现函数名对应的行为

遵循“最小惊讶原则”(The Principle of Least Surprise),函数或类应该实现有理由期待的行为。后端


边界行为要明确

追索每种边界条件,并编写测试。要求全部代码都有单元测试,这个条件比较严格,主要要注意的应该是在生产代码中,注意业务边界,避免NPE,数组越界等错误。设计模式


不要忽视安全

这里提到安全不只仅是边界和单元测试,还有无限循环和线程安全等意识。数组


避免重复代码

这个就是常说的胶水代码,这里copy一份到另外一个地方。每次看到重复代码,都表明遗漏了抽象安全

能够将重复的代码抽象到上一层,或者封装成公共方法,不少地方使用到的,能够当成一个工具类的静态方法等等。bash

学习到的设计模式,责任链、模板、抽象工厂等等,均可以用来消除重复代码~前后端分离


编码要在正确的抽象层级上

建立抽象类来容纳较高层级概念,建立派生类来容纳较低层次概念。socket

同时要注意,较低层级和较高层级概念不该该混杂在一块儿,分离要完整。


基类不该该依赖于派生类

将概念分解到基类和派生类的最广泛的缘由是较高层级概念能够不依赖于低层级派生类概念。(也就是说,基类应该对派生类一无所知,不提到派生类的名称)。


避免信息过多

设计良好的模块,让接口保持简单。

要学会限制类或模板中暴露的接口数量,减小类中的方法,减小函数的变量数目。(固然若是业务复杂时,简单变量可能越加越多,这个时候就该创建一个新的参数类来封装参数)。

尽可能保持接口紧凑,经过限制信息来控制耦合度。


删除死代码

有些方法可能在代码修改后就不会再调用了,这个时候路径就不会到达到,如今IDE很方便能看到方法是否被调用到。看到这些永远不会运行的代码,能够考虑将其删除~


垂直分隔

变量和函数应该靠近被使用的地方定义。

本地变量应该正好在其首次被使用的位置是哪一个命名,垂直距离要短。

私有函数应该恰好在其被使用的位置下面定义。


保持先后一致

从一而终。当心选择约定,一旦选中,就当心持续遵循。

例如参数命名,若是选中了HttpServletRequest request,那么以后的参数命名都按request来统一,让代码更加易于阅读和修改。


减小人为耦合

不互相依赖的东西不应耦合。

不要将两个没有直接目的之间的模块耦合,例如将变量、常量或函数放在不恰当的位置,普通的enum不该在特殊类中。


减小特性依赖

类的方法只应对其所属类中的变量和函数感兴趣,不应垂青其余类中的变量和函数。

当方法经过其余对象的访问器和修改器来操做该对象内部数据,则它就依恋于该对象所属类的范围。(固然在业务开发中,常常会遇到条件判断,而后修改对象的属性值,我以为这这种场景是正常,可是下面说的例子就要避免出现了)

Bad Example:在B类方法中,基本依赖A类的属性

public class HourlyPayCalculator {
    public Money calculateWeeklyPay(HourlyEmployee e) {
        int tenthRate = e.getTenthRate().getPennins();
        int tenthsWorked = e.getTenthsWorkeds()
        int straightTime = Math.min(400, tenthsWorked);
        int overTime = Math.max(0, tenthsWorked - straightTime);
        int straightPay = straightTime * tenthRate;
        int overtimePay = (int) Math.round(overTime * tenthRate * 1.5)
        return new Money(straightPay + overtimePay);
    }
}
复制代码

能够看到,上面的HourlyPayCalculator类的计算工资方法,侵入到是钟点工类e的做用范围。

如今看起来,这个方法更应该下沉到HourlyEmployee类中,让它对本身类中的属性进行操做(虽然以前这样写没感受到什么不妥,但以后会改~)


避免在函数末尾出现选择算子函数(selector)

我的理解上,应该是将复杂的选择算子函数拆分红多个子函数,而后经过组合调用,让函数的意图更加清晰。


避免晦涩的意图

代码要尽量具备表达力。

之前写Android代码时,看到匈牙利语标记语法和联排表示式,例如m_/f_等前缀,如今想一想这些前缀有些多余,同时也要减小魔术数,毕竟写个数字5,在没有注释的状况下,除了开发者,估计其余人也没法理解这个5的实际含义~


使用解释性变量

一个例子你就能懂:

Matcher match = headerPattern.matcher(line);
if (match.find()) {
    String key = match.group(1);
    String value = match.group(2);
    headers.put(key.toLowerCase(), value);
}
复制代码

是否是感受变量会说话,key和value让人一看就了解它们的含义,这种状况下都不须要加注释了~


函数名称应该表达其行为

如下这种应该是杜绝的:

Date newDate = date.add(5);
复制代码

函数名add(int),没法表达它的含义,是天数仍是月份增长5,因此这些表意不明的函数名是要杜绝的。


理解算法

这里的算法,应该是确认本身理解了这个函数是怎样工做,在此基础上,进行修改或者重构。


用多态替代If/Else或Switch/Case

以为这条建议应该是在业务复杂的状况下或者须要扩展代码逻辑状况下实施的,在简单业务状况下,为了不类膨胀的话,是可使用If/Else~(给本身偷懒找借口(^o^)/~)


用命名常量替代魔术数

避免在代码中硬编码数字,能够将常量数字写在类最上面的静态变量,或者抽象一个枚举,阐释这个数字的含义。


封装条件

在if表达式中,将判断式封装起来(此方法会在条件简单偷懒=-=):

例如: if (shouldBeDeleted(timer)) 要好于 if (timer.hasExpired() && !timer.isRecurrent())


避免否认性条件

例如: if (buffer.shouldCompact()) 要好于 if (!buffer.shouldNotCompact())


函数只该作一件事

函数只作一件事有点难,能够将一个函数拆解出几个小函数,例如付工资函数,能够从一个大方法拆分红三个小方法,获取人员、计算工资、打钱到银行帐户,这样这些小方法能够被其它方法复用。


掩蔽时序耦合

经常有必要使用时序耦合,上一个步骤的结果交给下一个步骤处理,这个时候就不该该掩蔽它们之间的时序性。

例如:

public class MovieDiver {
    Gradient gradient;
    List<Spline> splines;
    
    public void dive(String reason) {
        saturateGradient();
        reticulateSplines();
        diveForMoog(reason);
    }
    ···
}
复制代码

dive()方法的代码没有强调时序耦合,若是其余开发人员调换了执行顺序,这样dive方法就没法正常执行了。

能够更新成下面的形式:

public void dive(String reason) {
    Gradient gradient = saturateGradient();
    List<Spline> splines = reticulateSplines(gradient);
    diveForMoog(reason);
}
复制代码

这样经过建立顺序队列暴露时序耦合。


封装边界条件

这个规则挺像封装判断条件,不过这条规则一样适用于封装变量: 例如,将出现过两次的level + 1封装成一个变量

int nextLevel = level + 1;
if (nextLevel < tags.length) {
    parts = new Parse(body, tags, nextLevel, offset + endTag);
    body = null;
}
复制代码

在较高层级放置可配置数据

能够理解为,变量定义在类起始的位置,不要将共有变量定义在函数后面。


函数名称要说明函数反作用

例如:

public ObjectOutputStream getOos() throws IOException {
    if (oss == null) {
        oos = new ObjectOutputStream(socket.getOutputStream());
    }
    return oos;
}
复制代码

这个方法名只说了获取输出流,但没说明若是输出流不存在的状况下,它将新建一个输出流,因此这个函数名称应该修改为createOrReturnOos


避免传递游览

例若有A、B、C三个类,下面这种行为是要禁止的:

a.getB().getC().doSomething();
复制代码

正确的作法应该是:

myCollaborator.doSomething();
复制代码

让咱们的直接协做者提供所需的所有服务~~


参考书籍

  1. 《代码整洁之道》-第十七章
相关文章
相关标签/搜索