參考自:http://www.artima.com/weblogs/viewpost.jsp?thread=168511程序員
一直以來只知道防護式編程,《代碼大全》中也專門有一章講述防護式編程,防護式編程又稱被動編程。有一個比喻是說開車時司機只遵照交通規則是遠遠不夠的,還得時刻提防其餘意外因素,如馬路殺手、其餘醉酒的司機等等。對應在編程中就是「不能信任用戶的輸入,必須執行相應的參數檢查」。web
而offensive coding則與此相反,它的中文意思是攻擊式編程或者主動編程。編程
先看一段簡單的代碼:數組
private void addResolution(List<Flag> flags) { if (flags != null) { if (supportedAdapters.contains(Adapter.LOW) || supportedAdapters.contains(Adapter.MEDIUM)) { flags.add(new ResolutionFlag(ADAPTED)); } } }
這段代碼有啥問題?也許有人的第一反應是這段代碼應該使用Guard Clause,即改爲下面這樣:jsp
private void addResolution(List<Flag> flags) { if(flags == null) return; if (supportedAdapters.contains(Adapter.LOW) || supportedAdapters.contains(Adapter.MEDIUM)) { flags.add(new ResolutionFlag(ADAPTED)); } }
但其實問題不在於此,而在於這一行代碼:post
if(flags == null) {...
既然是private方法,那麼徹底能夠在類內部本身保證傳入的參數flags不爲null,而不用進行多餘的null check。code
濫用null check是糟糕代碼的標識。
null check是不可或缺的。若是你在你的代碼中處處傳遞null,那麼接收參數並處理的方法進行null check是必要的。可是這也意味着你的代碼很糟糕。你在給本身增長額外負擔(全部有參數的方法開頭都必須進行null check),你的代碼也很難複用。blog
另一個例子:get
public String [] getParameters() { if (intParms.length == 0) { return null; } String [] result = new String[intParms.length]; for(int n = 0; n < intParms.length; n++) { result[n] = "parameter " + intParms[n]; } return result; }
這段代碼有什麼問題呢?若是這個方法被你本身在同一個項目中其餘地方使用還好,你知道返回值可能爲null,因此你會進行null check。而若是是你的同事在使用這段代碼,或者這段代碼被打包在Jar中被世界上某個角落的另外一個程序員使用呢?他們不會像你同樣對你的代碼這麼熟悉。他們極可能忘記null check,好比以下:io
// 世界上某個角落的其餘程序員Fork在使用你的Jar包中的getParameters方法 public void foo() { ... int len = new ConcreteService().getParameters().length; ... }
極可能這段代碼在大部分狀況下都能正常運行,而不會拋出NullPointerException,因而這位程序員又將這段代碼打包到他的Jar包裏,以後又有第三個程序員John來使用Fork的Jar包中的foo()方法,以下:
// John使用Fork的Jar包中的代碼 public void bar() { ... new Delegator().foo(); ... }
OK,問題來了,大部分時候這段代碼也執行正常,可是某一天bar()忽然拋出NullPointerException,你最初挖的「坑」潛伏這麼久,終於有人掉進去了。John只能罵一句「好坑!」或者「Oh, shift!」了。那麼最初的getParameters()方法該怎麼寫纔好呢?很簡單,像下面這樣便可:
public String [] getParameters() { String [] result = new String[intParms.length]; for(int n = 0; n < intParms.length; n++) { result[n] = "parameter " + intParms[n]; } return result; }
如今若是intParms是一個空數組的話,返回的也是一個空數組。但若是intParms是null怎麼辦呢?很簡單,intParms顯然是類變量或者類成員,你只須要保證intParms始終不爲null便可,好比這樣:
private int[] intParms = new int[0];
而後任何對intParms的修改都不能將其設爲null,但能夠把它重置爲空數組。這樣當前類內部的處理負擔可能加劇了,但其餘類的負擔卻大大減輕,別人也將更樂意使用你的代碼。