李氏替換原則(Liskov Substitution Principle: LSP)

子類型必須能夠替換掉他們的父類型

這里的所有觀點摘抄自《敏捷軟件開發(fā)原則戈抄、模式與實踐》歌懒,原著Robert C. Martin具壮,鄧輝等譯茶袒。

李氏替換原則

假設(shè)有一個函數(shù)f梯刚,接受一個指向某個基類B的指針或者引用。如果把B的派生類D的對象作為參數(shù)傳給f薪寓,會導(dǎo)致f出現(xiàn)錯誤行為亡资。那么D就違反了LSP。

另外一種情況向叉,假定要對傳入的對象D做測試锥腻,看D是否滿足f所需要的條件。這個測試就會違反OCP母谎。原因是什么呢旷太?因為f需要的行為是B所具有的行為。
但是對于B的派生類卻沒有這樣的要求销睁。要讓測試D的用例通過供璧,就必須修改f,這樣就違反了對修改封閉的原則冻记,即OCP睡毒。

看一個違反LSP的例子:

struct Point (double x, y);

struct Shape {
    enum ShapeType { square, circle } itsType;
    Shape(ShapeType t) : itsType(t) {}
}

struct Circle: public Shape
{
    Circle(): Shape(circle) {};
    void Draw() const;
    Point itsCenter;
    double itsRadius;
}

struct Square: public Shape
{
    Square(): Shape(square) {};
    void Draw() const;
    Point itsTopLeft;
    double itsSide;
}

void DrawShape(const Shape& s)
{
    if (s.itsType == Shape::square)
        static_cast<const Square&>(s).Draw();
    else if (s.itsType == Shape::circle)
        static_cast<const Circle&>(s).Draw();
}

很明顯的,上述代碼違反了OCP原則冗栗,因為我們新增一種形狀演顾,都會導(dǎo)致源代碼的修改,需要添加新的if else隅居。

這個例子也理所當(dāng)然的違反了LSP钠至,因為派生類Circle和Square并不能替換掉Shape. 除非Shape中包含Draw并且將Draw設(shè)置為虛函數(shù)。

另外一個有名的例子就是矩形和正方形的問題胎源。我們在數(shù)學(xué)中會把正方形當(dāng)做矩形的一個特例棉钧。從而影響到我們在開發(fā)過程中,也會理所當(dāng)然的
認(rèn)為正方形應(yīng)該是從矩形派生而來涕蚤。

但實際上應(yīng)該考慮清楚宪卿,正方形和矩形,并非是一個繼承的關(guān)系万栅。矩形的長寬可以獨(dú)立調(diào)整佑钾,如果正方形派生自矩形,那么正方形的長寬也應(yīng)該可以
獨(dú)立的調(diào)整烦粒。但實際上正方形長寬是固定相等的休溶。

有人可能會說,可以在正方形的類中特殊處理。例如:

void Square::SetWidth(double w)
{
    Rectangle::SetWidth(w);
    Rectangle::SetHeight(w);
}

void Square::SetHeight(double w)
{
    Rectangle::SetWidth(w);
    Rectangle::SetHeight(w);
}

class Rectangle
{
    public:
        virtual void SetWidth(double w);
        virtual void SetHeight(double w);
}

void f(Rectangle& r)
{
    r.SetWidth(23);
}

上面的代碼看起來運(yùn)行沒有問題兽掰,Square類的對象也可以作為參數(shù)傳入f芭碍,但實際上隱藏了本質(zhì)上的問題。也就是認(rèn)知上的問題禾进。

考慮如下的代碼:

void ch(Rectange& r)
{
    r.SetWidth(22);
    r.SetHeight(30);
}

如果傳入了一個正方形的對象豁跑,我們最終期望的正方形邊長應(yīng)該是多大廉涕?使用者只會知道泻云,可以傳入正方形,并期望傳入之后
滿足矩形的行為狐蜕。但實際上會出現(xiàn)令人迷惑的結(jié)果宠纯。

這個有點類似于掛羊頭賣狗肉的感覺,實際上需要的羊肉的味道层释,但是你給的確實狗肉婆瓜,出來的結(jié)果可想而知。

如何識別

經(jīng)過上面的幾個例子贡羔,可以看到廉白,如果單單從一個問題來看,也許系統(tǒng)是滿足條件的乖寒。違反不違反LSP并沒有多大的壞處猴蹂。但是,
從設(shè)計的使用者的角度楣嘁,就可以看出磅轻,違反了LSP原則會給系統(tǒng)的穩(wěn)定性帶來多大的影響。

