範例:咖啡機(介面隔離原則)

Principle: 介面隔離原則


情境:目前我們有一台全自動咖啡機

  • 首先定義全自動咖啡機介面
1
2
3
4
5
6
7
8
9
10
11
<?php

namespace App\SOLID\ISP\CoffeeMachine\Contracts;

interface AutomaticCoffeeMachineInterface
{
public function grind($coffeeBeans);

public function brew($coffeePowder);
}

  • 接著是全自動咖啡機
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

namespace App\SOLID\ISP\CoffeeMachine;

use App\SOLID\ISP\CoffeeMachine\Contracts\AutomaticCoffeeMachineInterface;

class AutomaticCoffeeMachine implements AutomaticCoffeeMachineInterface
{
public function grind($coffeeBeans)
{
if ($coffeeBeans == '咖啡豆') {
return '咖啡粉';
}
}

public function brew($coffeePowder)
{
if ($coffeePowder == '咖啡粉') {
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
<?php

namespace App\SOLID\ISP\CoffeeMachine;

class Program
{
protected $coffeeGrinder;

protected $coffeeMaker;

public function getCoffeeByAutomaticCoffeeMachine($coffeeBeans)
{
$automaticCoffeeMachine = new AutomaticCoffeeMachine();
$this->coffeeGrinder = $automaticCoffeeMachine;
$this->coffeeMaker = $automaticCoffeeMachine;

return $this->getCoffee($coffeeBeans);
}

private function grind($coffeeBeans)
{
return $this->coffeeGrinder->grind($coffeeBeans);
}

private function brew($coffeePowder)
{
return $this->coffeeMaker->brew($coffeePowder);
}

private function getCoffee($coffeeBeans)
{
$coffeePowder = $this->grind($coffeeBeans);
$coffee = $this->brew($coffeePowder);
return $coffee;
}
}

然而,隨著對咖啡興趣增加,我們想來玩玩摩卡壺。
卻發現摩卡壺不會磨粉…


需求一:將全自動咖啡機介面職責分離,拆分出磨粉介面與沖煮介面

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

namespace App\SOLID\ISP\CoffeeMachine\Contracts;

interface CoffeeGrinder
{
public function grind($coffeeBeans);
}

  • 接著定義沖煮介面
1
2
3
4
5
6
7
8
9
<?php

namespace App\SOLID\ISP\CoffeeMachine\Contracts;

interface CoffeeMaker
{
public function brew($coffeePowder);
}

  • 最後修改原本的全自動咖啡機,讓它實作新的兩個介面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\SOLID\ISP\CoffeeMachine;

use App\SOLID\ISP\CoffeeMachine\Contracts\CoffeeGrinder;
use App\SOLID\ISP\CoffeeMachine\Contracts\CoffeeMaker;

class AutomaticCoffeeMachine implements CoffeeGrinder, CoffeeMaker
{
public function grind($coffeeBeans)
{
if ($coffeeBeans == '咖啡豆') {
return '咖啡粉';
}
}

public function brew($coffeePowder)
{
if ($coffeePowder == '咖啡粉') {
return '咖啡';
}
}
}


需求二:實作磨豆機與摩卡壺

  • 實作磨豆機(因為拆分出沖煮介面,它不需要知道如何沖煮)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace App\SOLID\ISP\CoffeeMachine;

use App\SOLID\ISP\CoffeeMachine\Contracts\CoffeeGrinder;

class NormalCoffeeGrinder implements CoffeeGrinder
{
public function grind($coffeeBeans)
{
if ($coffeeBeans == '咖啡豆') {
return '咖啡粉';
}
}
}

  • 實作摩卡壺(因為拆分出磨豆介面,它不需要知道如何磨豆)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace App\SOLID\ISP\CoffeeMachine;

use App\SOLID\ISP\CoffeeMachine\Contracts\CoffeeMaker;

class Mocalpot implements CoffeeMaker
{
public function brew($coffeePowder)
{
if ($coffeePowder == '咖啡粉') {
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php

namespace App\SOLID\ISP\CoffeeMachine;

use App\SOLID\ISP\CoffeeMachine\Contracts\CoffeeGrinder;
use App\SOLID\ISP\CoffeeMachine\Contracts\CoffeeMaker;
use App\SOLID\ISP\CoffeeMachine\NormalCoffeeGrinder;
use App\SOLID\ISP\CoffeeMachine\MocalPot;

class Program
{
/**
* @var CoffeeGrinder
*/
protected $coffeeGrinder;

/**
* @var CoffeeMaker
*/
protected $coffeeMaker;

public function getCoffeeByAutomaticCoffeeMachine($coffeeBeans)
{
$automaticCoffeeMachine = new AutomaticCoffeeMachine();
$this->setCoffeeGrinder($automaticCoffeeMachine);
$this->setCoffeeMaker($automaticCoffeeMachine);

return $this->getCoffee($coffeeBeans);
}

public function getCoffeeByNormalCoffeeGrinderAndMocalPot($coffeeBeans)
{
$normalCoffeeGrinder = new NormalCoffeeGrinder();
$mocalPot = new MocalPot();
$this->setCoffeeGrinder($normalCoffeeGrinder);
$this->setCoffeeMaker($mocalPot);

return $this->getCoffee($coffeeBeans);
}

private function grind($coffeeBeans)
{
return $this->coffeeGrinder->grind($coffeeBeans);
}

private function brew($coffeePowder)
{
return $this->coffeeMaker->brew($coffeePowder);
}

private function getCoffee($coffeeBeans)
{
$coffeePowder = $this->grind($coffeeBeans);
$coffee = $this->brew($coffeePowder);
return $coffee;
}

private function setCoffeeGrinder(CoffeeGrinder $coffeeGrinder)
{
$this->coffeeGrinder = $coffeeGrinder;
}

private function setCoffeeMaker(CoffeeMaker $coffeeMaker)
{
$this->coffeeMaker = $coffeeMaker;
}
}

ʕ •ᴥ•ʔ:運用介面隔離原則拆分介面職責,
我們就可以玩更多花樣的咖啡沖煮方法了。

而煮咖啡方法也不必因為器具更換而修改。
符合開放封閉原則