Pattern: 代理模式
Class Diagram: 快取代理
情境:以下是某搜尋功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?php
namespace App\ProxyPattern\Cache;
use App\ProxyPattern\Cache\Database;
class Program {
protected $database;
public function __construct() { $this->database = new Database(); }
public function search(string $keyword): array { return $this->database->read($keyword); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php
namespace App\ProxyPattern\Cache;
class Database {
public function read(string $keyword): array { if ($keyword == 'sushi') { return ['Bear Sushi', 'Lin Sushi', 'Alysa Sushi']; }
return []; } }
|
老闆希望搜尋時,若是已搜尋過的資料,
便由快取返回,不再呼叫實體資料庫。
讓我們用代理模式改造它。
需求一:實現快取代理
1 2 3 4 5 6 7 8 9 10 11 12
| <?php
namespace App\ProxyPattern\Cache\Contracts;
interface Readable {
public function read(string $keyword): array; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php
namespace App\ProxyPattern\Cache;
use App\ProxyPattern\Cache\Contracts\Readable;
class Database implements Readable {
public function read(string $keyword): array { if ($keyword == 'sushi') { return ['Bear Sushi', 'Lin Sushi', 'Alysa Sushi']; }
return []; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <?php
namespace App\ProxyPattern\Cache;
use App\ProxyPattern\Cache\Contracts\Readable;
class CacheProxy implements Readable {
protected $cached = [];
protected $database;
public function __construct() { $this->database = new Database(); }
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; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?php
namespace App\ProxyPattern\Cache;
use App\ProxyPattern\Cache\CacheProxy;
class Program {
protected $proxy;
public function __construct() { $this->proxy = new CacheProxy(); }
public function search(string $keyword): array { return $this->proxy->read($keyword); } }
|
這下子客戶端搜尋時,若快取代理有資料,便會直接返回結果。
[單一職責原則]
我們將實體類別與代理類別視作兩種不同的職責。
代理類別主要處理訪問實體類別的行為。
[開放封閉原則]
當我們需要實現不屬於實體類別的職責時(例如:關鍵字被搜尋次數),
我們可以在代理類別中實現,不須修改實體類別的程式碼。
若有需要其他控制訪問的職責時,也可以新增代理類別。
除了上述的提出介面的委派方法外,
也有人用繼承的手法修改行為,但我個人比較不喜歡。
ʕ •ᴥ•ʔ:代理類別就像是實體類別的經紀人一樣。