甚麼是SOLID?
SOLID,是由Robert C. Martin等人提出的5個物件導向設計原則的英文字首組成,分別為單一職責(S)、開閉原則(O)、里氏替換(L)、介面隔離(I)以及依賴反轉(D)。SOLID提供程式開發者良好的設計指引,可以開發出易理解、易維護、易拓展的系統。
里氏替換原則(Liskov Substitution Principle,LSP)
Liskov Substitution Principle,LSP
“Let φ(x) be a property provable about objects x of type T. Then φ(y) should be true for objects y of type S where S is a subtype of T.” – B. Liskov, J. Wing
譯文:如果 S 是 T 的子類型,則適用於 T 物件的內容也適用於 S 物件。
定義不是很好懂,參考很多文章後,自己大概理解為「子類如果屬於父類,則父類應也可以替換成子類,並保持原有功能的正確」。意思是強調繼承時不應修改父類別原有的功能(也可以說是架構),並需要實作所有抽象方法。
有個很經典的例子是”長方形不是正方形”,如果讓長方形去繼承正方形,就不符合長寬等長的限制了。
以C#多型為例
C#在多型應用時,常見的abstract/virtual
…等,編譯器會有明確的限制,例如:
abstract
類別或方法只能定義不能實例化virtual
方法雖然可在父類別實作,但子類別可以覆寫它- 沒有定義
abstract/virtual
的方法則不可覆寫
多多利用這些修飾詞去設計程式框架是重要的。
舉例
最常見的例子是把父類作為容器,像是List<Shape>
,我只需要計算各種形狀面積的功能,不會把Rectangle長寬欄位放在Shape內,否則遇到Polygon、Triangle…等不同形狀就會發生問題。
abstract class Shape
{
public abstract int GetArea();
}
class Rectangle : Shape
{
int width, height;
public override int GetArea() { DoSomething... }
}
class Polygon : Shape
{
public override int GetArea() { DoSomething... }
}
var shapes = new List<Shape>();
foreach(item in shapes)
{
Console.WriteLine(item.GetArea());
}
不過度依賴繼承
繼承雖然可以進行拓展及重複利用,但不是沒有缺點。承上方的例子,如果寫一個含有長寬及計算面積的類別給正方形、長方形繼承,如果遇到平行四邊形或是梯形呢?是不是就顯得這類別有點多餘。
退一步海闊天空,以宏觀角度去看整個架構,了解這類別需要的功能,會發現有些程式碼不一定是必要的。如果繼承類別的過程因為耦合程度太高,無法符合LSP,可以考慮interface去抽出部分功能來降低耦合,或是重新檢視類別之間的關係。
結論
里氏替換原則提供了物件階層關聯之間的約定”子類不能修改父類功能“,不能覆寫非abstract/virtual
的方法,另外在覆寫abstract/virtual
時要注意不能破壞父類原本的功能。
另外在LSP在物件導向設計就像是生物學的生物分類法,定義了界門綱目科屬種,從上到下定義會越來越嚴格,就像是人類跟獼猴都屬於靈長目,所以人與猴都會繼承自靈長目,且不會違反其定義,否則就會需要重新檢視了。
發佈留言