Item 12:把類和成員的可訪問范圍降到最低
好的模塊設(shè)計(jì)應(yīng)該盡最大可能封裝好自己的內(nèi)部信息,這樣可以把模塊之間的耦合程度降到最低。開發(fā)得以并行,無疑這將加快開發(fā)的速度,便于系統(tǒng)地維護(hù)。Java中通過訪問控制符來解決這個(gè)問題。
- public表示這個(gè)類在任何范圍都可用。
- protected表示只有子類和包內(nèi)的類可以使用
- private-package(default)表示在包內(nèi)可用
- private表示只有類內(nèi)才可以用
你在設(shè)計(jì)一個(gè)類的時(shí)候應(yīng)該盡量的按照4321得順序設(shè)計(jì)。如果一個(gè)類只是被另一個(gè)類使用,那么應(yīng)該考慮把它設(shè)計(jì)成這個(gè)類的內(nèi)部類。通常public的類不應(yīng)該有public得字段,不過我們通常會(huì)用一個(gè)類來定義所有的常量,這是允許的。不過必須保證這些字段要么是基本數(shù)據(jù)類型要么引用指向的對(duì)象是不可修改的。不然他們將可能被修改。例如下面的定義中data就是不合理的,后面兩個(gè)沒有問題。
public class Con
{
public static final int[] data = {1,2,3};// it is bad
public static final String hello = "world";
public static final int i = 1;
}
Item 13:不可修改的類更受青睞
不可修改的類意思是他們一經(jīng)創(chuàng)建就不會(huì)改變,例如String類。他們的設(shè)計(jì)、實(shí)現(xiàn)都很方便,安全性高——它們是線程安全的。設(shè)計(jì)不可修改類有幾點(diǎn)規(guī)則:
- 不要提供任何可以修改對(duì)象的方法
- 確保沒有方法能夠被覆蓋,可以通過把它聲明為final
- 所有字段設(shè)計(jì)成final
- 所有字段設(shè)計(jì)成private
- 確保外部不能訪問到類的可修改的組件
不可修改類也有個(gè)缺點(diǎn)就是創(chuàng)建不同值得類的時(shí)候要?jiǎng)?chuàng)建不同的對(duì)象,String就是這樣的。通常有個(gè)解決的辦法就是提供一個(gè)幫助類來彌補(bǔ),例如StringBuffer類。
Item 14:化合(合成)比繼承更值得考慮
實(shí)現(xiàn)代碼重用最重要的辦法就是繼承,但是繼承破壞了封裝,導(dǎo)致軟件的鍵壯性不足。如果子類繼承了父類,那么它從父類繼承的方法就依賴父類的實(shí)現(xiàn),一旦他改變了會(huì)導(dǎo)致不可預(yù)測(cè)的結(jié)果。作者介紹了InstrumentedHashSet作為反例進(jìn)行說明,原因就是沒有明白父類的方法實(shí)現(xiàn)。作者給出的解決辦法是通過化合來代替繼承,用包裝類和轉(zhuǎn)發(fā)方法來解決問題。把想擴(kuò)展的類作為本類的一個(gè)private final得成員變量。把方法參數(shù)傳遞給這個(gè)成員變量并得到返回值。這樣做的缺點(diǎn)是這樣的類不適合回掉框架。繼承雖然好,我們卻不應(yīng)該濫用,只有我們能確定它們之間是is-a得關(guān)系的時(shí)候才使用。
Item 15:如果要用繼承那么設(shè)計(jì)以及文檔都要有質(zhì)量保證,否則就不要用它
為了避免繼承帶來的問題,你必須提供精確的文檔來說明覆蓋相關(guān)方法可能出現(xiàn)的問題。在構(gòu)造器內(nèi)千萬不要調(diào)用可以被覆蓋的方法,因?yàn)樽宇惛采w方法的時(shí)候會(huì)出現(xiàn)問題。
import java.util.*;
public class SubClass extends SuperClass
{
private final Date date;
public SubClass()
{
date = new Date();
}
public void m()
{
System.out.println(date);
}
public static void main(String[] args)
{
SubClass s = new SubClass();
s.m();
}
}
class SuperClass
{
public SuperClass()
{
m();
}
public void m()
{
}
}
由于在date被初始化之前super()已經(jīng)被調(diào)用了,所以第一次輸出null而不是當(dāng)前的時(shí)間。
由于在Clone()或者序列化的時(shí)候非常類似構(gòu)造器的功能,因此readObject()和clone()方法內(nèi)最好也不要包括能被覆蓋的方法。
Item 16:在接口和抽象類之間優(yōu)先選擇前者
接口和抽象類都用來實(shí)現(xiàn)多態(tài),不過我們應(yīng)該優(yōu)先考慮用接口。知道嗎?James說過如果要讓他重新設(shè)計(jì)java的話他會(huì)把所有都設(shè)計(jì)成接口的。抽象類的優(yōu)點(diǎn)是方便擴(kuò)展,因?yàn)樗潜焕^承的,并且方法可以在抽象類內(nèi)實(shí)現(xiàn),接口則不行。
Item 17:接口只應(yīng)該用來定義類型
接口可以這樣用的 Collection c = new xxxx();這是我們最常用的。不要把接口用來做其他的事情,比如常量的定義。你應(yīng)該定義一個(gè)類,里面包含public final static 得字段。
Item 18: 在靜態(tài)和非靜態(tài)內(nèi)部類之間選擇前者
如果一個(gè)類被定義在其他的類內(nèi)部那么它就是嵌套類,可以分為靜態(tài)內(nèi)部類、非靜態(tài)內(nèi)部類和匿名類。
static member class 得目的是為enclosing class服務(wù),如果還有其他的目的,就應(yīng)該把它設(shè)計(jì)成top-level class。nonstatic member class是和enclosing class instance關(guān)聯(lián)的,如果不需要訪問enclosing class instance的話應(yīng)該把它設(shè)計(jì)成static得,不然會(huì)浪費(fèi)時(shí)間和空間。anonymous class是聲明和初始化同時(shí)進(jìn)行的。可以放在代碼的任意位置。典型應(yīng)用是Listener 和process object例如Thread。