轉(zhuǎn)帖|其它|編輯:郝浩|2010-05-12 12:06:06.000|閱讀 606 次
概述:從這篇文章開(kāi)始進(jìn)入Java面向?qū)ο笤O(shè)計(jì)的實(shí)戰(zhàn)階段,本文介紹Java內(nèi)置類(lèi)設(shè)計(jì)的最佳實(shí)踐。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
從這篇文章開(kāi)始進(jìn)入實(shí)戰(zhàn)階段的設(shè)計(jì)階段,本文介紹內(nèi)置類(lèi)設(shè)計(jì)的最佳實(shí)踐。
回顧一下,類(lèi)(Class)作為Java編程語(yǔ)言中的基本單元模塊,提供了面向?qū)ο蟮乃姆N基本性質(zhì):抽象性、封裝性、繼承性和多態(tài)性。
在面向?qū)ο笤O(shè)計(jì)原則中,盡可能偏好方法,而非字段(或?qū)傩?。簡(jiǎn)單的說(shuō),方法更好的表達(dá)語(yǔ)義。因此,在方法實(shí)現(xiàn)過(guò)程中,經(jīng)常會(huì)遇到類(lèi)似的情景,接口方法method1 調(diào)用其他方法來(lái)完成功能需要。無(wú)非有三種情況,利用本類(lèi)的(靜態(tài)或者實(shí)例的)方法,調(diào)用某個(gè)類(lèi)的靜態(tài)可訪問(wèn)的方法和某個(gè)實(shí)例可訪問(wèn)的實(shí)例方法。但是,良好類(lèi)設(shè)計(jì)是盡量的隱藏實(shí)現(xiàn)細(xì)節(jié),簡(jiǎn)單清晰地表達(dá)語(yǔ)義。
客戶(hù)端程序只關(guān)心輸入和輸出,沒(méi)有必要去關(guān)心中間的細(xì)節(jié)。回到上述的三情況,盡可能隱藏本類(lèi)和他類(lèi)的細(xì)節(jié)。如果本類(lèi)和他類(lèi)相互耦合,那么擴(kuò)張性和易用性受到一定程度的影響。但是設(shè)計(jì)人員想讓本類(lèi)和他類(lèi)相互知曉,或者范圍限制(主要是指類(lèi)之間的訪問(wèn)限制)盡可能小,那么內(nèi)置類(lèi)是一個(gè)很好的辦法。
筆者把內(nèi)置類(lèi)分為三類(lèi):類(lèi)內(nèi)置類(lèi)(Nested Class),實(shí)例內(nèi)置類(lèi)(Inner Class)和布局內(nèi)置類(lèi)(Local Class)。下面分別介紹這三種類(lèi)的使用場(chǎng)景和設(shè)計(jì)方法。
類(lèi)內(nèi)置類(lèi)(Nested Class),在內(nèi)置類(lèi)中使用得最多的一類(lèi)。其作為類(lèi)的一部分,隨外層類(lèi)(Enclosing Class)順序地被加載。它的好處是,定義的類(lèi)內(nèi)置類(lèi)僅此一次被創(chuàng)建并加載,可視外層類(lèi)的類(lèi)成員。那么使用場(chǎng)景不難得出,對(duì)于實(shí)例成員不感冒,只關(guān)心類(lèi)成員,并且減少?zèng)]有必要重復(fù)類(lèi)的創(chuàng)建和加載。在大多數(shù)實(shí)際情況中,這個(gè)模式已經(jīng)足夠了。舉一個(gè)的JDK里面的例子,迭代Map的時(shí)候,鍵值對(duì)實(shí)體接口java.util.Map.Entry,其定義在java.util.Map接口中,自然其修飾符是public static final。
為了客戶(hù)端程序能夠利用java.util.Map.Entry,JDK暴露了它。一般來(lái)說(shuō),private final static是通用的設(shè)計(jì)。外層類(lèi)對(duì)其是完全可視的,因此private 是沒(méi)有問(wèn)題的。至于final的修飾,要談到筆者設(shè)計(jì)經(jīng)驗(yàn)中的一個(gè)原則,盡量使用final修飾可修飾的。其中有幾個(gè)好處,比如線程安全、拒絕子類(lèi)、標(biāo)準(zhǔn)化(在后面的設(shè)計(jì)文章中會(huì)詳細(xì)說(shuō)明)等。在內(nèi)置類(lèi)設(shè)計(jì)中,不應(yīng)該期望其他類(lèi)繼承這個(gè)類(lèi),更不要期望其他人會(huì)使用的內(nèi)置類(lèi)了。又回到JDK,大家會(huì)發(fā)現(xiàn)java.util.HashMap內(nèi)部定義不少的類(lèi)內(nèi)置類(lèi)。
使用下了代碼實(shí)例補(bǔ)充說(shuō)明上述:
package org.mercy.design; /** * OuterClass 是外層類(lèi),NestedClass 類(lèi)內(nèi)置類(lèi) * @author mercyblitz */ public class OuterClass { /** * private final static 是類(lèi)內(nèi)置類(lèi)的通用設(shè)計(jì)技巧 */ private final static class NestedClass { } } |
代碼-1
如果OuterClass類(lèi)中有實(shí)例變量的話,顯然NestedClass是不可見(jiàn)的,也是不適用的(因?yàn)樗穷?lèi)的一部分)。
這個(gè)時(shí)候,利用實(shí)例內(nèi)置類(lèi)可以解決這類(lèi)問(wèn)題。
示例代碼如下:
package org.mercy.design; /** * OuterClass2 是外層類(lèi),InnerClass 實(shí)例內(nèi)置類(lèi) * * @author mercyblitz */ public class OuterClass2 { private String message; /** * 使用private final 是一種好習(xí)慣。:D */ private final class InnerClass { /** * 輸出OuterClass2消息 */ private void outputMessageFromOuterClass2() { // 注意,this的命名空間 System.out.println(OuterClass2.this.message); } } } |
代碼-2
在“代碼-2”中,InnerClass利用OuterClass2的message字段作為輸出。
可能有人會(huì)說(shuō),InnerClass這種實(shí)例內(nèi),為了得到這個(gè)類(lèi),不得不創(chuàng)建一個(gè)實(shí)例,太浪費(fèi)資源了,為什么不直接把OuterClass實(shí)例作為參數(shù),直接傳入到InnerClass的方法呢?
沒(méi)錯(cuò),可以那么做。不過(guò)單從訪問(wèn)外層類(lèi)的實(shí)例變量而言,利用實(shí)例內(nèi)置類(lèi)是有點(diǎn)顯得浪費(fèi)。如果客戶(hù)端利用了泛型編程的話,情況就會(huì)不同。
總所周知,泛型設(shè)計(jì)能夠提高靈活性,可是也有很多限制。模版參數(shù)類(lèi)型是跟隨其寄主類(lèi)的,模板參數(shù)類(lèi)型是不會(huì)寫(xiě)入class文件中的,這就是為什么反射(Reflection)不能解析出類(lèi)的模板參數(shù)類(lèi)型。但是,模板參數(shù)類(lèi)型在實(shí)例(對(duì)象)范圍是可用的(或可視的)。如果內(nèi)置類(lèi)中想要利用外層類(lèi)的模板參數(shù)類(lèi)型的話,那么實(shí)例內(nèi)置類(lèi)就有很大用處。
例子如下:
package org.mercy.design; /** * OuterClass3 是外層類(lèi),InnerClass 實(shí)例內(nèi)置類(lèi) * * @author mercyblitz * @param * 模板參數(shù)類(lèi)型,實(shí)例內(nèi)置類(lèi)可以利用 */ public class OuterClass3 { private T data; /** * 使用private final 是一種好習(xí)慣。:D */ private final class InnerClass { public void setData(T newData) { OuterClass3.this.data = newData; // DOES Other things } } } |
代碼-3
“代碼-3”中的實(shí)例內(nèi)置類(lèi)利用外層類(lèi)OuterClass3中的模板參數(shù)T,作為setData參數(shù)的類(lèi)型。
看似類(lèi)內(nèi)置類(lèi)和實(shí)例內(nèi)置類(lèi)已經(jīng)足夠使用了。考慮這么一個(gè)場(chǎng)景,一個(gè)方法利用了內(nèi)置類(lèi)來(lái)實(shí)現(xiàn)功能,這個(gè)方法中的變量需要被內(nèi)置類(lèi)來(lái)利用,通常可以把變量作為參數(shù),傳入內(nèi)置類(lèi)構(gòu)造器或者其方法中,這也是通常的方法。不過(guò)利用布局內(nèi)置類(lèi)(Local Class)更為方便,因?yàn)榫植績(jī)?nèi)置類(lèi)是在塊中(方法也是一種特殊的塊)定義的,這樣就很好的解決了上下文的參數(shù)傳遞問(wèn)題。
參看代碼:
package org.mercy.design; /** * OuterClass4 是外層類(lèi),Printer 局部?jī)?nèi)置類(lèi) * * @author mercyblitz */ public class OuterClass4 { public void print(byte[] bytes) { final String message = new String(bytes); /** * 名為Printer LocalClass,不必把message作為參數(shù)傳遞。 */ 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”的示例中,有人可能會(huì)說(shuō),這看不出什么好處呀?!如果內(nèi)置類(lèi)依賴(lài)的變量超過(guò)4個(gè)(Effective Java書(shū)中提到超過(guò)四個(gè)參數(shù)的話,不利于維護(hù)),那么局部?jī)?nèi)置類(lèi)是不是方便維護(hù)呢?
順便提到,匿名內(nèi)置類(lèi)是局部?jī)?nèi)置類(lèi)的一種。
不難發(fā)現(xiàn),局部?jī)?nèi)置類(lèi)的缺點(diǎn)是代碼混雜(方法和類(lèi)混在一起),如果依賴(lài)局部變量不多的情況下,在一定程度上面,增加了維護(hù)成本。
最后的篇幅來(lái)總結(jié)一下這幾種內(nèi)置類(lèi)的特點(diǎn),以及使用場(chǎng)景和設(shè)計(jì)技巧。
共同特點(diǎn),不想暴露并且不期望被外部使用或者擴(kuò)張(強(qiáng)調(diào)一下,一般類(lèi)中私有和包內(nèi)私用都是好的設(shè)計(jì)技巧),通過(guò)類(lèi)的四大特性提供更優(yōu)于方法的方法和外部?jī)?nèi)實(shí)現(xiàn)交互,從而達(dá)到良好設(shè)計(jì)目的。
對(duì)于類(lèi)內(nèi)置類(lèi)(Nested Class),適合的絕大多數(shù)內(nèi)置類(lèi)場(chǎng)景,利于維護(hù)。但不適合利用外層類(lèi)模板參數(shù)類(lèi)型和實(shí)例變量。
就實(shí)例內(nèi)置類(lèi)(Inner Class),適合利用外層類(lèi)模板參數(shù)類(lèi)型和實(shí)例變量,更好的彈性設(shè)計(jì)。可是加載其類(lèi)時(shí),必須實(shí)例化外部類(lèi),造成不必要開(kāi)銷(xiāo),因此不是必須,盡量使用類(lèi)內(nèi)置類(lèi)。
局部?jī)?nèi)置類(lèi)(Local Class),適合多局部變量依賴(lài)的場(chǎng)景,提高可維護(hù)性,相反就不適合。
因此,內(nèi)置類(lèi)的設(shè)計(jì)和其他面向?qū)ο笤O(shè)計(jì)類(lèi)似,根據(jù)適合的場(chǎng)景來(lái)合理設(shè)計(jì)。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:網(wǎng)絡(luò)轉(zhuǎn)載