設計模式 - 工廠模式(簡單工廠、工廠方法、抽象工廠)

  • 簡單工廠模式
  • 工廠模式
  • 抽象工廠模式

設計模式是解決開發時遇到普遍存在(反覆出現)的問題的各種解法。但並不是絕對的,遇到問題才使用解法而不是為了使用而使用

切記: 不要拿了錘子,看什麼都是釘子

介紹

工廠模式重點在於情境複雜度度來針對代碼提取封裝的行為,可以的達到高內聚低耦合的效果。

工廠模式又細分為以下三種:

  • 簡單工廠模式。
  • 工廠方法模式。
  • 抽象工廠模式。

簡單工廠模式

又稱靜態工廠,可以用於較簡單邏輯的業務需求。嚴格來說簡單工廠模式不是一種設計模式,更像是一種開發習慣。

定義

透過一個類別來創建其他類別的實例,被創建的實例通常要有共同的父類別

組成

  • 工廠角色:負責依照內部邏輯返回創建的實例
  • 抽象產品角色:一般是讓具體產品繼承的父類或者實現的接口。由接口或者抽象類來實現。
  • 具體產品角色:工廠類所創建的對象就是此角色的實例。

範例

抽象產品角色

1
2
3
4
abstract class Academy
{
abstract function announce();
}

具體產品角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Gryffindor extends Academy
{
public function announce()
{
echo '葛萊芬多! <br/>';
}
}

class Slytherin extends Academy
{
public function announce()
{
echo '史萊哲林! <br/>';
}
}

工廠角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class AcademyFactory
{
public static function createAcademy($academy)
{
switch ($academy)
{
case 'Gryffindor':
return new Gryffindor();

case 'Slytherin':
return new Slytherin();

default:
echo '無此學院';
return null;
}
}
}

使用工廠

1
2
3
4
5
# 格萊芬多宣示
AcademyFactory::createAcademy('Gryffindor')->announce();

# 史萊哲林宣示
AcademyFactory::createAcademy('Slytherin')->announce();

輸出結果

1
2
葛萊芬多!
史萊哲林!

討論

  • 該模式依照邏輯判斷實作何種產品的實例,客戶端可以免除直接創建產品的責任。
  • 但若是新增了產品(霍格華滋不單只有兩個學院),必然修改工廠邏輯,此行為直接違反了開放封閉原則,且在產品過多的情況下,會造成邏輯複雜難以維護的問題。

工廠方法模式

屬於『創建型模式』。

定義

由父類別定義一個建立物件的介面(工廠方法),再由實作的次類別(工廠類別)去實作實體化的動作,工廠方法是一個抽象的方法。

組成

  • 抽象產品角色:一般是讓具體產品繼承的父類或者實現的接口。由接口或者抽象類來實現。
  • 具體產品角色:工廠類所創建的對象就是此角色的實例。
  • 工廠父類別:內含用以實體化的產品的抽象方法。
  • 工廠子類別:實作產生物件的方法。

範例

抽象產品角色及具體產品角色

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
abstract class Academy
{
abstract public function announce(): string;
abstract public function feature(): string;
abstract public function mascot(): string;
}

class Gryffindor extends Academy
{
public function announce(): string
{
return '葛萊芬多! <br/>';
}

public function feature(): string
{
return '強調勇氣的特質,還有「膽識、氣魄和豪爽」,注重榮譽與騎士精神。 <br/>';
}

public function mascot(): string
{
return '獅。 <br/>';
}
}

class Slytherin extends Academy
{
public function announce(): string
{
return '史萊哲林! <br/>';
}

public function feature(): string
{
return '有野心、精明、狡猾,有很強的領導力,足智多謀,審時度勢,並且追求成就。 <br/>';
}

public function mascot(): string
{
return '蛇。 <br/>';
}
}

工廠父類別

1
2
3
4
abstract class AcademyFactory
{
abstract public function introduceAcadmy();
}

工廠子類別

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class GryffindorFactor extends AcademyFactory
{
public function introduceAcadmy()
{
$acadmy = new Gryffindor();
echo '學院:' . $acadmy->announce();
echo '特質:' . $acadmy->feature();
echo '代表物:' . $acadmy->mascot();
}
}

class SlytherinFactory extends AcademyFactory
{
public function introduceAcadmy()
{
$acadmy = new Slytherin();
echo '學院:' . $acadmy->announce();
echo '特質:' . $acadmy->feature();
echo '代表物:' . $acadmy->mascot();
}
}

分類帽

1
2
3
4
5
6
7
8
9
echo '哈利波特適合葛萊芬多! </br>';
$selectAcadmy = new GryffindorFactor();
$selectAcadmy->introduceAcadmy();

echo '</br>';

echo '馬份似乎比較適合史萊哲林! </br>';
$selectAcadmy = new SlytherinFactory();
$selectAcadmy->introduceAcadmy();

輸出結果

1
2
3
4
5
6
7
8
9
哈利波特適合葛萊芬多!
學院:葛萊芬多!
特質:強調勇氣的特質,還有「膽識、氣魄和豪爽」,注重榮譽與騎士精神。
代表物:獅。

馬份似乎比較適合史萊哲林!
學院:史萊哲林!
特質:有野心、精明、狡猾,有很強的領導力,足智多謀,審時度勢,並且追求成就。
代表物:蛇。

