LSP指得是在程式中使用base class(基礎類別)的地方,
都能夠改用其derived class(沿生類別),
而且不會讓程式出現非預期的結果(例如RuntimeException),
也不會影響程式原本的功能。
亦即,derived class能夠做為base class的substitution(替代品)。
反面範例
最常見的例子,就是正方形與矩形之間的關係。
數學上來說,正方形是一種(is-A)矩形,
假設有個數學系統,
須新增一個能夠計算矩形面積的程式,
會直覺地設計成:
在系統中,設計了printArea method,用來印出矩形的面積:
當系統增加正方形時,會設計成:
當系統要計算Square的面積時,一樣透過printArea method:
上述程式翻譯成中文是:
建立一個正方形(是一種矩形),
設定長度為9,設定寬度為8,
(與正方形特性有所抵觸,因為正方形四邊一樣長,只要設定一個邊即可)
印出面積為64。
這樣的執行結果並不在使用者的預期中,
因為9 * 8 = 72 != 64
使用者只知道resize method接收Rectangle型態的物件,
即使傳入的物件為Square,
使用者預設仍然會認為setWidth與setHeight不會互相影響。
這就造成了問題。
因此,在進行物件導向設計時,不要違反了LSP,
也就是Liskov's Substitution Principle。
正面範例
在改善上述例子的方式前,
先思考Square和Rectangle之間的關係,
以及程式的目的(印出面積)。
針對上面兩點,可以歸納出,
1. Square只須要設定一個邊
2. Rectangle要設定二個邊
3. 兩者都須要印出面積
因此,改善的方式為:
Sqaure不用去繼承Rectangle(雖然數學老師會說正方形是一種矩形)
而是讓Square和Rectangle都去實作Shape interface:
然後以建構子的方式,傳入Rectangle的長寬和Square的邊。
之後就能夠正常地使用Rectangle與Square:
改善過後,
Rectangle維持長與寬,而Square只須要設定邊長
一來Rectangle與Square變得更明確,
二來程式也變得容易擴充。
結論
在設計程式時,須考慮base class與dervied class之間的關係,
不恰當的base class會影響dervied class,使其出現不必要的method(參考ISP),
同樣地,實作dervied class時,也必須思考到是否符合LSP,
避免程式發生非預期的問題。
沒有留言:
張貼留言