Refactored example of Proxy pattern

This commit is contained in:
Dominik Liebler
2018-06-14 22:17:14 +02:00
parent a494c07255
commit ea85485b85
10 changed files with 197 additions and 413 deletions

View File

@@ -0,0 +1,10 @@
<?php
namespace DesignPatterns\Structural\Proxy;
interface BankAccount
{
public function deposit(int $amount);
public function getBalance(): int;
}

View 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;
}
}

View 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);
}
}

View File

@@ -25,15 +25,21 @@ Code
You can also find this code on `GitHub`_
Record.php
BankAccount.php
.. literalinclude:: Record.php
.. literalinclude:: BankAccount.php
:language: php
:linenos:
RecordProxy.php
HeavyBankAccount.php
.. literalinclude:: RecordProxy.php
.. literalinclude:: HeavyBankAccount.php
:language: php
:linenos:
BankAccountProxy.php
.. literalinclude:: BankAccountProxy.php
:language: php
:linenos:

View File

@@ -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];
}
}

View File

@@ -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;
}
}

View File

@@ -2,24 +2,23 @@
namespace DesignPatterns\Structural\Proxy\Tests;
use DesignPatterns\Structural\Proxy\RecordProxy;
use DesignPatterns\Structural\Proxy\BankAccountProxy;
use PHPUnit\Framework\TestCase;
class ProxyTest extends TestCase
{
public function testWillSetDirtyFlagInProxy()
public function testProxyWillOnlyExecuteExpensiveGetBalanceOnce()
{
$recordProxy = new RecordProxy([]);
$recordProxy->username = 'baz';
$bankAccount = new BankAccountProxy();
$bankAccount->deposit(30);
$this->assertTrue($recordProxy->isDirty());
}
// this time balance is being calculated
$this->assertEquals(0, $bankAccount->getBalance());
public function testProxyIsInstanceOfRecord()
{
$recordProxy = new RecordProxy([]);
$recordProxy->username = 'baz';
// inheritance allows for BankAccountProxy to behave to an outsider exactly like ServerBankAccount
$bankAccount->deposit(50);
$this->assertInstanceOf(RecordProxy::class, $recordProxy);
// this time the previously calculated balance is returned again without re-calculating it
$this->assertEquals(0, $bankAccount->getBalance());
}
}

View File

@@ -1,28 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<Diagram>
<ID>PHP</ID>
<OriginalElement>\DesignPatterns\Structural\Proxy\Record</OriginalElement>
<nodes>
<node x="4.0" y="0.0">\DesignPatterns\Structural\Proxy\Record</node>
<node x="0.0" y="159.0">\DesignPatterns\Structural\Proxy\RecordProxy</node>
</nodes>
<notes />
<edges>
<edge source="\DesignPatterns\Structural\Proxy\RecordProxy" target="\DesignPatterns\Structural\Proxy\Record">
<point x="0.0" y="-54.5" />
<point x="0.0" y="54.5" />
</edge>
</edges>
<settings layout="Hierarchic Group" zoom="1.0" x="81.0" y="134.0" />
<SelectedNodes>
<node>\DesignPatterns\Structural\Proxy\Record</node>
</SelectedNodes>
<Categories>
<Category>Fields</Category>
<Category>Constants</Category>
<Category>Constructors</Category>
<Category>Methods</Category>
</Categories>
<VISIBILITY>private</VISIBILITY>
</Diagram>
<?xml version="1.0" encoding="UTF-8"?>
<Diagram>
<ID>PHP</ID>
<OriginalElement>\DesignPatterns\Structural\Proxy\BankAccount</OriginalElement>
<nodes>
<node x="7.5" y="282.0">\DesignPatterns\Structural\Proxy\BankAccountProxy</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>
<notes />
<edges>
<edge source="\DesignPatterns\Structural\Proxy\HeavyBankAccount" target="\DesignPatterns\Structural\Proxy\BankAccount">
<point x="0.0" y="-48.0" />
<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>
</edges>
<settings layout="Hierarchic Group" zoom="1.0" x="73.0" y="178.0" />
<SelectedNodes />
<Categories>
<Category>Fields</Category>
<Category>Constants</Category>
<Category>Constructors</Category>
<Category>Methods</Category>
</Categories>
<VISIBILITY>private</VISIBILITY>
</Diagram>

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