iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

0
Software Development

你終究都要學設計模式的,那為什麼不一開始就學呢?系列 第 44

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

本文同步更新於blog

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

<?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;
    }
}

  • 客戶端的使用方式
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,
它們要顯示的文字各不相同,但形狀大概就那幾種。

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


  • 首先定義抽象的積木介面
<?php

namespace App\Flyweight\Blocks\Contracts;

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

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


  • 接著定義實體積木
<?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;
    }
}

<?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];
    }
}

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


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

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

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

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

[裡氏替換原則]
工廠依賴於抽象的積木介面。
實體積木實作了抽象的積木介面。

最後附上類別圖:
https://ithelp.ithome.com.tw/upload/images/20201127/20111630HZxVHEhR1n.png
(註:若不熟悉 UML 類別圖,可參考UML類別圖說明。)

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


上一篇
Day43. 蠅量模式
下一篇
Day45. 解譯器模式
系列文
你終究都要學設計模式的,那為什麼不一開始就學呢?57

尚未有邦友留言

立即登入留言