範例:文字積木 (蠅量模式)

Pattern: 蠅量模式

Class Diagram: 文字積木


情境:這是公司生產的文字積木

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
<?php

namespace App\Flyweight\Blocks;

class ConcreteBlock
{
/**
* @var string
*/
protected $shape;

/**
* @var string
*/
public $word;

/**
* @param string $shape
* @param string $word
*/
public function __construct(string $shape, string $word)
{
$this->shape = $shape;
$this->word = $word;
}

/**
* @return string
*/
public function getShape(): string
{
return $this->shape;
}

/**
* @return string
*/
public function getWord(): string
{
return $this->word;
}
}

  • 客戶端的使用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace App\Flyweight\Blocks;
<?php

namespace App\Flyweight\Blocks;

use App\Flyweight\Blocks\ConcreteBlock;

class Program
{
/**
* @return array
*/
public function getBlocks(): array
{
$firstBlock = new ConcreteBlock('star', 'B');
$secondBlock = new ConcreteBlock('square', 'E');
$thirdBlock = new ConcreteBlock('square', 'A');
$fourthBlock = new ConcreteBlock('square', 'R');

return [$firstBlock, $secondBlock, $thirdBlock, $fourthBlock];
}
}

隨著積木越賣越好,我們會創建出許多Block,
它們要顯示的文字各不相同,但形狀大概就那幾種。

我們決定改變積木的實作,不紀錄顯示文字於積木的狀態中
抽離出共用的形狀部分,來節省記憶體的消耗。


  • 首先定義抽象的積木介面
1
2
3
4
5
6
7
8
9
<?php

namespace App\Flyweight\Blocks\Contracts;

interface Block
{
public function getShape();
public function display(string $word): string;
}

之後我們要顯示文字時,會使用display()方法。


  • 接著定義實體積木
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 App\Flyweight\Blocks;

use App\Flyweight\Blocks\Contracts\Block;

class ConcreteBlock implements Block
{
/**
* @var string
*/
protected $shape;

/**
* @param string $shape
*/
public function __construct(string $shape)
{
$this->shape = $shape;
}

/**
* @return string
*/
public function getShape(): string
{
return $this->shape;
}

/**
* @param string $word
* @return string
*/
public function display(string $word): string
{
return $word;
}
}

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
<?php

namespace App\Flyweight\Blocks;

use App\Flyweight\Blocks\ConcreteBlock;
use App\Flyweight\Blocks\Contracts\Block;

class BlockFactory
{
/**
* @var Block[]
*/
protected $blocks;

/**
* @param string $shape
* @return ConcreteBlock
*/
public function getBlock(string $shape): Block
{
if (!isset($this->blocks[$shape])) {
$this->blocks[$shape] = new ConcreteBlock($shape);
}

return $this->blocks[$shape];
}
}

  • 最後修改客戶端的使用方式
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
<?php

namespace App\Flyweight\Blocks;

use App\Flyweight\Blocks\Block;

class Program
{
/**
* @return array
*/
public function getBlocks(): array
{
$blockFactory = new BlockFactory();

$firstBlock = $blockFactory->getBlock('star');
// $firstBlock->display('B');

$secondBlock = $blockFactory->getBlock('square');
// $secondBlock->display('E');

$thirdBlock = $blockFactory->getBlock('square');
// $thirdBlock->display('A');

$fourthBlock = $blockFactory->getBlock('square');
// $fourthBlock->display('R');

return [$firstBlock, $secondBlock, $thirdBlock, $fourthBlock];
}
}

當客戶端要顯示文字時,再呼叫display()方法。


[單一職責原則]
我們將管理內部狀態管理外部狀態,視為兩種不同的職責。

透過區分兩者,我們便可以用簡單工廠模式搭配單例模式
切出細粒度的物件,來共用相同的程式碼。

其蠅量類別只紀錄內部狀態且創建後便不會改變。

[開放封閉原則]
透過工廠,我們可以修改積木本身的實作(例如換廠牌),而不影響客戶端。
當客戶端修改外部狀態時,也不影響原本純內部狀態的積木。

[依賴反轉原則]
工廠依賴於抽象的積木介面。
實體積木實作了抽象的積木介面。

ʕ •ᴥ•ʔ:原來共享經濟是用了蠅量模式的概念啊。