mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-08-03 13:37:27 +02:00
Refactored example of Proxy pattern
This commit is contained in:
10
Structural/Proxy/BankAccount.php
Normal file
10
Structural/Proxy/BankAccount.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Structural\Proxy;
|
||||||
|
|
||||||
|
interface BankAccount
|
||||||
|
{
|
||||||
|
public function deposit(int $amount);
|
||||||
|
|
||||||
|
public function getBalance(): int;
|
||||||
|
}
|
24
Structural/Proxy/BankAccountProxy.php
Normal file
24
Structural/Proxy/BankAccountProxy.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Structural\Proxy;
|
||||||
|
|
||||||
|
class BankAccountProxy extends HeavyBankAccount implements BankAccount
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $balance;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
25
Structural/Proxy/HeavyBankAccount.php
Normal file
25
Structural/Proxy/HeavyBankAccount.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DesignPatterns\Structural\Proxy;
|
||||||
|
|
||||||
|
class HeavyBankAccount implements BankAccount
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
private $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);
|
||||||
|
}
|
||||||
|
}
|
@@ -25,15 +25,21 @@ Code
|
|||||||
|
|
||||||
You can also find this code on `GitHub`_
|
You can also find this code on `GitHub`_
|
||||||
|
|
||||||
Record.php
|
BankAccount.php
|
||||||
|
|
||||||
.. literalinclude:: Record.php
|
.. literalinclude:: BankAccount.php
|
||||||
:language: php
|
:language: php
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
RecordProxy.php
|
HeavyBankAccount.php
|
||||||
|
|
||||||
.. literalinclude:: RecordProxy.php
|
.. literalinclude:: HeavyBankAccount.php
|
||||||
|
:language: php
|
||||||
|
:linenos:
|
||||||
|
|
||||||
|
BankAccountProxy.php
|
||||||
|
|
||||||
|
.. literalinclude:: BankAccountProxy.php
|
||||||
:language: php
|
:language: php
|
||||||
:linenos:
|
:linenos:
|
||||||
|
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace DesignPatterns\Structural\Proxy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property string username
|
|
||||||
*/
|
|
||||||
class Record
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
private $data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[] $data
|
|
||||||
*/
|
|
||||||
public function __construct(array $data = [])
|
|
||||||
{
|
|
||||||
$this->data = $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
*/
|
|
||||||
public function __set(string $name, string $value)
|
|
||||||
{
|
|
||||||
$this->data[$name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __get(string $name): string
|
|
||||||
{
|
|
||||||
if (!isset($this->data[$name])) {
|
|
||||||
throw new \OutOfRangeException('Invalid name given');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->data[$name];
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace DesignPatterns\Structural\Proxy;
|
|
||||||
|
|
||||||
class RecordProxy extends Record
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $isDirty = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $isInitialized = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $data
|
|
||||||
*/
|
|
||||||
public function __construct(array $data)
|
|
||||||
{
|
|
||||||
parent::__construct($data);
|
|
||||||
|
|
||||||
// when the record has data, mark it as initialized
|
|
||||||
// since Record will hold our business logic, we don't want to
|
|
||||||
// implement this behaviour there, but instead in a new proxy class
|
|
||||||
// that extends the Record class
|
|
||||||
if (count($data) > 0) {
|
|
||||||
$this->isInitialized = true;
|
|
||||||
$this->isDirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
*/
|
|
||||||
public function __set(string $name, string $value)
|
|
||||||
{
|
|
||||||
$this->isDirty = true;
|
|
||||||
|
|
||||||
parent::__set($name, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isDirty(): bool
|
|
||||||
{
|
|
||||||
return $this->isDirty;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,24 +2,23 @@
|
|||||||
|
|
||||||
namespace DesignPatterns\Structural\Proxy\Tests;
|
namespace DesignPatterns\Structural\Proxy\Tests;
|
||||||
|
|
||||||
use DesignPatterns\Structural\Proxy\RecordProxy;
|
use DesignPatterns\Structural\Proxy\BankAccountProxy;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ProxyTest extends TestCase
|
class ProxyTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testWillSetDirtyFlagInProxy()
|
public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce()
|
||||||
{
|
{
|
||||||
$recordProxy = new RecordProxy([]);
|
$bankAccount = new BankAccountProxy();
|
||||||
$recordProxy->username = 'baz';
|
$bankAccount->deposit(30);
|
||||||
|
|
||||||
$this->assertTrue($recordProxy->isDirty());
|
// this time balance is being calculated
|
||||||
}
|
$this->assertEquals(0, $bankAccount->getBalance());
|
||||||
|
|
||||||
public function testProxyIsInstanceOfRecord()
|
// inheritance allows for BankAccountProxy to behave to an outsider exactly like ServerBankAccount
|
||||||
{
|
$bankAccount->deposit(50);
|
||||||
$recordProxy = new RecordProxy([]);
|
|
||||||
$recordProxy->username = 'baz';
|
|
||||||
|
|
||||||
$this->assertInstanceOf(RecordProxy::class, $recordProxy);
|
// this time the previously calculated balance is returned again without re-calculating it
|
||||||
|
$this->assertEquals(0, $bankAccount->getBalance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +1,37 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Diagram>
|
<Diagram>
|
||||||
<ID>PHP</ID>
|
<ID>PHP</ID>
|
||||||
<OriginalElement>\DesignPatterns\Structural\Proxy\Record</OriginalElement>
|
<OriginalElement>\DesignPatterns\Structural\Proxy\BankAccount</OriginalElement>
|
||||||
<nodes>
|
<nodes>
|
||||||
<node x="4.0" y="0.0">\DesignPatterns\Structural\Proxy\Record</node>
|
<node x="7.5" y="282.0">\DesignPatterns\Structural\Proxy\BankAccountProxy</node>
|
||||||
<node x="0.0" y="159.0">\DesignPatterns\Structural\Proxy\RecordProxy</node>
|
<node x="-58.0" y="123.0">\DesignPatterns\Structural\Proxy\HeavyBankAccount</node>
|
||||||
|
<node x="45.0" y="0.0">\DesignPatterns\Structural\Proxy\BankAccount</node>
|
||||||
</nodes>
|
</nodes>
|
||||||
<notes />
|
<notes />
|
||||||
<edges>
|
<edges>
|
||||||
<edge source="\DesignPatterns\Structural\Proxy\RecordProxy" target="\DesignPatterns\Structural\Proxy\Record">
|
<edge source="\DesignPatterns\Structural\Proxy\HeavyBankAccount" target="\DesignPatterns\Structural\Proxy\BankAccount">
|
||||||
<point x="0.0" y="-54.5" />
|
<point x="0.0" y="-48.0" />
|
||||||
<point x="0.0" y="54.5" />
|
<point x="21.5" y="98.0" />
|
||||||
|
<point x="84.75" y="98.0" />
|
||||||
|
<point x="-39.75" y="36.5" />
|
||||||
|
</edge>
|
||||||
|
<edge source="\DesignPatterns\Structural\Proxy\BankAccountProxy" target="\DesignPatterns\Structural\Proxy\BankAccount">
|
||||||
|
<point x="37.5" y="-37.0" />
|
||||||
|
<point x="120.0" y="243.0" />
|
||||||
|
<point x="164.5" y="243.0" />
|
||||||
|
<point x="164.5" y="161.0" />
|
||||||
|
<point x="165.25" y="105.0" />
|
||||||
|
<point x="39.75" y="36.5" />
|
||||||
|
</edge>
|
||||||
|
<edge source="\DesignPatterns\Structural\Proxy\BankAccountProxy" target="\DesignPatterns\Structural\Proxy\HeavyBankAccount">
|
||||||
|
<point x="-37.5" y="-37.0" />
|
||||||
|
<point x="45.0" y="247.0" />
|
||||||
|
<point x="21.5" y="247.0" />
|
||||||
|
<point x="0.0" y="48.0" />
|
||||||
</edge>
|
</edge>
|
||||||
</edges>
|
</edges>
|
||||||
<settings layout="Hierarchic Group" zoom="1.0" x="81.0" y="134.0" />
|
<settings layout="Hierarchic Group" zoom="1.0" x="73.0" y="178.0" />
|
||||||
<SelectedNodes>
|
<SelectedNodes />
|
||||||
<node>\DesignPatterns\Structural\Proxy\Record</node>
|
|
||||||
</SelectedNodes>
|
|
||||||
<Categories>
|
<Categories>
|
||||||
<Category>Fields</Category>
|
<Category>Fields</Category>
|
||||||
<Category>Constants</Category>
|
<Category>Constants</Category>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 35 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 52 KiB |
Reference in New Issue
Block a user