PHP 代理模式
目的
为昂贵或者无法复制的资源提供接口。
例子
- Doctrine2 使用代理来实现框架特性(如延迟初始化),同时用户还是使用自己的实体类并且不会使用或者接触到代理
UML 图
代码
BankAccount.php
<?php declare(strict_types=1); namespace DesignPatterns\Structural\Proxy; interface BankAccount { public function deposit(int $amount); public function getBalance(): int; }
HeavyBankAccount.php
<?php declare(strict_types=1); namespace DesignPatterns\Structural\Proxy; class HeavyBankAccount implements BankAccount { /** * @var int[] */ private array $transactions = []; public function deposit(int $amount) { $this->transactions[] = $amount; } public function getBalance(): int { // this is the heavy part, imagine all the transactions even from // years and decades ago must be fetched from a database or web service // and the balance must be calculated from it return array_sum($this->transactions); } }
BankAccountProxy.php
<?php declare(strict_types=1); namespace DesignPatterns\Structural\Proxy; class BankAccountProxy extends HeavyBankAccount implements BankAccount { private ?int $balance = null; public function getBalance(): int { // because calculating balance is so expensive, // the usage of BankAccount::getBalance() is delayed until it really is needed // and will not be calculated again for this instance if ($this->balance === null) { $this->balance = parent::getBalance(); } return $this->balance; } }
测试
ProxyTest.php
<?php declare(strict_types=1); namespace DesignPatterns\Structural\Proxy\Tests; use DesignPatterns\Structural\Proxy\BankAccountProxy; use PHPUnit\Framework\TestCase; class ProxyTest extends TestCase { public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce() { $bankAccount = new BankAccountProxy(); $bankAccount->deposit(30); // this time balance is being calculated $this->assertSame(30, $bankAccount->getBalance()); // inheritance allows for BankAccountProxy to behave to an outsider exactly like ServerBankAccount $bankAccount->deposit(50); // this time the previously calculated balance is returned again without re-calculating it $this->assertSame(30, $bankAccount->getBalance()); } }