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类别图说明。)

ʕ •ᴥ•ʔ:虽然有点复杂,但这个模式好美。


<<:  Day29 Postman 设定环境变数

>>:  HERE API Example - 使用 Platform Data Extension REST API 显示邮递区号图层

Day 5. 在设置Unity VR环境时遇到的问题,以及不存在的解法Q

Warning与error直扑而来XDD 蛮头痛的,我目前遇到的有两个问题被回报为Bug。 简单来说...

【第二四天 - Flutter iBeacon 官方范例讲解(下)】

前言 今日的程序码 => GITHUB 讲解官方范例的权限、扫描、广播的部分。官方范例是使用 ...

Day 0xE - 建立订单纪录到资料库

0x1 前言 目前还没有画面可以看或操作,所以单就资料流的部分先写进资料库, 而建立订单目前也是先塞...

Alpine Linux Porting (2.1) clock is _not_ ticking

这篇开始基本上是进入持续分析有哪些未完善的部份需要进行补足。 依照这几次的bootlog的部份分析,...

#14 JS: create a calculator by prompt()

Final Calculator Design <!DOCTYPE html> <...