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 > 內部定義很多的類內置類。
使用下面代碼實例補充說明上述:

/**
* OuterClass 是外層類,NestedClass 類內置類
* @author mercyblitz
*/

public class OuterClass {
       /**
       * private final static 是類內置類的通用設計技巧
       */

       private final static class NestedClass {
      }
}
代碼 -1
若是 OuterClass類中有實例變量的話,顯然 NestedClass 是不可見的,也是不適用的(由於它是類的一部分)。
這個時候,利用實例內置類能夠解決這類問題。
示例代碼以下:

/**
* 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利用OuterClass2message字段做爲輸出。
可能有人會說,InnerClass這種實例內,爲了獲得這個類,不得不建立一個實例,太浪費資源了,爲何不直接把OuterClass實例做爲參數,直接傳入到InnerClass的方法呢?
沒錯,能夠那麼作。不過單從訪問外層類的實例變量而言,利用實例內置類是有點顯得浪費。若是客戶端利用了泛型編程的話,狀況就會不一樣。
總所周知, 泛型設計可以提升靈活性,但是也有不少限制。模版參數類型是跟隨其寄主類的, 模板參數類型是不會寫入class文件中的,這就是爲何反射(Reflection)不能解析出類的模板參數類型。可是,模板參數類型在實例(對象)範圍是可用的(或可視的) 。若是內置類中想要利用外層類的模板參數類型的話,那麼實例內置類就有很大用處。
例子以下:

/**
* 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) 更爲方便,由於局部內置類是在塊中(方法也是一種特殊的塊)定義的,這樣就很好的解決了上下文的參數傳遞問題。
參看代碼:

/**
* 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書中提到超過四個參數的話,不利於維護),那麼局部內置類是否是方便維護呢?
順便提到,匿名內置類是局部內置類的一種。
不難發現,局部內置類的缺點是代碼混雜(方法和類混在一塊兒),若是依賴局部變量很少的狀況下,在必定程度上面,增長了維護成本。
最後的篇幅來總結一下這幾種內置類的特色,以及使用場景和設計技巧。
共同特色,不想暴露而且不指望被外部使用或者擴張(強調一下,通常類中私有和包內私用都是好的設計技巧),經過類的四大特性提供更優於方法的方法和外部內實現交互,從而達到良好設計目的。
對於類內置類(Nested Class),適合的絕大多數內置類場景,利於維護。但不適合利用外層類模板參數類型和實例變量。
就實例內置類(Inner Class),適合利用外層類模板參數類型和實例變量,更好的彈性設計。但是加載其類時,必須實例化外部類,形成沒必要要開銷,所以不是必須,儘可能使用類內置類。
局部內置類(Local Class),適合多局部變量依賴的場景,提升可維護性,相反就不適合。
所以,內置類的設計和其餘面向對象設計相似,根據適合的場景來合理設計。 在設計上,沒有最好,只有更好 。筆者精力和經驗優先,但願你們指正,謝謝。
================= 此文章的原處不明,我只是保留了原文檔
相關文章
相關標籤/搜索