里氏替換原理,第三原理 SOLID
子類別永遠不應該影響或修改父類別的類型定義。
這就是里氏替換原理,在本文中我們也將看到它的應用範例。
4 minuti
的概念 里氏替換原則, 是由 芭芭拉·利斯科夫 在一個 1987年會議的主題演講 然後 1994 年與 Jannette Wing 一起發表在一篇文章中。
他們原來的定義如下: 子類別永遠不應該影響或修改父類別的類型定義。
原則 里氏替換
假設q(x)是類型T的對象x的可證明性質。那麼q(y)對於類型S的對象y應該是可證明的,其中S是T的子類型。
隨後,隨著《 堅實的原則 di 羅伯特·馬丁 在他的《敏捷軟體開發、原則、模式和實踐》一書中,該定義隨後在《C# 中的敏捷原則、模式和實踐》一書的C# 版本中重新出版,該定義被稱為里氏替換原則。
這讓我們想到了 Robert C. Martin 給的定義: 子類型必須可以用其基本類型替換。
更簡單地講,子類將必須以不破壞客戶角度的功能的方式覆蓋父類的方法。 這是一個簡單的示例來演示該概念。
class Vehicle {
function startEngine() {
// Default engine start functionality
}
function accelerate() {
// Default acceleration functionality
}
}
給定一個Vehicle類-它可以是抽像類-和兩個實現:
class Car extends Vehicle {
function startEngine() {
$this->engageIgnition();
parent::startEngine();
}
private function engageIgnition() {
// Ignition procedure
}
}
class ElectricBus extends Vehicle {
function accelerate() {
$this->increaseVoltage();
$this->connectIndividualEngines();
}
private function increaseVoltage() {
// Electric logic
}
private function connectIndividualEngines() {
// Connection logic
}
}
class Driver {
function go(Vehicle $v) {
$v->startEngine();
$v->accelerate();
}
}
如果可以使用Vehicle,則客戶端類應該能夠同時使用兩者。
這使我們可以輕鬆地在OCP中使用模板方法設計模式。
您可能也對第二個 SOLID 原則感興趣: https://bloginnovazione.it/open-closed-secondo-principio-s-o-l-i-d/3906/
根據我們先前對開閉原則的經驗,我們可以得出里氏替換原則與OCP密切相關的結論。事實上,「違反里氏違反原則就是對 OCP 的潛在違反」(Robert C. Martin),而模板方法設計模式是尊重和實施里氏替換原則的經典示例,而這又是一種的解決方案也尊重OCP。
違反里氏替換原則的範例
class Rectangle {
private $topLeft;
private $width;
private $height;
public function setHeight($height) {
$this->height = $height;
}
public function getHeight() {
return $this->height;
}
public function setWidth($width) {
$this->width = $width;
}
public function getWidth() {
return $this->width;
}
}
讓我們從一個基本的幾何形狀開始,一個矩形。 這只是一個簡單的數據對象,具有用於寬度和高度的設置器和獲取器。 想像一下,我們的應用程序可以正常工作,並且已經在多個客戶端上進行了部署。 現在他們需要一個新功能。 他們需要能夠操縱正方形。
在現實生活中,在幾何學中,正方形是矩形的特殊形狀。因此,我們可以嘗試實作一個擴展 Rectangle 類別的 Square 類別。人們常說子類就是父類,這種表達方式也符合里氏替換原則,至少乍看之下是如此。
class Square extends Rectangle {
public function setHeight($value) {
$this->width = $value;
$this->height = $value;
}
public function setWidth($value) {
$this->width = $value;
$this->height = $value;
}
}
正方形是寬度和高度相等的矩形,我們可以像上一個示例一樣執行奇怪的實現。 我們可以覆蓋兩個設置器來同時設置高度和寬度。 但是,這將如何影響客戶端代碼?
class Client {
function areaVerifier(Rectangle $r) {
$r->setWidth(5);
$r->setHeight(4);
if($r->area() != 20) {
throw new Exception('Bad area!');
}
return true;
}
}
可以想像有一個客戶端類來檢查矩形的區域並在錯誤的情況下拋出異常。
function area() {
return $this->width * $this->height;
}
我們顯然將上述方法添加到了Rectangle類中以提供該區域。
class LspTest extends PHPUnit_Framework_TestCase {
function testRectangleArea() {
$r = new Rectangle();
$c = new Client();
$this->assertTrue($c->areaVerifier($r));
}
}
我們透過向區域驗證器發送空矩形物件來建立一個簡單的測試,並且測試通過。如果我們的 Square 類別定義正確,將其傳送到客戶端的 areaVerifier() 不應破壞其功能。畢竟,從任何數學意義上來說,正方形都是長方形。但這是我們班嗎?
function testSquareArea() {
$r = new Square();
$c = new Client();
$this->assertTrue($c->areaVerifier($r));
}
所以,我們的 Square 類別畢竟不是 Rectangle。 它打破了幾何定律。 它失敗並違反了里氏替換原則.