Principle: 裡氏替換原則
情境:老爸開了一間速食餐廳
1 2 3 4 5 6 7 8 9
| <?php
namespace App\SOLID\LSP\Restaurant\Contracts;
interface Eatable { public function beEaten(); }
|
- 接著供應的餐點類型:漢堡、炸雞、雞塊,它們都必須實作餐點介面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php
namespace App\SOLID\LSP\Restaurant\Food;
use App\SOLID\LSP\Restaurant\Contracts\Eatable;
class Burger implements Eatable { public function beEaten() { return '招牌漢堡被吃了'; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php
namespace App\SOLID\LSP\Restaurant\Food;
use App\SOLID\LSP\Restaurant\Contracts\Eatable;
class FriedChicken implements Eatable { public function beEaten() { return '招牌炸雞被吃了'; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php
namespace App\SOLID\LSP\Restaurant\Food;
use App\SOLID\LSP\Restaurant\Contracts\Eatable;
class ChickenNuggets implements Eatable { public function beEaten() { return '招牌雞塊被吃了'; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?php
namespace App\SOLID\LSP\Restaurant;
use App\SOLID\LSP\Restaurant\Contracts\Eatable; use App\SOLID\LSP\Restaurant\Food\Burger; use Exception; use App\SOLID\LSP\Restaurant\Food\ChickenNuggets; use App\SOLID\LSP\Restaurant\Food\FriedChicken;
class DadRestaurant { public function getFood($money): Eatable { if (!is_int($money)) { throw new Exception('我們只收現金'); }
$randomNumber = rand(1, 3);
switch ($randomNumber) { case 1: return new Burger(); break;
case 2: return new FriedChicken(); break;
case 3: return new ChickenNuggets(); break; } } }
|
這邊可以發現,老爸餐廳的出餐是很隨意的,
客人用現金,可以換取餐點。
但餐點可能是漢堡、炸雞或是雞塊其中之一。
時光匆匆,過了幾年,兒子決定繼承老爸餐廳
現在有些客人會使用信用卡等其他的付款方式。
也不希望餐廳餐點總是如此隨意,無法預料。
新開的餐廳決定只供應漢堡這種餐點,並提供其他付款方式。
(即對Input型態更寬鬆,而Output型態更嚴謹)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\SOLID\LSP\Restaurant;
use App\SOLID\LSP\Restaurant\DadRestaurant; use App\SOLID\LSP\Restaurant\Food\Burger; use App\SOLID\LSP\Restaurant\Contracts\Eatable;
class SonRestaurant extends DadRestaurant { public function getFood($goldFlow): Eatable { return new Burger(); } }
|
測試客人消費的情況
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <?php
namespace Tests\Unit\SOLID\LSP;
use PHPUnit\Framework\TestCase; use App\SOLID\LSP\Restaurant\Program; use App\SOLID\LSP\Restaurant\Contracts\Eatable;
class ProgramTest extends TestCase {
protected $sut;
public function setUp(): void { $this->sut = new Program(); }
public function testUseMoneyInDadRestaurant() { $expected = Eatable::class; $money = 100; $actual = $this->sut->getFoodInDadRestaurant($money); $this->assertInstanceOf($expected, $actual); }
public function testUseCardInDadRestaurant() { $card = '信用卡'; $this->expectExceptionMessage('我們只收現金'); $this->sut->getFoodInDadRestaurant($card); }
public function testUseMoneyInSonRestaurant() { $expected = Eatable::class; $money = 100; $actual = $this->sut->getFoodInSonRestaurant($money); $this->assertInstanceOf($expected, $actual); }
public function testUseCardInSonRestaurant() { $expected = Eatable::class; $card = '信用卡'; $actual = $this->sut->getFoodInSonRestaurant($card); $this->assertInstanceOf($expected, $actual); } }
|
透過測試,我們可以發現:
顧客類型 |
付款方式 |
老爸的餐廳 |
兒子的餐廳 |
老顧客 |
現金 |
可以換取餐點 |
可以換取餐點 (但只有漢堡) |
新顧客 |
信用卡 |
無法換取餐點 |
可以換取餐點 |
符合了裡氏替換原則的精神。
ʕ •ᴥ•ʔ:這是一個簡易的範例,細節部分請大家多多包涵。