範例:電力系統(依賴反轉原則)

Principle: 依賴反轉原則


情境:目前電力系統採用火力發電

  • 火力發電
1
2
3
4
5
6
7
8
9
10
11
12
<?php

namespace App\SOLID\DIP\PowerSystems;

class ThermalPower
{
public function generatePower()
{
return '電力';
}
}

  • 電力系統
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace App\SOLID\DIP\PowerSystems;

use App\SOLID\DIP\PowerSystems\ThermalPower;

class Program
{
public function getPower()
{
$thermalPower = new ThermalPower();
return $thermalPower->generatePower();
}
}

隨著科技發展,現在我們想要改成用風力發電來取代火力發電,
卻發現原本的程式,強耦合在ThermalPower
(依賴了具體的類別)

我們決定定義一個抽象的介面,
讓程式依賴在介面,並由各個發電方式實作介面。
改變彼此的依賴關係。


需求一:定義抽象介面,改變電力系統與火力發電的依賴關係

  • 首先定義發電介面
1
2
3
4
5
6
7
8
9
<?php

namespace App\SOLID\DIP\PowerSystems\Contracts;

interface PowerGeneratable
{
public function generatePower();
}

  • 接著讓火力發電實作發電介面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\SOLID\DIP\PowerSystems;

use App\SOLID\DIP\PowerSystems\Contracts\PowerGeneratable;

class ThermalPower implements PowerGeneratable
{
public function generatePower()
{
return '電力';
}
}

  • 最後改寫原本的電力系統
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\SOLID\DIP\PowerSystems;

use App\SOLID\DIP\PowerSystems\Contracts\PowerGeneratable;

class Program
{
public function getPower(PowerGeneratable $powerGeneration)
{
return $powerGeneration->generatePower();
}
}

(註:現在要用什麼樣的發電方式,會交由客戶端決定)

  • 客戶端使用火力發電(提供測試程式碼,供參考)
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
<?php

namespace Tests\Feature\SOLID\DIP\PowerSystems;

use PHPUnit\Framework\TestCase;
use App\SOLID\DIP\PowerSystems\Program;
use App\SOLID\DIP\PowerSystems\ThermalPower;

class ProgramTest extends TestCase
{
/**
* @var Program
*/
protected $sut;

protected function setUp(): void
{
$this->sut = new Program();
}

public function testGetPowerByThermalPower()
{
$expected = '電力';
$powerGeneration = new ThermalPower();
$actual = $this->sut->getPower($powerGeneration);
$this->assertEquals($expected, $actual);
}
}


需求二:新增風力發電

  • 實作風力發電
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\SOLID\DIP\PowerSystems;

use App\SOLID\DIP\PowerSystems\Contracts\PowerGeneratable;

class WindPower implements PowerGeneratable
{
public function generatePower()
{
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
37
38
<?php

namespace Tests\Feature\SOLID\DIP\PowerSystems;

use PHPUnit\Framework\TestCase;
use App\SOLID\DIP\PowerSystems\Program;
use App\SOLID\DIP\PowerSystems\ThermalPower;
use App\SOLID\DIP\PowerSystems\WindPower;

class ProgramTest extends TestCase
{
/**
* @var Program
*/
protected $sut;

protected function setUp(): void
{
$this->sut = new Program();
}

public function testGetPowerByThermalPower()
{
$expected = '電力';
$powerGeneration = new ThermalPower();
$actual = $this->sut->getPower($powerGeneration);
$this->assertEquals($expected, $actual);
}

public function testGetPowerByWindPower()
{
$expected = '電力';
$powerGeneration = new WindPower();
$actual = $this->sut->getPower($powerGeneration);
$this->assertEquals($expected, $actual);
}
}


最後附上類別圖比較:

  • 依賴具體的火力發電
  • 依賴抽象的發電介面,並由各個發現方式實作介面
    (可以觀察火力發電與風力發電的依賴方向,是不是反轉了呢)

ʕ •ᴥ•ʔ:透過遵守依賴反轉原則,我們也讓程式符合開放封閉原則