iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 29
1
Software Development

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

Day29. 範例:運輸系統 (抽象工廠模式)

本文同步更新於blog

前情提要:鐵路運輸系統,參考範例:運輸系統(工廠方法模式)

<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use ReflectionClass;

class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $model = $modelFactory->createModel();
        return $model->getName();
    }

    /**
     * @param string $model
     * @return ModelFactory
     */
    private function createModelFactory($model)
    {
        $namespace = 'App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories';
        $className = $model . 'Factory';

        $reflector = new ReflectionClass($namespace . '\\' . $className);
        return $reflector->newInstance();
    }
}

需求一:各交通工具準備就緒,要開始處理內裝問題。

由於不同的機型會有不同的內裝,讓我們從定義椅子介面開始。

  • 定義椅子介面
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts;

interface Chair
{
    public function getName();
}

  • 接著改寫機型工廠介面,
    除了製作機型外,要新增製作椅子的方法。
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

interface ModelFactory
{
    public function createModel(): Model;

    public function createChair(): Chair;
}

  • 實作長型座椅
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

class LongChair implements Chair
{
    public function getName()
    {
        return '長型座椅';
    }
}

  • 實作對號座椅
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

class ReservedSeatChair implements Chair
{
    public function getName()
    {
        return '對號座椅';
    }
}

  • 實作飛機座椅
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;

class PlaneChair implements Chair
{
    public function getName()
    {
        return '飛機座椅';
    }
}


接著改寫各個機型工廠,選擇適合的座椅。

  • 改寫區間車工廠
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\LocalTrain;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\LongChair;

class LocalTrainFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new LocalTrain();
    }

    public function createChair(): Chair
    {
        return new LongChair();
    }
}

  • 改寫復興號工廠
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\SemiExpress;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\ReservedSeatChair;

class SemiExpressFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new SemiExpress();
    }

    public function createChair(): Chair
    {
        return new ReservedSeatChair();
    }
}

  • 改寫自強號工廠
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\LimitedExpress;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\ReservedSeatChair;

class LimitedExpressFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new LimitedExpress();
    }

    public function createChair(): Chair
    {
        return new ReservedSeatChair();
    }
}
  • 改寫波音747工廠
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\Boeing747;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\PlaneChair;

class Boeing747Factory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊..
        return new Boeing747();
    }

    public function createChair(): Chair
    {
        return new PlaneChair();
    }
}

  • 最後改寫原本的程式碼
<?php

namespace App\FactoryPattern\Transport\AbstractFactoryPattern;

use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use ReflectionClass;

class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $model = $modelFactory->createModel();
        return $model->getName();
    }

    /**
     * @param string $model
     * @return string
     */
    public function getChair($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $chair = $modelFactory->createChair();
        return $chair->getName();
    }

    /**
     * @param string $model
     * @return ModelFactory
     */
    private function createModelFactory($model)
    {
        $namespace = 'App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories';
        $className = $model . 'Factory';

        $reflector = new ReflectionClass($namespace . '\\' . $className);
        return $reflector->newInstance();
    }
}

透過工廠介面,我們組合了各種類型產品。
使得不同類型的產品之間,有了產品族的聯繫關係。


[單一職責原則]
我們將工廠類別 (Creator)產品類別 (Product) 視作兩種不同的職責。

[開放封閉原則]
當新增新的產品族時,我們僅需新增工廠類別。
當修改既有產品時,我們僅需修改其產品類別,不會影響到其他的產品類別。

[介面隔離原則]
工廠介面:用來創建機型及座椅。
產品介面:用來創建對應的產品。

[依賴反轉原則]
工廠介面依賴於抽象的機型介面與座椅介面。

工廠實作抽象的工廠介面。
機型實作抽象的機型介面。
座椅實作抽象的座椅介面。

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

ʕ •ᴥ•ʔ:雖然有點複雜,但這個模式好美。


上一篇
Day28. 抽象工廠模式
下一篇
Day30. 8人訂閱Q&A問答
系列文
你終究都要學設計模式的,那為什麼不一開始就學呢?57
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言