Java 初學者——繼承設計技巧

       下面給出一些對設計繼承關系很有幫助的建議:

       (1)將公共操作和域放置在超類

       (2)不要使用受保護的域

       有些程序員認為,將大多數的實例域定義為protected是一個不錯的主意,只有這樣,子類才能夠在需要的時候直接訪問他們。然而,protected機制并不能夠帶來更好的保護,其原因主要有兩點。第一,子類集合是無限制的,任何一個人都能夠由某個類派生一個子類,并編寫代碼以直接訪問protected的實例域,從而破壞了封裝性。第二,在Java程序設計語言中,在同一個包中的所有類都可以訪問protected域,而不管它是否為這個類的子類。

       (3)使用繼承實現“is-a”關系

       使用繼承很容易得到節省代碼的目的,但有時候也被人們濫用了。例如,假設需要定義一個鐘點工(Contractor)類。鐘點工的信息包含姓名和雇傭日期,但是沒有薪水。他們按小時計薪,并且不會因為拖延時間而獲得加薪。這似乎在誘導人們由Employee派生出子類Constractor,然后再增加一個hourlyWage域。

class Contractor extends Employee

{

       ….

       private double hourlyWage;

}

 

       這并不是一個好主意。因為這樣一來,每個鐘點工對象中都包含了薪水和計時工資這兩個域。在實現打印支票或稅單方法的時候,會到來無盡的麻煩,并且會多些很多代碼。

       鐘點工與雇員之間不屬于“is-a”關系。鐘點工不是特殊的雇員。

       (4)除非所有繼承的方法都有意義,否則不要使用繼承。

       假設想編寫一個Holiday類。毫無疑問,每個假日也是一日,并且一日可以用GregorianCalendar類的實例表示,因此可以使用繼承。

class Holiday extends GregorianCalendar

{

       ………….

}

 

       很遺憾,在繼承的操作中,假日集不是封閉的。在GregorianCalendar中有一個共有方法add,可以將假日轉換成非假日:

Holiday Christmas;

       christmas.add(Calendar.DAY_OF_MONTH,12);

       因此,繼承對于這個例子來時并不太適宜。

(5)在覆蓋方法的時候,不要改變預期的行為。

       置換原則不僅應用于語法,而且也可以應用于行為,這似乎更加重要。在覆蓋一個方法的時候,不應該毫無緣由的改變行為的內涵。就這一點而言,編譯器不會提供任何幫助,即編譯器不會檢查重定義的方法是否有意義。例如,可以重定義Holiday類中的add方法“修正”原方法的問題,或什么也不做,或拋出一個異常,或繼續到下一個假日。然而這些都違反了置換原則,語句序列

int d1=x.get(Calendar.DAY_OF_MONTH);

x.add(Calendar.DAY_OF_MONTH,1);

int d2=x.get(Calendar.DAY_OF_MONTH);

System.out.println(d2-d1);

 

       不管x屬于GregorianCalendar類,還是屬于Holiday類,執行上述語句后都應該得到預期的行為。

       當然,這樣可能會引起某些爭議。人們可能就預期行為的含義爭論不休。例如,有些人爭論說,置換原則要求Manager.equals不處理bonus域,因為Employee.equals沒有它。實際上,憑空討論這些問題毫無意義。關鍵在于,在覆蓋子類中的方法時,不要偏離最初的實際想法。

       (6)使用多態,而非類型信息。

       無論什么時候,對于下面這種形式的代碼:

if(x is of type1)

action1(x);

else if (x is of type2)

       action2(x)

 

都應該考慮使用多態性。

action1與 action2表示的是相同的概念嗎?如果是相同的概念,就應該為這個概念定義一個方法,并將其放置在兩個類的超類或接口中,然后,就可以調用x.action( );以便使用多態性提供的動態分派機制執行相應的動作。

使用多態犯法或接口編寫的代碼比使用對多種類型進行檢測的代碼更加易于為何和擴展。

(7)不要過多地使用反射

反射機制使得人們可以通過在運行時查看域和方法,讓人們編寫出更具有通行的程序。這種功能對于編寫系統程序來說及其實用,但是通常不是用于編寫應用程序。反射是很脆弱的,即編譯器很難幫助人們發現程序中的錯誤。任何錯誤只能在運行時才被發現,并導致異常。