封裝、繼承、多態常被稱為「面向物件三大特徵」。但 OO 本質是「世界由物件構成」,與面向過程的世界觀不同。三大特徵實際上是 C++ 相對於 C 的三大語法擴充,甚至在編譯器層面只是「語法糖」。理解這點,有助於我們擺脫教條,回歸設計本質。
教科書常用「四邊形 → 矩形 → 正方形」繼承鏈建模。新手直覺認為合理,資深工程師卻敏銳發現悖論:
若無具體功能需求(如「取邊長」「計算面積」),直接設計 Student、Teacher 類別毫無意義。類別設計必須服務於具體行為契約。
class Quadrangle { // 四邊形
int GetSideLength(int index);
int GetArea();
int m_sides[4];
};
class Rectangle { // 矩形
int GetWidth(); int GetHeight(); int GetArea();
int m_width, m_height;
};
class Square { // 正方形
int GetWidth(); int GetArea();
int m_width;
};
各類別獨立,無繼承關係。簡單、直接、符合開閉原則。
若必須表達概念層次,引入純虛類別(介面):
class IShape { // 介面
virtual int GetArea() = 0;
};
class Quadrangle : public IShape { ... };
class Rectangle : public IShape { ... };
class Square : public IShape { ... };
關鍵洞見:C++ 用純虛類別實作介面,但介面機制 ≠ 繼承機制。介面描述「契約/層次」,繼承用於「實作復用/擴充」,兩者本質不同。
class Quadrangle { ... };
class Rectangle : public Quadrangle { ... }; // 強行加欄位、改行為
class Square : public Rectangle { ... }; // 更糟:正方形不是矩形的特化(寬高可獨立變化)
導致:Square 繼承了 Rectangle 的 SetWidth/SetHeight,違反里氏替換原則(LSP),引發執行期錯誤或需大量防禦性程式碼。
微博:@飛舞的煙灰缸