Day36. 范例:快取代理(代理模式)

本文同步更新於blog

情境:以下是某搜寻功能

  • 客户端程序码
<?php

namespace App\ProxyPattern\Cache;

use App\ProxyPattern\Cache\Database;

class Program
{
    /**
     * @var Database
     */
    protected $database;

    public function __construct()
    {
        $this->database = new Database();
    }

    /**
     * @param string $keyword
     * @return array
     */
    public function search(string $keyword): array
    {
        return $this->database->read($keyword);
    }
}

  • 实体资料库(供存取资料用)
<?php

namespace App\ProxyPattern\Cache;

class Database
{
    /**
     * @param string $keyword
     * @return array
     */
    public function read(string $keyword): array
    {
        if ($keyword == 'sushi') {
            return ['Bear Sushi', 'Lin Sushi', 'Alysa Sushi'];
        }

        return [];
    }
}

老板希望搜寻时,若是已搜寻过的资料,
便由快取返回,不再呼叫实体资料库。

让我们用代理模式改造它。


需求一:实现快取代理

  • 首先定义读取介面
<?php

namespace App\ProxyPattern\Cache\Contracts;

interface Readable
{
    /**
     * @param string $keyword
     * @return array
     */
    public function read(string $keyword): array;
}
  • 实体资料库实现读取介面
<?php

namespace App\ProxyPattern\Cache;

use App\ProxyPattern\Cache\Contracts\Readable;

class Database implements Readable
{
    /**
     * @param string $keyword
     * @return array
     */
    public function read(string $keyword): array
    {
        if ($keyword == 'sushi') {
            return ['Bear Sushi', 'Lin Sushi', 'Alysa Sushi'];
        }

        return [];
    }
}

  • 实作快取代理
<?php

namespace App\ProxyPattern\Cache;

use App\ProxyPattern\Cache\Contracts\Readable;

class CacheProxy implements Readable
{
    /**
     * @var array
     */
    protected $cached = [];

    /**
     * @var Database
     */
    protected $database;

    public function __construct()
    {
        $this->database = new Database();
    }

    /**
     * @param string $keyword
     * @return array
     */
    public function read(string $keyword): array
    {
        if (isset($this->cached[$keyword])) {
            return $this->cached[$keyword];
        }

        $result = $this->database->read($keyword);
        $this->cached[$keyword] = $result;

        return $result;
    }
}

  • 修改客户端程序码
<?php

namespace App\ProxyPattern\Cache;

use App\ProxyPattern\Cache\CacheProxy;

class Program
{
    /**
     * @var CacheProxy
     */
    protected $proxy;

    public function __construct()
    {
        $this->proxy = new CacheProxy();
    }

    /**
     * @param string $keyword
     * @return array
     */
    public function search(string $keyword): array
    {
        return $this->proxy->read($keyword);
    }
}

这下子客户端搜寻时,若快取代理有资料,便会直接返回结果。


[单一职责原则]
我们将实体类别代理类别视作两种不同的职责。
代理类别主要处理访问实体类别的行为。

[开放封闭原则]
当我们需要实现不属於实体类别的职责时(例如:关键字被搜寻次数),
我们可以在代理类别中实现,不须修改实体类别的程序码。

若有需要其他控制访问的职责时,也可以新增代理类别。

除了上述的提出介面的委派方法外,
也有人用继承的手法修改行为,但我个人比较不喜欢。

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

ʕ •ᴥ•ʔ:代理类别就像是实体类别的经纪人一样。


<<:  Sass/Css 设计模式(Smacss) DAY37

>>:  HTTP标头开发方法

Day 05-其他常结合Chatbot的云端服务器介绍

上一篇我们提到很多人会选择Azure来结合LineBot,其实还有很多提供免费方案的云端服务器!可以...

实战练习 - 使用 RxJS 实作 Flux Pattern

使用 React 作为前端架构的朋友对於 Flux 应该都不陌生,React 也内建了 Flux 让...

第二十一天:TeamCity 技术名词回顾

经过 20 天的练习,我们已经大致掌握了 TeamCity 的基本功能,刚好是一个很好的机会来回顾一...

【Day30】挑战回顾 & 铁人练成心得分享

挑战最後一日的题目真的让我想了很久,倒底该放什麽元件来压轴才好?要写一个综合演练,把前面的元件都拿出...

【从实作学习ASP.NET Core】Day07 | 後台 | 复杂的商品模型

前面花了点时间介绍了 MVC,今天终於要进入正题啦! 我会以一个电玩专卖店的购物网站为主题,并且从後...