轉帖|其它|編輯:郝浩|2010-05-12 12:06:06.000|閱讀 606 次
概述:從這篇文章開始進入Java面向對象設計的實戰階段,本文介紹Java內置類設計的最佳實踐。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
從這篇文章開始進入實戰階段的設計階段,本文介紹內置類設計的最佳實踐。
回顧一下,類(Class)作為Java編程語言中的基本單元模塊,提供了面向對象的四種基本性質:抽象性、封裝性、繼承性和多態性。
在面向對象設計原則中,盡可能偏好方法,而非字段(或屬性)。簡單的說,方法更好的表達語義。因此,在方法實現過程中,經常會遇到類似的情景,接口方法method1 調用其他方法來完成功能需要。無非有三種情況,利用本類的(靜態或者實例的)方法,調用某個類的靜態可訪問的方法和某個實例可訪問的實例方法。但是,良好類設計是盡量的隱藏實現細節,簡單清晰地表達語義。
客戶端程序只關心輸入和輸出,沒有必要去關心中間的細節。回到上述的三情況,盡可能隱藏本類和他類的細節。如果本類和他類相互耦合,那么擴張性和易用性受到一定程度的影響。但是設計人員想讓本類和他類相互知曉,或者范圍限制(主要是指類之間的訪問限制)盡可能小,那么內置類是一個很好的辦法。
筆者把內置類分為三類:類內置類(Nested Class),實例內置類(Inner Class)和布局內置類(Local Class)。下面分別介紹這三種類的使用場景和設計方法。
類內置類(Nested Class),在內置類中使用得最多的一類。其作為類的一部分,隨外層類(Enclosing Class)順序地被加載。它的好處是,定義的類內置類僅此一次被創建并加載,可視外層類的類成員。那么使用場景不難得出,對于實例成員不感冒,只關心類成員,并且減少沒有必要重復類的創建和加載。在大多數實際情況中,這個模式已經足夠了。舉一個的JDK里面的例子,迭代Map的時候,鍵值對實體接口java.util.Map.Entry,其定義在java.util.Map接口中,自然其修飾符是public static final。
為了客戶端程序能夠利用java.util.Map.Entry,JDK暴露了它。一般來說,private final static是通用的設計。外層類對其是完全可視的,因此private 是沒有問題的。至于final的修飾,要談到筆者設計經驗中的一個原則,盡量使用final修飾可修飾的。其中有幾個好處,比如線程安全、拒絕子類、標準化(在后面的設計文章中會詳細說明)等。在內置類設計中,不應該期望其他類繼承這個類,更不要期望其他人會使用的內置類了。又回到JDK,大家會發現java.util.HashMap內部定義不少的類內置類。
使用下了代碼實例補充說明上述:
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 * 模板參數類型,實例內置類可以利用 */ public class OuterClass3 { 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書中提到超過四個參數的話,不利于維護),那么局部內置類是不是方便維護呢?
順便提到,匿名內置類是局部內置類的一種。
不難發現,局部內置類的缺點是代碼混雜(方法和類混在一起),如果依賴局部變量不多的情況下,在一定程度上面,增加了維護成本。
最后的篇幅來總結一下這幾種內置類的特點,以及使用場景和設計技巧。
共同特點,不想暴露并且不期望被外部使用或者擴張(強調一下,一般類中私有和包內私用都是好的設計技巧),通過類的四大特性提供更優于方法的方法和外部內實現交互,從而達到良好設計目的。
對于類內置類(Nested Class),適合的絕大多數內置類場景,利于維護。但不適合利用外層類模板參數類型和實例變量。
就實例內置類(Inner Class),適合利用外層類模板參數類型和實例變量,更好的彈性設計。可是加載其類時,必須實例化外部類,造成不必要開銷,因此不是必須,盡量使用類內置類。
局部內置類(Local Class),適合多局部變量依賴的場景,提高可維護性,相反就不適合。
因此,內置類的設計和其他面向對象設計類似,根據適合的場景來合理設計。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:網絡轉載