大师级的程序员把系统看成故事来说,而不是看成程序来写。
若是同一做用范围内两样不一样的东西不能重名,那其意思也应该不一样才对。那么这两样东西应该取不一样的名字而不是以数字区分。若是如下代码参数名改成 source
和 destination
,这个函数就会像样许多java
public static void copyChars(char a1[], char a2[]){ for(int i = 0; i < a1.length; i++){ a2[i] = a1[i]; } }
例如循环中的 i、j、k
等单字母名称不是个好选择;读者必须在脑中将它映射为真实概念。最好用 filter、map
等方法代替 for循环
程序员
名称或名词短语
动词或动词短语
例如 fetch
、retrive
、 get
表达同一个意思,应该选定一个,而后在各个类中使用相同的方法名。算法
避免将同一单词用于不一样的目的。同一术语用于不一样概念,基本上就是双关语了。app
记住,只有程序员才会读你的代码。因此,尽管用那些计算机科学(Computer Science,CS)术语、算法名、模式名等。函数
给函数取个好名字,能较好地解释函数的意图,以及参数的顺序和意图。fetch
对于一元函数,函数和参数应当造成一种很是良好的动词/名词形式。编码
// good write(name) // better // 更具体,它告诉咱们,"name"是一个"field" writeField(name)
函数名称的关键字(keyword)形式。使用这种形式,把参数的名称编码成了函数名spa
// bad assertEqual(expected, actual); // good // 这大大减轻了记忆参数顺序的负担 assertExpectedEqualsActual(expected, actual);
20行封顶
if
、else
、while
等语句,其中的代码应该只有一行。该行大抵应该是一个函数调用语句。由于块内的函数拥有较具体说明性的名称,从而增长了文档上的价值确保函数不能被再拆分
日志
最理想的参数数量是零,其次是一,再次是二,应尽可能避免三code
true
将会这样,标识为 false
则会那样二元函数:有些时候两个参数正好。例如 Point p = new Point(0, 0)
;由于点天生拥有两个参数。但大多数状况下应该尽可能利用一些机制将二元函数转换成一元函数。例如,把writeField 方法写成outputStream的成员之一
// bad writeField(outputStream, name); // good outputStream.writeFiled(name);
参数对象:若是函数看起来须要两个、三个、或三个以上参数,就说明其中一些应该封装为类了
// bad Circle makeCircle(double x, double y, double, radius); // good Circle makeCircle(Point center, double radius);
从参数封装成对象,从而减小参数数量,看起来像是在做弊,但实则并不是如此。当一组参数被共同传递,就像上例中的x和y那样,每每就是该有本身名称的某个概念的一部分
确保函数功能就像函数名描述的同样,不要作函数名描述之外的事情。应该为起一个更能描述函数功能的函数名
public UserValidator { private Cyptographer cyptographer; public boolean checkPassword(String userName, String password) { User user = UserGateway.findByName(userName); if(user != User.NULL) { String codePhrase = user.getPhraseEncodedByPassword(); String phrase = cyptographer.decrypt(codePhrase, password); if("Valid Password".equals(phrase)) { // 反作用在于对这个调用 // checkPassword函数,顾名思义,就是用来检查密码。该名称并未暗示它会 // 初始化该次会话。能够重命名为 checkPasswordAndIntializeSession Session.initialize(); return true; } } return false; } }
普通而言,应该避免使用输出参数,若是函数必需要修改某种状态,就修改所属对象的状态
// bad // 读者会弄不清s是输入参数仍是输出参数 // 也会弄不清这函数是把s添加到什么东西后面,仍是把什么东西添加到s后面 appendFooter(s); // 函数签名 public void appendFooter(StringBuffer report){} // good report.appendFooter();
函数要行作什么事( 例如 user.setName('xxx')
)、要么回答什么事( 例如 user.isVip()
)。一个函数里不要把两件事都干了。
TODO 注释
虽好,但也要按期查看,删除再也不须要的切断代码间的联系
// bad public class ReportConfig { // // The class name of the reporter listener // private String m_className; // //The properties of the reporter listener // private List<Property> m_properties = new ArrayList<Property>(); public void addProperty(Property property) { m_properties.add(property); } } // good public class ReportConfig { private String m_className; private List<Property> m_properties = new ArrayList<Property>(); public void addProperty(Property property) { m_properties.add(property); } }
概念相关。 概念相关的代码应该放到一块儿。相关性越强,彼此之间的距离就该越短
public class Assert { static public void assertTrue(String message, boolean codition(){} static public void assertTrue(boolean codition(){} static public void assertFalse(String message, boolean codition(){} // ..... }
这些函数有关极强的概念相关性,由于他们拥有共同的命名模式,执行同一基础任务的不一样变种。互相调用是第二位的。即使没有互相调用。也应该放在一块儿。
更多例子查看 p79 -- 5.25 垂直顺序
函数应该只作一件事,错误处理就是一件事。
// bad public void delete(Page page) { try{ deletePage(page); registery.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey(); }catch(Expection e){ logError(e); } } // good public void delete(Page page) { try{ // 将上例的操做,封装到一个方法 deletePageAndAllReferences(page); }catch(Expection e){ logError(e); } }
受控异常:Checked Exception(FileException、SQLException等)
,这类异常必须写 try/catch
,或者 throw抛出
,不然编译通不过。
非受控异常:Unchecked Exception
,这类异常也叫作运行时异常(与非受控异常 字数相等),这类异常不须要 try/catch
,也不须要 throw抛出
。即便 throw 了,上层调用函数也非必须捕获,编译能经过。
受控异常的代价就是违反开放/闭合原则。若是你在方法中抛出受控异常,这意味着每一个调用该函数的函数都要修改,捕获新异常,或在其签名中添加合适的throw语句。对于通常的应用开发,受控异常依赖成本要高于收益成本,尽可能 try/catch
处理,不要抛出。
应建立信息充分的错误信息,并和异常一块儿传递出去。在消息中,包括失败的操做和失败类型。若是你的应用程序有日志系统,传递足够的信息给catch块,并记录下来。
// bad ACMEPort port = new ACMEPort(12); try { port.open(); } catch(DeviceResponseException e) { reportPortError(e); logger.log("Device response exception",e); } catch(ATM1212UnlockedException e) { reportPortError(e); logger.log("Unlock exception",e); } catch(GMXError e) { reportPortError(e); logger.log("Device response exception",e); } finally { // ..... }
经过打包调用API,确保它返回经过用异常类型,从而简化代码
// good LocalPort port = new LocalPort(12); try { port.open(); } catch(PortDeviceFailure e) { reportError(e); logger.log(e.getMessage(),e); } finally { // ..... } public class LocalPort{ private ACMEPort innerPort; public LocalPort(int portNumber){ innerPort = new ACMEPort(portNumber); } public open() { try { innerPort.open(); } catch(DeviceResponseException e) { // 自定义的异常类 throw new PortDeviceFailure(e); } catch(ATM1212UnlockedException e) { throw new PortDeviceFailure(e); } catch(GMXError e) { throw new PortDeviceFailure(e); } } }
将第三方API打包是个良好的实践手段。当你打包一个第三方API,你就下降了对它的依赖。