Liskov substitution principle
里氏替換原則(Liskov substitution principle
):子類(Subclass
)必須要可以替代父類(Super class
),即任何父類出現的地方,都可以使用其子類替換,而不影響程式的正確性。
在考慮里氏替換原則時,可以遵循以下幾點:
避免移除特性
:如果在父類的行為中,如果子類中沒有這個特性的話,你可能違反了里氏替換原則(If you are removing features when subclassing, you are likely breaking Liskov substitution
)。在設計時,- 如果子類中出現了
NotImplementedException
,那你可能違反了里氏替換原則 - 如果出現只能是空白的方法也可能違反了里氏替換原則
- 如果子類中出現了
保持抽象簡單
:盡量讓抽象保持簡單(Keep abstractions simple
):如果在基類放入越多的邏輯,那你有很大的機率違反了里氏替換原則。基類應只包含子類可以共有的方法。- 子類需要有與基類相同的公有成員(
A subclass needs to have the same public members as the base class
):這些成員在呼叫時還需要具有相同的行為。 先考慮類的API再建立層次結構
:在建立類的層次結構前先考慮它們的API(Consider the class API before establishing class hierarchies
):現實中的分類並不總能完全轉為類的結構,例如汽車(Car)與火車(Train)皆是車輛但它們不能直接繼承同一個父類,最好是將它們分開繼承優先考慮組合而非繼承
(Favor composition over inheritance
):在實作時,先考慮使用介面 (Interface
) 或是將行為委託給其他類,而不是直接使用繼承。
例如:假設有一個交通工具 (Vehicle) 的類層次結構,會思考:汽車 (Car) 和卡車 (Truck) 繼承自交通工具 (Vehicle),
1 | public class Vehicle |
1 | public class Navigator |
但若是將火車(Train
)作為車輛(Vehicle
)的子類則會違反里氏替換原則
,這是因為火車沒有右轉(TurnRight
)與左轉(TurnLeft
)的行為,因此火車不能作為車輛(Vehicle
)的子類。
為了修正,可以將轉彎(Turn
)和移動(Move
)動作抽象為介面可轉彎(ITurnable
)和可移動(IMovable
)
1 | public interface ITurnable |
並將車輛分為可以在一般道路上行駛的車輛RoadVehicle
,它實作可轉彎(ITurnable
)和可移動(IMovable
)
1 | public class RoadVehicle : IMovable, ITurnable |
與需要在軌道上行駛的車輛RailVehicle
,它只實作可移動(IMovable
)
1 | public class RailVehicle : IMovable |
最後,定義汽車和火車的類:
1 | public class Car : RoadVehicle |
這樣,Navigator 類可以安全地操作所有可以移動的交通工具,而不需要關心它們是否能轉彎:
1 | public class Navigator |