1. Home
  2. PHP
  3. DesignPatterns
  4. Proxyパターン - PHPデザインパターン

Proxyパターン - PHPデザインパターン

  • 公開日
  • 更新日
  • カテゴリ:DesignPatterns
  • タグ:PHP,DesignPatterns,Structure,Proxy,VirtualProxy
Proxyパターン - PHPデザインパターン

Proxy パターン(プロキシ・パターン)は、構造に関するデザインパターン手法の1つで、主クラスの代理クラスを同一インターフェイスで実装する事で処理を委譲し、省リソース化や処理の取り回しを調整できる処理モデルです。

Proxy パターンの基本的なクラス図は以下になります。

実装例

今回は、画像の読み込みと表示についてを Proxy パターンで実装します。 Proxy パターンにはいくつかのパターンがあり、その中でも VirtualProxy パターンと言われる手法になります。

画像を取り扱う機能を作成する際に最低限必要なものとして「画像名」「画像パス」「画像データ」あたりを操作する機能にします。

まずは画像情報を保有する Item クラスを作成します。

App/Item/Image.php
<?php

namespace App\Item;

class Image
{
    private $name;
    private $path;

    public function __construct($name, $path)
    {
        $this->name = $name;
        $this->path = $path;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getPath()
    {
        return $this->path;
    }
}

画像名(題名)と画像パスを持つシンプルなものになっています。

次に Subject クラスです。インターフェイスを定義します。

App/Interfaces/ImageInterface.php
<?php

namespace App\Interfaces;

interface ImageInterface
{
    public function ImageInfo();
    public function ImageDisplay();
}
  • ImageInfo() メソッドは、画像情報を出力します。
  • ImageDisplay() メソッドは、画像を表示します。

次は RealSubject クラスです。「本人」の役割を持つクラスです。

App/Subject/RealImage.php
<?php

namespace App\Subject;

use App\Interfaces\ImageInterface;
use App\Item\Image;

class RealImage implements ImageInterface
{
    private $image;
    private $content;

    public function __construct(Image $image)
    {
        $this->image = $image;
        $this->content = base64_encode(file_get_contents($image->getPath()));
    }

    public function ImageInfo()
    {
        echo sprintf("画像名:%s<br>画像パス:%s", $this->image->getName(), $this->image->getPath());
    }

    public function ImageDisplay()
    {
<div class="img_wrap"><img src='data:image/png;base64,%s' width='300' height='auto'></div>", $this->content);
    }
}

ここでは、コンストラクタで画像オブジェクトを受け取り、画像情報と、画像の実態の読み込みを行っています。

そして、インターフェイスを実装しています。 ImageInfo() メソッドでは画像情報の出力を、ImageDisplay() メソッドでは画像自体の表示を行っています。

最後に、Proxy クラスです。「代理人」の役割を持つクラスになります。

App/Proxy/ProxyImage.php
<?php

namespace App\Proxy;

use App\Interfaces\ImageInterface;
use App\Item\Image;
use App\Subject\RealImage;

class ProxyImage implements ImageInterface
{
    private $image;

    public function __construct(Image $image)
    {
        $this->image = $image;
    }

    public function ImageInfo()
    {
        echo sprintf("画像名:%s<br>画像パス:%s", $this->image->getName(), $this->image->getPath());
    }

    public function ImageDisplay()
    {
        $real = new RealImage($this->image);
        $real->ImageDisplay();
    }
}

ここでのポイントは ImageDisplay() メソッドです。 RealImage オブジェクトをインスタンス化し、そこの ImageDisplay() メソッドを用いて画像表示を行っています。

実装が完了したので、クライアントから利用してみます。

index.php
<?php

use App\Item\Image;
use App\Proxy\ProxyImage;

$image = new ProxyImage(new Image('サンプル画像', 'images/sample.jpg'));

// 画像情報の出力
$image->ImageInfo();

echo '<hr>';

// 画像の表示
$image->ImageDisplay();

結果は以下になります。

これらが結局何なのかというと、結果で写っている画像は実は 12MB 弱ある大きな画像を読み込んでいます。

この時に RealSubject クラスでは、インスタンス化を行う際に画像データを読み込み表示に対応するので、大きなリソースを読み込む事で処理に時間がかかる事になり、それなりのサーバリソースも消費します。

これが、必ず画像を表示するような場面であれば Proxy パターンは不要ですが、もし画像の情報は必要だが画像の表示自体は不要である場面もある場合はどうでしょうか。その時は読み込む必要はありません。

その場合は VirtualProxy パターンを用いる事で、Proxy クラスでは画像データは読み込まずに処理する事ができ、処理にかかる時間やリソースを大幅に短縮できます。

実際に、クライアントコードの中の以下の部分

// 画像の表示
$image->ImageDisplay();

これがある場合は処理完了までに2秒弱かかりますが、これが無い場合は一瞬で完了します。つまり画像表示が不要な場面であれば ProxyImage クラスで取り回した方が圧倒的に処理時間や消費リソースが少なくて済むのです。

まとめ

実装例はつまりは遅延ロードであり、本人役のコア処理が「不要」な時こそ力を発揮するパターンですが、本当に必要になった時にそのリソースを読み込む事で処理を省力化する事が出来ます。 Proxy パターンはまさしくこういった手法も思想の1つとして持っています。「Proxy = 代理人」を用いる利点の1つです。

Proxy パターンには他にも「remote proxy パターン」「Protection Proxy パターン」「smart reference パターン」などがあるので是非試してみてください。

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級