設計模式 是解決開發時遇到普遍存在(反覆出現)的問題的各種解法。但並不是絕對的,遇到問題才使用解法而不是為了使用而使用 。
切記: 不要拿了錘子,看什麼都是釘子
介紹 工廠模式 重點在於情境
複雜度度來針對代碼提取
及封裝
的行為,可以的達到高內聚
、低耦合
的效果。
工廠模式 又細分為以下三種:
簡單工廠模式 又稱靜態工廠 ,可以用於較簡單邏輯的業務需求。嚴格來說簡單工廠模式 不是一種設計模式,更像是一種開發習慣。
定義 透過一個類別 來創建其他類別的實例 ,被創建的實例通常要有共同的父類別 。
組成
工廠角色:負責依照內部邏輯返回創建的實例 。
抽象產品角色:一般是讓具體產品繼承的父類 或者實現的接口。由接口或者抽象類來實現。
具體產品角色:工廠類所創建的對象就是此角色的實例。
範例 抽象產品角色
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 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 哈利波特適合葛萊芬多! 學院:葛萊芬多! 特質:強調勇氣的特質,還有「膽識、氣魄和豪爽」,注重榮譽與騎士精神。 代表物:獅。 哈利波特:我手上握有格萊芬多寶劍! 馬份似乎比較適合史萊哲林! 學院:史萊哲林! 特質:有野心、精明、狡猾,有很強的領導力,足智多謀,審時度勢,並且追求成就。 代表物:蛇。 馬份:我手上握有史萊哲林小金匣!
討論