本文同步更新於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);
}
}
這下子客戶端搜尋時,若快取代理有資料,便會直接返回結果。
[單一職責原則]
我們將實體類別與代理類別視作兩種不同的職責。
代理類別主要處理訪問實體類別的行為。
[開放封閉原則]
當我們需要實現不屬於實體類別的職責時(例如:關鍵字被搜尋次數),
我們可以在代理類別中實現,不須修改實體類別的程式碼。
若有需要其他控制訪問的職責時,也可以新增代理類別。
除了上述的提出介面的委派方法外,
也有人用繼承的手法修改行為,但我個人比較不喜歡。
最後附上類別圖:
(註:若不熟悉 UML 類別圖,可參考UML類別圖說明。)
ʕ •ᴥ•ʔ:代理類別就像是實體類別的經紀人一樣。