Java面向對象設計最佳實踐 - 內置類設計

從這篇文章開始進入實戰階段的設計階段,本文介紹內置類設計的最佳實踐。

回顧一下,類(Class )作爲 Java 編程語言中的基本單元模塊,提供了面向對象的四種基本性質: 抽象性、封裝性、繼承性和多態性。

在面向對象設計原則中, 儘可能偏好方法,而非字段(或屬性) 。簡單的說,方法更好的表達語義。因此,在方法實現過程中,經常會遇到類似的情景,接口方法method1  調用其他方法來完成功能需要。無非有三種情況,利用本類的(靜態或者實例的)方法,調用某個類的靜態可訪問的方法和某個實例可訪問的實例方法。但是, 良好類設計是儘量的隱藏實現細節,簡單清晰地表達語義

客戶端程序只關心輸入和輸出,沒有必要去關心中間的細節。回到上述的三情況,儘可能隱藏本類和他類的細節。如果本類和他類相互耦合,那麼擴張性和易用性受到一定程度的影響。但是設計人員想讓本類和他類相互知曉,或者範圍限制(主要是指類之間的訪問限制)儘可能小,那麼內置類是一個很好的辦法。

筆者把內置類分爲三類: 類內置類(Nested Class) 實例內置類(Inner Class)和 佈局 內置類(Local Class) 。下面分別介紹這三種類的使用場景和設計方法。

類內置類(Nested Class) ,在內置類中使用得最多的一類。其作爲類的一部分,隨外層類(Enclosing Class)順序地被加載。它的好處是,定義的類內置類僅此一次被創建並加載,可視外層類的類成員。那麼使用場景不難得出,對於實例成員不感冒,只關心類成員,並且減少沒有必要重複類的創建和加載。在大多數實際情況中,這個模式已經足夠了。舉一個的JDK裏面的例子,迭代Map的時候,鍵值對實體接口 java.util.Map.Entry<K, V> ,其定義在 java.util.Map<K, V> 接口中,自然其修飾符是 public static final

爲了客戶端程序能夠利用 java.util.Map.Entry<K, V> JDK 暴露了 它。一般來說, private final static是通用的設計。外層類對其是完全可視的,因此 private  是沒有問題的。至於 final 的修飾,要談到筆者設計經驗中的一個原則,儘量使用 final 修飾可修飾的。其中有幾個好處,比如線程安全、拒絕子類、標準化(在後面的設計文章中會詳細說明)等。 在內置類設計中,不應該期望其他類繼承這個類,更不要期望其他人會使用的內置類了 。又回到JDK ,大家會發現 java.util.HashMap<K,V> 內部定義不少的類內置類。

使用下了代碼實例補充說明上述:

package  org.mercy.design;

 

/**

  *   OuterClass   是外層類,NestedClass   類內置類

  *   @author   mercyblitz

  */

public   class  OuterClass {

/**

  *   private   final   static   是類內置類的通用設計技巧

  */

private   final   static   class   NestedClass  {

}

}

代碼 -1

 

如果 OuterClass類中有實例變量的話,顯然 NestedClass 是不可見的,也是不適用的(因爲它是類的一部分)。

這個時候,利用實例內置類可以解決這類問題。

示例代碼如下:

package  org.mercy.design;

 

/**

  *   OuterClass2   是外層類,InnerClass   實例內置類

  *  

  *   @author   mercyblitz

  */

public   class  OuterClass2 {

private  String  message ;

/**

  *   使用private   final   是一種好習慣。:D

  */

private   final   class   InnerClass  {

/**

  *   輸出OuterClass2消息

  */

private   void   outputMessageFromOuterClass2 ()  {

// 注意,this的命名空間

System. out .println(OuterClass2. this . message );

}

}

}

代碼 -2

 

在“代碼-2 ”中, InnerClass 利用 OuterClass2 message 字段作爲輸出。

可能有人會說,InnerClass 這種實例內,爲了得到這個類,不得不創建一個實例,太浪費 資源 了,爲什麼不直接把OuterClass 實例作爲參數,直接傳入到 InnerClass 的方法呢?

沒錯,可以那麼做。不過單從訪問外層類的實例變量而言,利用實例內置類是有點顯得 浪費。 如果 客戶端利用了泛型編程的話,情況就會不同。

總所周知, 泛型設計能夠提高靈活性,可是也有很多限制。模版參數類型是跟隨其寄主類的, 模板參數類型是不會寫入class 文件中的,這就是爲什麼反射( Reflection )不能解析出類的模板參數類型。但是,模板參數類型在實例(對象)範圍是可用的(或可視的) 。如果內置類中想要利用外層類的模板參數類型的話,那麼實例內置類就有很大用處。

例子如下:

package  org.mercy.design;

 

/**

  *   OuterClass3   是外層類,InnerClass   實例內置類

  *  

  *   @author   mercyblitz

  *   @param   <T>

  *              模板參數類型,實例內置類可以利用

  */

public   class  OuterClass3<T> {

 

private  T  data ;

 

/**

  *   使用private   final   是一種好習慣。:D

  */

private   final   class   InnerClass  {

public   void  setData(T newData) {

OuterClass3. this . data  = newData;

// DOES Other things

}

}

}

代碼 -3

      

“代碼-3 ”中的實例內置類利用外層類 OuterClass3 中的模板參數 T ,作爲 setData 參數的類型。

 

看似類內置類和實例內置類已經足夠使用了。考慮這麼一個場景,一個方法利用了內置類來實現功能,這個方法中的變量需要被內置類來利用,通常可以把變量作爲參數,傳入內置類構造器或者其方法中,這也是通常的方法。不過利用 佈局 內置類(Local Class) 更爲方便,因爲局部內置類是在塊中(方法也是一種特殊的塊)定義的,這樣就很好的解決了上下文的參數傳遞問題。

參看代碼:

package  org.mercy.design;

 

/**

  *   OuterClass4   是外層類,Printer   局部內置類

  *  

  *   @author   mercyblitz

  */

public   class  OuterClass4 {

 

public   void  print( byte [] bytes) {

final  String message =  new  String(bytes);

/**

  *   名爲Printer   LocalClass,不必把message作爲參數傳遞。

  */

class  Printer {

private   void  doPrint() {

System. out .println(message);

}

}

new  Printer().doPrint();

}

 

public   static   void  main(String[] args) {

new  OuterClass4().print( "AAAAAAA" .getBytes());

}

}

代碼 -4

 

在“代碼-4”的示例中,有人可能會說,這看不出什麼好處呀?!如果內置類依賴的變量超過4個(Effective Java書中提到超過四個參數的話,不利於維護),那麼局部內置類是不是方便維護呢?

順便提到,匿名內置類是局部內置類的一種。

不難發現,局部內置類的缺點是代碼混雜(方法和類混在一起),如果依賴局部變量不多的情況下,在一定程度上面,增加了維護成本。

(其他內容,見附件)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章