前面几篇文章中,我们介绍了了设计模式中的创建型模式。创建型模式主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。其中,

  • 单例模式 用来创建全局唯一的对象。
  • 工厂模式 用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。
  • 建造者模式 是用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。
  • 原型模式 针对创建成本比较大的对象,利用对已有对象进行复制的方式进行创建,以达到节省创建时间的目的。

从这篇文章,我们来学习设计模式的结构性设计模式。结构型模式主要总结了一些类或对象组合在一起的经典结构,这些经典的结构可以解决特定应用场景的问题。结构型模式包括:代理模式、桥接模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式。
这篇文章中我将为大家来介绍代理模式

代理模式

代理(Proxy)模式的定义为:为其他对象提供一种代理以控制对这个对象的访问。简单的说就是,在一些情况下客户不想或者不能直接引用一个对象,而代理对象可以在客户和目标对象之间起到中介作用,去掉客户不能看到的内容和服务或者增添客户需要的额外服务。

代理模式的结构

  1. 抽象主题角色 服务接口 (Service Interface) 声明了服务接口。 代理必须遵循该接口才能伪装成服务对象。
  2. 真实主题角色 服务 (Service) 类提供了一些实用的业务逻辑。
  3. 代理主题角色 代理 (Proxy) 类包含一个指向服务对象的引用成员变量。 代理完成其任务 (例如延迟初始化、 记录日志、 访问控制和缓存等) 后会将请求传递给服务对象。 通常情况下, 代理会对其服务对象的整个生命周期进行管理。
  4. 客户端 (Client) 能通过同一接口与服务或代理进行交互, 所以你可在一切需要服务对象的代码中使用代理。

示例

下面是一个使用代理模式通过对结果进行缓存来提高下载器对象的性能的例子
抽象主题角色 服务接口 Downloader.php

<?php
namespace DesignPatterns\Proxy;
/**
 * Interface Downloader
 * @package DesignPatterns\Proxy
 */
interface Downloader
{
    public function download(string $url): string;
}

真实主题角色 服务 RealDownloader.php

<?php
namespace DesignPatterns\Proxy;

/**
 * Class ProxyDownloader
 * @package DesignPatterns\Proxy
 */
class RealDownloader implements Downloader
{
    /**
     * @param string $url
     * @return string
     */
    public function download(string $url): string
    {
        echo "下载文件<br>";
        $result = file_get_contents($url);
        echo "文件大小 " . strlen($result) . "bytes<br>";

        return $result;
    }
}

代理主题角色 Proxydownloader.php

<?php
namespace DesignPatterns\Proxy;
class ProxyDownloader implements Downloader
{
    private $downloader;
    private $cache = [];
    public function __construct(RealDownloader $downloader)
    {
        $this->downloader = $downloader;
    }

    public function download(string $url): string
    {
        if (!isset($this->cache[$url])) {
          echo "缓存代理未命中<br>";
          $result = $this->downloader->download($url);
          $this->cache[$url] = $result;
        }else{
            echo "缓存命中,从缓存中检索结果<br>";

        }
       return $this->cache[$url];
    }
}

运行 client.php

<?php
namespace DesignPatterns\Proxy;
require dirname(__DIR__).'/vendor/autoload.php';
class Client
{
    public function run(Downloader $subject)
    {
        $result = $subject->download("https://www.baidu.com");
        $result = $subject->download("https://www.baidu.com");
    }
}

//使用真实主题执行客户端代码
echo "使用真实主题执行客户端代码:<br>";
$client = new Client();
$realSubject = new RealDownloader();
$client->run($realSubject);
echo "<br>";

//使用代理执行相同的客户端代码
echo "使用代理执行相同的客户端代码:<br>";
$client->run(new ProxyDownloader($realSubject));

运行结果:

代理模式在不改变原有类的情况下通过引入代理类来给原始类附加功能,将目标写入缓存,提升下载器性能,也符合我们的开闭原则。

github示例:https://github.com/yangpanyao/design-patterns/tree/master/Proxy

Last modification:June 16th, 2020 at 09:14 pm