選擇Java接口還是抽象類
作者
俞良松 本文選自
開放系統世界-賽迪網
年
月
日
很多人有過這樣的疑問
為什麼有的地方必須使用接口而不是抽象類
而在另一些地方
又必須使用抽象類而不是接口呢?或者說
在考慮Java類的一般化問題時
很多人會在接口和抽象類之間猶豫不決
甚至隨便選擇一種
實際上接口和抽象類的選擇不是隨心所欲的
要理解接口和抽象類的選擇原則
有兩個概念很重要
對象的行為和對象的實現
如果一個實體可以有多種實現方式
則在設計實體行為的描述方式時
應當達到這樣一個目標
在使用實體的時候
無需詳細了解實體行為的實現方式
也就是說
要把對象的行為和對象的實現分離開來
既然Java的接口和抽象類都可以定義不提供具體實現的方法
在分離對象的行為和對象的實現時
到底應該使用接口還是使用抽象類呢?
通過抽象類建立行為模型
在接口和抽象類的選擇上
必須遵守這樣一個原則
行為模型應該總是通過接口而不是抽象類定義
為了說明其原因
下面試著通過抽象類建立行為模型
看看會出現什麼問題
假設要為銷售部門設計一個軟件
這個軟件包含一個
發動機
(Motor)實體
顯然無法在發動機對象中詳細地描述發動機的方方面面
只能描述某些對當前軟件來說重要的特征
至於發動機的哪些特征是重要的
則要與用戶(銷售部門)交流才能確定
銷售部門的人要求每一個發動機都有一個稱為馬力的參數
對於他們來說
這是惟一值得關心的參數
基於這一判斷
可以把發動機的行為定義為以下行為
行為
查詢發動機的馬力
發動機將返回一個表示馬力的整數
雖然現在還不清楚發動機如何取得馬力這個參數
但可以肯定發動機一定支持這個行為
而且這是所有發動機惟一值得關注的行為特征
這個行為特征既可以用接口定義
也可以用抽象類定義
為了說明用抽象類定義可能出現的問題
下面用抽象類建立發動機的行為模型
並用Java方法描述行為
代碼如下
public abstract Motor{
abstract public int getHorsepower();
}
在Motor抽象類的基礎上構造出多種具體實現
例如A型發動機
B型發動機等
再加上系統的其它部分
最後得到
版的軟件並交付使用
一段時間過去了
現在要設計
版的軟件
在評估
版軟件需求的過程中
發現一小部分發動機是電池驅動的
而電池需要一定的充電時間
銷售部門的人希望能夠通過計算機查閱充電時間
根據這一要求定義一個新的行為
如圖
所示
行為
查詢電驅動發動機的充電時間
發動機將返回一個表示充電時間的整數
用Java方法來描述這個行為
代碼如下
public abstract BatteryPoweredMotor extends Motor{
abstract public int getTimeToRecharge();
}
在銷售部門的軟件中
電驅動發動機也以類的形式實現
但這些類從BatteryPoweredMotor而不是Motor派生
這些改動加入到
版軟件之後
銷售部門很滿意
隨著業務的不斷發展
不久之後光驅動的發動機出現了
銷售部門要求光驅動發動機需要一定光能才能運轉
光能以流明(Lumen)度量
這個信息對客戶很重要
因為下雨或多雲的天氣裡
某些光驅動發動機可能無法運轉
銷售部門要求為軟件增加對光驅動發動機的支持
所以要定義一個新的行為
行為
查詢光驅動發動機能夠正常運轉所需要的最小流明數
發動機返回一個整數
再定義一個抽象類並把行為
轉換成Java方法
代碼如下
public abstract SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
選擇Java接口還是抽象類
作者
俞良松 本文選自
開放系統世界-賽迪網
年
月
日
很多人有過這樣的疑問
為什麼有的地方必須使用接口而不是抽象類
而在另一些地方
又必須使用抽象類而不是接口呢?或者說
在考慮Java類的一般化問題時
很多人會在接口和抽象類之間猶豫不決
甚至隨便選擇一種
實際上接口和抽象類的選擇不是隨心所欲的
要理解接口和抽象類的選擇原則
有兩個概念很重要
對象的行為和對象的實現
如果一個實體可以有多種實現方式
則在設計實體行為的描述方式時
應當達到這樣一個目標
在使用實體的時候
無需詳細了解實體行為的實現方式
也就是說
要把對象的行為和對象的實現分離開來
既然Java的接口和抽象類都可以定義不提供具體實現的方法
在分離對象的行為和對象的實現時
到底應該使用接口還是使用抽象類呢?
通過抽象類建立行為模型
在接口和抽象類的選擇上
必須遵守這樣一個原則
行為模型應該總是通過接口而不是抽象類定義
為了說明其原因
下面試著通過抽象類建立行為模型
看看會出現什麼問題
假設要為銷售部門設計一個軟件
這個軟件包含一個
發動機
(Motor)實體
顯然無法在發動機對象中詳細地描述發動機的方方面面
只能描述某些對當前軟件來說重要的特征
至於發動機的哪些特征是重要的
則要與用戶(銷售部門)交流才能確定
銷售部門的人要求每一個發動機都有一個稱為馬力的參數
對於他們來說
這是惟一值得關心的參數
基於這一判斷
可以把發動機的行為定義為以下行為
行為
查詢發動機的馬力
發動機將返回一個表示馬力的整數
雖然現在還不清楚發動機如何取得馬力這個參數
但可以肯定發動機一定支持這個行為
而且這是所有發動機惟一值得關注的行為特征
這個行為特征既可以用接口定義
也可以用抽象類定義
為了說明用抽象類定義可能出現的問題
下面用抽象類建立發動機的行為模型
並用Java方法描述行為
代碼如下
public abstract Motor{
abstract public int getHorsepower();
}
在Motor抽象類的基礎上構造出多種具體實現
例如A型發動機
B型發動機等
再加上系統的其它部分
最後得到
版的軟件並交付使用
一段時間過去了
現在要設計
版的軟件
在評估
版軟件需求的過程中
發現一小部分發動機是電池驅動的
而電池需要一定的充電時間
銷售部門的人希望能夠通過計算機查閱充電時間
根據這一要求定義一個新的行為
如圖
所示
行為
查詢電驅動發動機的充電時間
發動機將返回一個表示充電時間的整數
用Java方法來描述這個行為
代碼如下
public abstract BatteryPoweredMotor extends Motor{
abstract public int getTimeToRecharge();
}
在銷售部門的軟件中
電驅動發動機也以類的形式實現
但這些類從BatteryPoweredMotor而不是Motor派生
這些改動加入到
版軟件之後
銷售部門很滿意
隨著業務的不斷發展
不久之後光驅動的發動機出現了
銷售部門要求光驅動發動機需要一定光能才能運轉
光能以流明(Lumen)度量
這個信息對客戶很重要
因為下雨或多雲的天氣裡
某些光驅動發動機可能無法運轉
銷售部門要求為軟件增加對光驅動發動機的支持
所以要定義一個新的行為
行為
查詢光驅動發動機能夠正常運轉所需要的最小流明數
發動機返回一個整數
再定義一個抽象類並把行為
轉換成Java方法
代碼如下
public abstract SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
}
如圖
所示
SolarPoweredMotor和BatteryPoweredMotor都從Motor抽象類派生
在整個軟件中
%以上的代碼以相同的方式對待所有的發動機
偶爾需要檢查一下發動機是光驅動還是電驅動
使用instanceof實現
代碼如下
[color=#
]
if (instanceof SolarPoweredMotor){
}
if (instanceof BatteryPoweredMotor){
}
無論是哪種發動機
馬力這個參數都很重要
所以在所有派生的抽象類(SolarPoweredMotor和BatteryPoweredMotor)中
getHorsepower()方法都有效
現在銷售部門又有了一種新的發動機
它是一種既有電驅動又有光驅動的雙重驅動發動機
光驅動和電驅動的行為本身沒有變化
但新的發動機同時支持兩種行為
在考慮如何定義新型的光電驅動發動機時
接口和抽象類的差別開始顯示出來了
新的目標是在增加新型發動機的前提下盡量少改動代碼
因為與光驅動發動機
電驅動發動機有關的代碼已經過全面的測試
不存在已知的Bug
為了增加光電驅動發動機
要定義一個新的SolarBatteryPowered抽象類
如果讓SolarBatteryPowered從Motor抽象類派生
SolarBatteryPowered將不支持針對光驅動發動機和電驅動發動機的instanceof操作
也就是說
如果查詢一個光電驅動的發動機是光驅動的
還是電驅動的
得到的答案是
都不是
如果讓SolarBatteryPowered從SolarPoweredMotor(或BatteryPoweredMotor)抽象類派生
類似的問題也會出現
SolarBatteryPowered將不支持針
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26371.html