2.10. Proxy
2.10.1. Scopo
Creare un’interfaccia per qualunque cosa sia onerosa o impossibile da duplicare.
2.10.2. Esempi
Doctrine2 utilizza i proxy per implementare alcune magie del framework (ad esempio inizializzazione pigra), mentre l’utente lavora con le sue classi entità senza utilizzare nè toccare i proxy.
2.10.3. Diagramma UML
2.10.4. Codice
Potete trovare questo codice anche su GitHub
BankAccount.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy;
6
7interface BankAccount
8{
9 public function deposit(int $amount);
10
11 public function getBalance(): int;
12}
HeavyBankAccount.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy;
6
7class HeavyBankAccount implements BankAccount
8{
9 /**
10 * @var int[]
11 */
12 private array $transactions = [];
13
14 public function deposit(int $amount)
15 {
16 $this->transactions[] = $amount;
17 }
18
19 public function getBalance(): int
20 {
21 // this is the heavy part, imagine all the transactions even from
22 // years and decades ago must be fetched from a database or web service
23 // and the balance must be calculated from it
24
25 return array_sum($this->transactions);
26 }
27}
BankAccountProxy.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy;
6
7class BankAccountProxy extends HeavyBankAccount implements BankAccount
8{
9 private ?int $balance = null;
10
11 public function getBalance(): int
12 {
13 // because calculating balance is so expensive,
14 // the usage of BankAccount::getBalance() is delayed until it really is needed
15 // and will not be calculated again for this instance
16
17 if ($this->balance === null) {
18 $this->balance = parent::getBalance();
19 }
20
21 return $this->balance;
22 }
23}
2.10.5. Test
ProxyTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Structural\Proxy\Tests;
6
7use DesignPatterns\Structural\Proxy\BankAccountProxy;
8use PHPUnit\Framework\TestCase;
9
10class ProxyTest extends TestCase
11{
12 public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce()
13 {
14 $bankAccount = new BankAccountProxy();
15 $bankAccount->deposit(30);
16
17 // this time balance is being calculated
18 $this->assertSame(30, $bankAccount->getBalance());
19
20 // inheritance allows for BankAccountProxy to behave to an outsider exactly like ServerBankAccount
21 $bankAccount->deposit(50);
22
23 // this time the previously calculated balance is returned again without re-calculating it
24 $this->assertSame(30, $bankAccount->getBalance());
25 }
26}