【Guava 教學】(2)命名明確的條件檢查
【Guava 教學】(1)從避免使用 null 開始 << 前情 有多少次了呢?你總會對傳入的引數作一些檢查,像是某個管理物件的容器,你也許會有個 public void add(List<T> lt) { if(lt == null) { throw new IllegalArgumentException("不能傳入 null"); } if(lt.isEmpty()) { throw new IllegalArgumentException("List 不能是空"); } // 繼續辦事... } 每次都得為了做這類的檢查而撰寫類似程式碼的話,為什麼不把它封裝起來呢?像是寫個 public static void checkArgument(boolean expression, Object errorMessage) { if(expression) { throw new IllegalArgumentException(errorMessage.toString()); } } 那麼你原本的方法就可以修改為: public void add(List<T> lt) { checkArgument(lt != null, "不能傳入 null"); checkArgument(!lt.isEmpty(), "List 不能是空"); // 繼續辦事... } 看來不錯,那為什麼不用 在 Guava 中對這類前置檢查的工作,實際上在 public void add(List<T> lt) { checkNotNull(lt, "不能傳入 null"); checkArgument(!lt.isEmpty(), "List 不能是空"); // 繼續辦事... } 這就是 Bob 大叔在《Clean Code》中一直強調的概念「有意義的命名(Meaningful Names)」,只要有助於可讀性,流程中某個區塊都可以使用函式並「使用具描述能力的名稱(Use Descriptive Names)」來取代。比方說,如果某個方法要檢查物件內部狀態: public void doSome() { if(container.size() > 100) { throw new IllegalStateException("超過負載"); } // 繼續辦事... } 那麼可以直接使用 Guava 的 public void doSome() { checkState(container.size() <= 100, "超過負載"); // 繼續辦事... } 乍看 把語義清晰納入考量的話,你會怎麼修改這段程式碼呢? public T get(int index) { if(index < 0) { throw new IllegalArgumentException("索引不得小於 0"); } if(index >= container.size()) { throw new IllegalArgumentException("索引超出範圍"); } // 繼續辦事... return ...; } 用 public T get(int index) { checkArgument(index >= 0, "索引不得小於 0"); checkArgument(index < container.size(), "索引超出範圍"); // 繼續辦事... return ...; } 還不錯!不過如果可以更明確地丟出 public T get(int index) { checkElementIndex(index, container.size()); // 繼續辦事... return ...; } 至於 ... public static int checkElementIndex(int index, int size) { return checkElementIndex(index, size, "index"); } public static int checkElementIndex( int index, int size, @Nullable String desc) { // Carefully optimized for execution by hotspot (explanatory comment above) if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); } return index; } private static String badElementIndex(int index, int size, String desc) { if (index < 0) { return format("%s (%s) must not be negative", desc, index); } else if (size < 0) { throw new IllegalArgumentException("negative size: " + size); } else { // index >= size return format("%s (%s) must be less than size (%s)", desc, index, size); } } 與 public List<T> slice(int start, int end) { if(start < 0 || end < start || end > container.size()) { throw new IllegalArgumentException("索引超出範圍"); } // 繼續辦事... return null; } 那麼就可以使用 Guava 提供的 public List<T> slice(int start, int end) { checkPositionIndexes(start, end, container.size()); // 繼續辦事... return null; } 有時候, |
Yuen-Kuei Hsueh
06/13
最後的論點似乎與Preconditions初衷有點落差
可以參考下列討論串
http://code.google.com/p/guava-libraries/issues/detail?id=1388
javadoc
http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Preconditions.html
Yuen-Kuei Hsueh
06/13
作者或許可以參考 王建興大師的「程式該自我防禦或盡早面對錯誤? 」一文
http://www.ithome.com.tw/itadm/article.php?c=78765&s=1