Pattern: 原型模式
Class Diagram: 三隻小豬
情境:三隻小豬想要蓋房子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\PrototypePattern\Building;
class Wall {
public $material;
public function __construct(string $material) { $this->material = $material; } }
|
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 App\PrototypePattern\Building;
use App\PrototypePattern\Building\Wall;
class Building {
public $name;
public $wall;
public function __construct(Wall $wall, string $name = 'unnamed') { $this->wall = $wall; $this->name = $name; } }
|
- 接著豬大哥用 稻草 (straw) ,蓋了間稻草屋
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\PrototypePattern\Building;
use App\PrototypePattern\Building\Building; use App\PrototypePattern\Building\Wall;
class Program {
public function run() { $strawWall = new Wall('straw'); $firstBuilding = new Building($strawWall, 'oldestPigHouse');
dump($firstBuilding->name); dump($firstBuilding->wall->material); } }
|
完成後,豬大哥很得意自己對房子的設計,
詢問弟弟們要不要直接拷貝一間。
需求一:拷貝豬大哥的稻草屋
兩個弟弟想了想,拷貝大哥的房子,好像是個省事的方法,
但二弟想用木材 (wood),三弟想用磚塊 (bricks) 來蓋房子。
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
| <?php
namespace App\PrototypePattern\Building;
use App\PrototypePattern\Building\Building; use App\PrototypePattern\Building\Wall;
class Program {
public function run() { $strawWall = new Wall('straw'); $firstBuilding = new Building($strawWall, 'oldestPigHouse');
$secondBuilding = clone $firstBuilding; $secondBuilding->name = 'middlePigHouse'; $secondBuilding->wall->material = 'wood';
$thirdBuilding = clone $firstBuilding; $thirdBuilding->name = 'youngestPigHouse'; $thirdBuilding->wall->material = 'bricks';
dump($firstBuilding->name); dump($firstBuilding->wall->material);
dump($secondBuilding->name); dump($secondBuilding->wall->material);
dump($thirdBuilding->name); dump($thirdBuilding->wall->material);
return [ 'firstBuilding' => $firstBuilding, 'secondBuilding' => $secondBuilding, 'thirdBuilding' => $thirdBuilding ]; } }
|
這時發生了件很尷尬的事情,
當豬二哥選用木材當建材時,大哥的稻草屋就變成木頭屋了…
而豬小弟選用磚塊當建材時,兩個哥哥們的房子就變成磚頭屋了…
導演表示,這樣故事沒辦法進行下去,請我們修改一下。
需求二:讓弟弟們對建材的修改,不會影響到哥哥
- 修改Building中的clone()方法,讓拷貝時能重新創建牆壁
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
| <?php
namespace App\PrototypePattern\Building;
use App\PrototypePattern\Building\Wall;
class Building {
public $name;
public $wall;
public function __construct(Wall $wall, string $name = 'unnamed') { $this->wall = $wall; $this->name = $name; }
public function __clone() { $this->wall = clone $this->wall; } }
|
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
| <?php
namespace App\PrototypePattern\Building;
use App\PrototypePattern\Building\Building; use App\PrototypePattern\Building\Wall;
class Program {
public function run() { $strawWall = new Wall('straw'); $firstBuilding = new Building($strawWall, 'oldestPigHouse');
$secondBuilding = clone $firstBuilding; $secondBuilding->name = 'middlePigHouse'; $secondBuilding->wall->material = 'wood';
$thirdBuilding = clone $firstBuilding; $thirdBuilding->name = 'youngestPigHouse'; $thirdBuilding->wall->material = 'bricks';
dump($firstBuilding->name); dump($firstBuilding->wall->material);
dump($secondBuilding->name); dump($secondBuilding->wall->material);
dump($thirdBuilding->name); dump($thirdBuilding->wall->material);
return [ 'firstBuilding' => $firstBuilding, 'secondBuilding' => $secondBuilding, 'thirdBuilding' => $thirdBuilding ]; } }
|
後來弟弟們改用其他建材時,就不會影響到原本哥哥的房子了。
這就是淺複製 (Shallow Copy) 與 深複製 (Deep Copy) 的不同。
後來三隻小豬跑去開建設公司,又是另一個故事了。
ʕ •ᴥ•ʔ:一開始clone()方法能直接使用,是因為PHP的魔術方法__clone()唷!