討論

  • 因為工廠模式隱藏了內部實作細節,客戶端只要注重於正確工廠的使用,這使得整個設計符合了單一職責,提高了系統內聚性

  • 若今日要添加兩個學院(霍格華滋有四大學院),只要添加對應工廠即可,這樣就不會更改到原有的邏輯,也符合了開放封閉原則

  • 但由於將實作細節封裝進工廠內部,會導致系統結構複雜化,後續人員的學習曲線會跟著提高。更糟糕的是若設計上有所失誤,會大大提升功能的擴充性及維護性的難度。

抽象工廠

抽象工廠注重的是各種不同的產品得實作與組合,父類別會定義多個抽象物件讓次類別(工廠類別)去實作各個實體化的動作。像是汽車的內裝(不同廠牌的音響)、冒險者的武器(長劍或弓)或是家電的零件(面板來源)等…

定義

由父類別定義多個建立物件的介面(工廠方法)**,再由實作的次類別(工廠類別)去實作各個實體化的組裝動作**。

組成

  • 抽象產品角色:一般是讓具體產品繼承的父類或者實現的接口。由接口或者抽象類來實現。
  • 具體產品角色:工廠類所創建的對象就是此角色的實例。
  • 工廠父類別:內含用以實體化的產品的抽象方法。
  • 工廠子類別:實作產生物件的方法。

範例

抽象產品角色及具體產品角色(學院,此時多了寶物而未裝備)

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
abstract class Academy
{
protected string $treasure='';

abstract public function announce(): string;
abstract public function feature(): string;
abstract public function mascot(): string;

public function setTreasure($setTreasure)
{
$this->treasure = $setTreasure;
}

public function showTreasure(): string
{
if (trim($this->treasure) == '') {
return '糟糕,我沒拿到寶物';
}

return $this->treasure;
}
}

class Gryffindor extends Academy
{
public function announce(): string
{
return '葛萊芬多! <br/>';
}

public function feature(): string
{
return '強調勇氣的特質,還有「膽識、氣魄和豪爽」,注重榮譽與騎士精神。 <br/>';
}

public function mascot(): string
{
return '獅。 <br/>';
}
}

class Slytherin extends Academy
{
public function announce(): string
{
return '史萊哲林! <br/>';
}

public function feature(): string
{
return '有野心、精明、狡猾,有很強的領導力,足智多謀,審時度勢,並且追求成就。 <br/>';
}

public function mascot(): string
{
return '蛇。 <br/>';
}
}

抽象產品角色及具體產品角色(寶物,待工廠實作組裝給學院生)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
abstract class Treasure
{
abstract public function equipTreasure(): string;
}

class GryffindorTreasure extends Treasure
{
public function equipTreasure(): string
{
return '格萊芬多寶劍!';
}
}

class SlytherinTreasure extends Treasure
{

public function equipTreasure(): string
{
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
abstract class AcademyFactory
{
abstract public function introduceAcadmy();
}

class GryffindorFactor extends AcademyFactory
{
public function introduceAcadmy()
{
$acadmy = new Gryffindor();
echo '學院:' . $acadmy->announce();
echo '特質:' . $acadmy->feature();
echo '代表物:' . $acadmy->mascot();

$treasure = new GryffindorTreasure();
$acadmy->setTreasure($treasure->equipTreasure());
return $acadmy;
}
}

class SlytherinFactory extends AcademyFactory
{
public function introduceAcadmy()
{
$acadmy = new Slytherin();
echo '學院:' . $acadmy->announce();
echo '特質:' . $acadmy->feature();
echo '代表物:' . $acadmy->mascot();

$treasure = new SlytherinTreasure();
$acadmy->setTreasure($treasure->equipTreasure());
return $acadmy;
}
}

分類帽(分派學生,且學生高興地舉起手中的寶物)

1
2
3
4
5
6
7
8
9
10
11
echo '哈利波特適合葛萊芬多! </br>';
$selectAcadmy = new GryffindorFactor();
$potter = $selectAcadmy->introduceAcadmy();
echo '哈利波特:我手上握有' . $potter->showTreasure() . '</br>';

echo '</br>';

echo '馬份似乎比較適合史萊哲林! </br>';
$selectAcadmy = new SlytherinFactory();
$malfoy = $selectAcadmy->introduceAcadmy();
echo '馬份:我手上握有' . $malfoy->showTreasure() . '</br>';

輸出結果

1
2
3
4
5
6
7
8
9
10
11
哈利波特適合葛萊芬多!
學院:葛萊芬多!
特質:強調勇氣的特質,還有「膽識、氣魄和豪爽」,注重榮譽與騎士精神。
代表物:獅。
哈利波特:我手上握有格萊芬多寶劍!

馬份似乎比較適合史萊哲林!
學院:史萊哲林!
特質:有野心、精明、狡猾,有很強的領導力,足智多謀,審時度勢,並且追求成就。
代表物:蛇。
馬份:我手上握有史萊哲林小金匣!

討論

  • 由工廠去組裝各個實作產品,讓各部件的組合更具重用性及靈活性。充分達到高內聚低耦合的設計目的。

  • 增加新的具體工廠和產品族很方便,不需要修改已有的系統,非常符合開放封閉原則

  • 因為間有組合的關係,要修改結構是相當麻煩的。

Author

LinYoYo

Posted on

2021-09-27

Updated on

2021-10-18

Licensed under