Day52. 范例:新产品开发(职责链模式)

本文同步更新於blog

情境:公司开发了一个新产品,客户端有许多不同的请求

  • 客户端的请求类别
<?php

namespace App\ChainOfResponsibilityPattern\Software;

class Request
{
    /**
     * @var string
     */
    protected $type;

    /**
     * @var string
     */
    protected $content;

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

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @return string
     */
    public function getContent()
    {
        return $this->content;
    }
}

  • 原本处理客户端请求的方式
<?php

namespace App\ChainOfResponsibilityPattern\Software;

use Tests\Unit\ChainOfResponsibilityPattern\Software\Request;

class Program
{
    /**
     * @param Request $request
     * @return string
     */
    public function handle(Request $request)
    {
        $type = $request->getType();
        $content = $request->getContent();

        switch ($type) {
            case 'bug':
                // $request = new Request('bug', 'no connection');
                return "Support已开始处理[$type:$content]的问题。";
                break;

            case 'feature':
                // $request = new Request('feature', 'add filter');
                return "PM已开始处理[$type:$content]的问题。";
                break;

            default:
                // $request = new Request('cooperative business', 'become Google partner');
                return "Boss已开始处理[$type:$content]的问题。";
                break;
        }
    }
}

根据请求类型的不同,我们会交由不同的角色来处理问题。

但这些请求,可以透过区分请求类别的方式,
统一先交由Support处理,若Support无法处理,再转给下个负责人。

以这样的想法,让我们用职责链模式改造它。


  • 首先是抽象的处理器类别,当该处理器无法处理时,会转给下一个处理器
<?php

namespace App\ChainOfResponsibilityPattern\Software\Abstracts;

use App\ChainOfResponsibilityPattern\Software\Request;

abstract class Handler
{
    /**
     * @var string
     */
    protected $role;

    /**
     * @var array
     */
    protected $canHandleType = [];

    /**
     * @var string
     */
    protected $requestType;

    /**
     * @var string
     */
    protected $requestContent;

    /**
     * @var Handler
     */
    protected $nextHandler;

    /**
     * @param Request $request
     * @return string
     */
    public function handle(Request $request): string
    {
        $this->requestType = $request->getType();
        $this->requestContent = $request->getContent();

        if ($this->canHandle()) {
            $role = $this->role;
            $result = "$role can solve [$this->requestType:$this->requestContent] issue.";
            return $result;
        }

        return $this->nextHandler->handle($request);
    }

    /**
     * @param Handler $handler
     */
    public function setNextHandler(Handler $handler)
    {
        $this->nextHandler = $handler;
    }

    /**
     * @return boolean
     */
    protected function canHandle()
    {
        return in_array($this->requestType, $this->canHandleType);
    }
}

由canHandle()方法来知道,该处理器能不能处理。
由setNextHandler()方法,来决定下一个处理器。


  • Support处理器
<?php

namespace App\ChainOfResponsibilityPattern\Software;

use App\ChainOfResponsibilityPattern\Software\Abstracts\Handler;

class Support extends Handler
{
    /**
     * @var string
     */
    protected $role = 'Support';

    /**
     * @var array
     */
    protected $canHandleType = ['bug'];
}

  • PM处理器
<?php

namespace App\ChainOfResponsibilityPattern\Software;

use App\ChainOfResponsibilityPattern\Software\Abstracts\Handler;

class ProjectManager extends Handler
{
    /**
     * @var string
     */
    protected $role = 'PM';

    /**
     * @var array
     */
    protected $canHandleType = ['bug', 'feature'];
}

  • Boss处理器
<?php

namespace App\ChainOfResponsibilityPattern\Software;

use App\ChainOfResponsibilityPattern\Software\Abstracts\Handler;

class Boss extends Handler
{
    /**
     * @var string
     */
    protected $role = 'Boss';

    /**
     * @return boolean
     */
    protected function canHandle()
    {
        return true;
    }
}


  • 最後修改原本的程序码
<?php

namespace App\ChainOfResponsibilityPattern\Software;

use App\ChainOfResponsibilityPattern\Software\Request;
use App\ChainOfResponsibilityPattern\Software\Support;

class Program
{
    public function handle(Request $request)
    {
        $support = new Support();
        $projectManager = new ProjectManager();
        $boss = new Boss();

        $support->setNextHandler($projectManager);
        $projectManager->setNextHandler($boss);

        return $support->handle($request);
    }
}

这边可以留意,若有未定义类型的请求,最终都会交由Boss处理器来捕捉。


[单一职责原则]
我们把处理器处理器的顺序视作两种不同的职责。

[开放封闭原则]
无论是新增/修改处理器的逻辑,或者修改处理器的顺序,
皆不会改动到所有程序码。

[依赖反转原则]
抽象的处理器类别依赖於自身(组合模式)。
实体的处理器类别则实现它。

最後附上类别图:
https://ithelp.ithome.com.tw/upload/images/20201224/20111630SbYQt82Rx6.png
(注:若不熟悉 UML 类别图,可参考UML类别图说明。)

ʕ •ᴥ•ʔ:一个很生活化的设计模式。


<<:  资讯安全治理(Information Security Governance)

>>:  iOS App开发 OC 第一天, @interface设计思维

Golang-gRPC & Protocol Buffers

之前都是使用RESTful API开发 换工作面试几轮之後发现有蛮多家公司都在使用gRPC 就多学一...

Day22 Alerts简介

今日我们要介绍一下Kibana内的警报功能,在Elastic Stack 内有提供Kibana Al...

C#入门之文本处理(补充)

在前面,我们有讲解过 C# 的文本处理,这篇文章是对前面的内容的一个补充。 前面我们有讲解过,写入文...

增加 App 下载量必备的 ASO 工具

场景与需求 APP跟网页一样,要被下载,最容易的办法就是要取得流量大的关键字的上位排名,也就是要做S...

Day22 jQuery 基本教学(二)

selector 选取 JQ 的 DOM 存取方式是透过 selector 来达到索引目标,会先转换...