但是有誰能夠知道設(shè)計的使用者會做出怎樣的合理假設(shè)呢逐虚?大多數(shù)這樣的假設(shè)都很難預(yù)測聋溜。事實上如果試圖去猜測使用者的所有
假設(shè),會讓系統(tǒng)無比復(fù)雜叭爱。最好的方法就是只預(yù)測那些明顯對于LSP違反的情況撮躁。

繼續(xù)深入的思考一下,LSP所描述的是一個嚴(yán)格的IS-A的關(guān)系买雾。我們從認(rèn)知上來看馒胆,正方形似乎是一個矩形,是它的一個特例凝果。
但是從使用者的角度祝迂,正方形卻處處和長方形不兼容。那么真正可以區(qū)分是否滿足LSP(也就是滿足IS-A)的條件是什么呢器净?

  • 對象的行為方式

因為正方形和長方形所預(yù)期的行為方式不相容型雳,所以不滿足LSP。

所以,考察一個繼承關(guān)系是否滿足LSP纠俭,最終看的是派生的類和基類是否具有相同的行為方式沿量。

具體的考察行為方式的辦法有兩種:

  • 基于契約的設(shè)計(DBC)
  • 單元測試中指定契約
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冤荆,隨后出現(xiàn)的幾起案子朴则,更是在濱河造成了極大的恐慌,老刑警劉巖钓简,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乌妒,死亡現(xiàn)場離奇詭異,居然都是意外死亡外邓,警方通過查閱死者的電腦和手機(jī)撤蚊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來损话,“玉大人侦啸,你說我怎么就攤上這事∩デ梗” “怎么了光涂?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拧烦。 經(jīng)常有香客問我忘闻,道長,這世上最難降的妖魔是什么屎篱? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任服赎,我火速辦了婚禮,結(jié)果婚禮上交播,老公的妹妹穿的比我還像新娘重虑。我一直安慰自己,他們只是感情好秦士,可當(dāng)我...
    茶點故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布缺厉。 她就那樣靜靜地躺著,像睡著了一般隧土。 火紅的嫁衣襯著肌膚如雪提针。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天曹傀,我揣著相機(jī)與錄音辐脖,去河邊找鬼。 笑死皆愉,一個胖子當(dāng)著我的面吹牛嗜价,可吹牛的內(nèi)容都是我干的艇抠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼久锥,長吁一口氣:“原來是場噩夢啊……” “哼家淤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起瑟由,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤絮重,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后歹苦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體青伤,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年暂氯,在試婚紗的時候發(fā)現(xiàn)自己被綠了潮模。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亮蛔。...
    茶點故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡痴施,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出究流,到底是詐尸還是另有隱情辣吃,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布芬探,位于F島的核電站神得,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏偷仿。R本人自食惡果不足惜哩簿,卻給世界環(huán)境...
    茶點故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酝静。 院中可真熱鬧节榜,春花似錦、人聲如沸别智。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薄榛。三九已至讳窟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敞恋,已是汗流浹背丽啡。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留硬猫,地道東北人补箍。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓倚评,卻偏偏與公主長得像,于是被迫代替她去往敵國和親馏予。 傳聞我的和親對象是個殘疾皇子天梧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 《Agile Principles, Patterns, and Practices in C#》by Micah...
    丑小丫大笨蛋閱讀 11,993評論 3 18
  • 面向?qū)ο蟮?個基本要素: 封裝、繼承霞丧、多態(tài) 面向?qū)ο蟮?個基本設(shè)計原則: 單一職責(zé)原則(Single-Respos...
    badcyc閱讀 858評論 0 4
  • 這是SOLID原則這一系列的第四篇文章呢岗,主要來描述里氏替換(LSP)原則。LSP指定所有引用基類的地方必須能透明地...
    好大一只龍閱讀 784評論 0 0
  • 六月的一個周一蛹尝,大概中午11:45左右后豫,在我家小區(qū)大門口,我遇見了一個小孩子突那。 是個男孩兒挫酿,估計是建安小學(xué)的小學(xué)生...
    一場春雪閱讀 229評論 0 0
  • 從官網(wǎng)上下載安裝之后,JDK默認(rèn)安裝路徑:/Library/Java/JavaVirtualMachines/jd...
    大玩具閱讀 2,027評論 0 2