@@ -3,9 +3,9 @@ language: php
|
||||
sudo: false
|
||||
|
||||
php:
|
||||
- 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
@@ -33,7 +33,7 @@ class ChainTest extends TestCase
|
||||
->willReturn('GET');
|
||||
$request->method('getUri')->willReturn($uri);
|
||||
|
||||
$this->assertEquals('Hello In Memory!', $this->chain->handle($request));
|
||||
$this->assertSame('Hello In Memory!', $this->chain->handle($request));
|
||||
}
|
||||
|
||||
public function testCanRequestKeyInSlowStorage()
|
||||
@@ -47,6 +47,6 @@ class ChainTest extends TestCase
|
||||
->willReturn('GET');
|
||||
$request->method('getUri')->willReturn($uri);
|
||||
|
||||
$this->assertEquals('Hello World!', $this->chain->handle($request));
|
||||
$this->assertSame('Hello World!', $this->chain->handle($request));
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,6 @@ class CommandTest extends TestCase
|
||||
|
||||
$invoker->setCommand(new HelloCommand($receiver));
|
||||
$invoker->run();
|
||||
$this->assertEquals('Hello World', $receiver->getOutput());
|
||||
$this->assertSame('Hello World', $receiver->getOutput());
|
||||
}
|
||||
}
|
||||
|
@@ -17,17 +17,17 @@ class UndoableCommandTest extends TestCase
|
||||
|
||||
$invoker->setCommand(new HelloCommand($receiver));
|
||||
$invoker->run();
|
||||
$this->assertEquals('Hello World', $receiver->getOutput());
|
||||
$this->assertSame('Hello World', $receiver->getOutput());
|
||||
|
||||
$messageDateCommand = new AddMessageDateCommand($receiver);
|
||||
$messageDateCommand->execute();
|
||||
|
||||
$invoker->run();
|
||||
$this->assertEquals("Hello World\nHello World [".date('Y-m-d').']', $receiver->getOutput());
|
||||
$this->assertSame("Hello World\nHello World [".date('Y-m-d').']', $receiver->getOutput());
|
||||
|
||||
$messageDateCommand->undo();
|
||||
|
||||
$invoker->run();
|
||||
$this->assertEquals("Hello World\nHello World [".date('Y-m-d')."]\nHello World", $receiver->getOutput());
|
||||
$this->assertSame("Hello World\nHello World [".date('Y-m-d')."]\nHello World", $receiver->getOutput());
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,6 @@ namespace DesignPatterns\Behavioral\Iterator\Tests;
|
||||
|
||||
use DesignPatterns\Behavioral\Iterator\Book;
|
||||
use DesignPatterns\Behavioral\Iterator\BookList;
|
||||
use DesignPatterns\Behavioral\Iterator\BookListIterator;
|
||||
use DesignPatterns\Behavioral\Iterator\BookListReverseIterator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class IteratorTest extends TestCase
|
||||
@@ -23,7 +21,7 @@ class IteratorTest extends TestCase
|
||||
$books[] = $book->getAuthorAndTitle();
|
||||
}
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
[
|
||||
'Learning PHP Design Patterns by William Sanders',
|
||||
'Professional Php Design Patterns by Aaron Saray',
|
||||
@@ -48,7 +46,7 @@ class IteratorTest extends TestCase
|
||||
$books[] = $book->getAuthorAndTitle();
|
||||
}
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
['Professional Php Design Patterns by Aaron Saray'],
|
||||
$books
|
||||
);
|
||||
|
@@ -15,18 +15,18 @@ class MementoTest extends TestCase
|
||||
// open the ticket
|
||||
$ticket->open();
|
||||
$openedState = $ticket->getState();
|
||||
$this->assertEquals(State::STATE_OPENED, (string) $ticket->getState());
|
||||
$this->assertSame(State::STATE_OPENED, (string) $ticket->getState());
|
||||
|
||||
$memento = $ticket->saveToMemento();
|
||||
|
||||
// assign the ticket
|
||||
$ticket->assign();
|
||||
$this->assertEquals(State::STATE_ASSIGNED, (string) $ticket->getState());
|
||||
$this->assertSame(State::STATE_ASSIGNED, (string) $ticket->getState());
|
||||
|
||||
// now restore to the opened state, but verify that the state object has been cloned for the memento
|
||||
$ticket->restoreFromMemento($memento);
|
||||
|
||||
$this->assertEquals(State::STATE_OPENED, (string) $ticket->getState());
|
||||
$this->assertSame(State::STATE_OPENED, (string) $ticket->getState());
|
||||
$this->assertNotSame($openedState, $ticket->getState());
|
||||
}
|
||||
}
|
||||
|
@@ -1,55 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Diagram>
|
||||
<ID>PHP</ID>
|
||||
<OriginalElement>\DesignPatterns\Behavioral\Specification\AbstractSpecification</OriginalElement>
|
||||
<nodes>
|
||||
<node x="168.0" y="314.0">\DesignPatterns\Behavioral\Specification\PriceSpecification</node>
|
||||
<node x="551.0" y="333.5">\DesignPatterns\Behavioral\Specification\Not</node>
|
||||
<node x="259.25" y="0.0">\DesignPatterns\Behavioral\Specification\SpecificationInterface</node>
|
||||
<node x="383.0" y="323.5">\DesignPatterns\Behavioral\Specification\Plus</node>
|
||||
<node x="259.25" y="157.0">\DesignPatterns\Behavioral\Specification\AbstractSpecification</node>
|
||||
<node x="0.0" y="323.5">\DesignPatterns\Behavioral\Specification\Either</node>
|
||||
<node x="0.0" y="487.0">\DesignPatterns\Behavioral\Specification\Item</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\Plus" target="\DesignPatterns\Behavioral\Specification\AbstractSpecification">
|
||||
<point x="0.0" y="-54.5" />
|
||||
<point x="457.0" y="294.0" />
|
||||
<point x="386.75" y="294.0" />
|
||||
<point x="25.5" y="53.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\AbstractSpecification" target="\DesignPatterns\Behavioral\Specification\SpecificationInterface">
|
||||
<point x="0.0" y="-53.5" />
|
||||
<point x="0.0" y="53.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\PriceSpecification" target="\DesignPatterns\Behavioral\Specification\AbstractSpecification">
|
||||
<point x="0.0" y="-64.0" />
|
||||
<point x="265.5" y="294.0" />
|
||||
<point x="335.75" y="294.0" />
|
||||
<point x="-25.5" y="53.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\Not" target="\DesignPatterns\Behavioral\Specification\AbstractSpecification">
|
||||
<point x="0.0" y="-44.5" />
|
||||
<point x="618.0" y="284.0" />
|
||||
<point x="437.75" y="284.0" />
|
||||
<point x="76.5" y="53.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\Either" target="\DesignPatterns\Behavioral\Specification\AbstractSpecification">
|
||||
<point x="0.0" y="-54.5" />
|
||||
<point x="74.0" y="284.0" />
|
||||
<point x="284.75" y="284.0" />
|
||||
<point x="-76.5" y="53.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="0.6476510067114094" x="342.5" y="288.0" />
|
||||
<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\Behavioral\Specification\AndSpecification</OriginalElement>
|
||||
<nodes>
|
||||
<node x="0.0" y="112.0">\DesignPatterns\Behavioral\Specification\OrSpecification</node>
|
||||
<node x="259.0" y="101.0">\DesignPatterns\Behavioral\Specification\PriceSpecification</node>
|
||||
<node x="807.0" y="112.0">\DesignPatterns\Behavioral\Specification\NotSpecification</node>
|
||||
<node x="0.0" y="265.0">\DesignPatterns\Behavioral\Specification\Item</node>
|
||||
<node x="548.0" y="112.0">\DesignPatterns\Behavioral\Specification\AndSpecification</node>
|
||||
<node x="436.99999999999994" y="0.0">\DesignPatterns\Behavioral\Specification\SpecificationInterface</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\AndSpecification" target="\DesignPatterns\Behavioral\Specification\SpecificationInterface">
|
||||
<point x="0.0" y="-48.5" />
|
||||
<point x="667.5" y="81.0" />
|
||||
<point x="553.875" y="81.0" />
|
||||
<point x="23.375000000000057" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\NotSpecification" target="\DesignPatterns\Behavioral\Specification\SpecificationInterface">
|
||||
<point x="0.0" y="-48.5" />
|
||||
<point x="923.0" y="71.0" />
|
||||
<point x="600.625" y="71.0" />
|
||||
<point x="70.12500000000006" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\PriceSpecification" target="\DesignPatterns\Behavioral\Specification\SpecificationInterface">
|
||||
<point x="0.0" y="-59.5" />
|
||||
<point x="393.5" y="81.0" />
|
||||
<point x="507.12499999999994" y="81.0" />
|
||||
<point x="-23.375" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Behavioral\Specification\OrSpecification" target="\DesignPatterns\Behavioral\Specification\SpecificationInterface">
|
||||
<point x="0.0" y="-48.5" />
|
||||
<point x="119.5" y="71.0" />
|
||||
<point x="460.37499999999994" y="71.0" />
|
||||
<point x="-70.125" y="25.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="425.5" y="91.0" />
|
||||
<SelectedNodes>
|
||||
<node>\DesignPatterns\Behavioral\Specification\AndSpecification</node>
|
||||
</SelectedNodes>
|
||||
<Categories>
|
||||
<Category>Fields</Category>
|
||||
<Category>Constants</Category>
|
||||
<Category>Constructors</Category>
|
||||
<Category>Methods</Category>
|
||||
</Categories>
|
||||
<VISIBILITY>private</VISIBILITY>
|
||||
</Diagram>
|
||||
|
||||
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 108 KiB |
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class ContextOrder extends StateOrder
|
||||
{
|
||||
public function getState():StateOrder
|
||||
{
|
||||
return static::$state;
|
||||
}
|
||||
|
||||
public function setState(StateOrder $state)
|
||||
{
|
||||
static::$state = $state;
|
||||
}
|
||||
|
||||
public function done()
|
||||
{
|
||||
static::$state->done();
|
||||
}
|
||||
|
||||
public function getStatus(): string
|
||||
{
|
||||
return static::$state->getStatus();
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class CreateOrder extends StateOrder
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->setStatus('created');
|
||||
}
|
||||
|
||||
protected function done()
|
||||
{
|
||||
static::$state = new ShippingOrder();
|
||||
}
|
||||
}
|
34
Behavioral/State/OrderContext.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class OrderContext
|
||||
{
|
||||
/**
|
||||
* @var State
|
||||
*/
|
||||
private $state;
|
||||
|
||||
public static function create(): OrderContext
|
||||
{
|
||||
$order = new self();
|
||||
$order->state = new StateCreated();
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
public function setState(State $state)
|
||||
{
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
public function proceedToNext()
|
||||
{
|
||||
$this->state->proceedToNext($this);
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return $this->state->toString();
|
||||
}
|
||||
}
|
@@ -20,27 +20,33 @@ Code
|
||||
|
||||
You can also find this code on `GitHub`_
|
||||
|
||||
ContextOrder.php
|
||||
OrderContext.php
|
||||
|
||||
.. literalinclude:: ContextOrder.php
|
||||
.. literalinclude:: OrderContext.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
StateOrder.php
|
||||
State.php
|
||||
|
||||
.. literalinclude:: StateOrder.php
|
||||
.. literalinclude:: State.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
ShippingOrder.php
|
||||
StateCreated.php
|
||||
|
||||
.. literalinclude:: ShippingOrder.php
|
||||
.. literalinclude:: StateCreated.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
CreateOrder.php
|
||||
StateShipped.php
|
||||
|
||||
.. literalinclude:: CreateOrder.php
|
||||
.. literalinclude:: StateShipped.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
StateDone.php
|
||||
|
||||
.. literalinclude:: StateDone.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class ShippingOrder extends StateOrder
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->setStatus('shipping');
|
||||
}
|
||||
|
||||
protected function done()
|
||||
{
|
||||
$this->setStatus('completed');
|
||||
}
|
||||
}
|
10
Behavioral/State/State.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
interface State
|
||||
{
|
||||
public function proceedToNext(OrderContext $context);
|
||||
|
||||
public function toString(): string;
|
||||
}
|
16
Behavioral/State/StateCreated.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class StateCreated implements State
|
||||
{
|
||||
public function proceedToNext(OrderContext $context)
|
||||
{
|
||||
$context->setState(new StateShipped());
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return 'created';
|
||||
}
|
||||
}
|
16
Behavioral/State/StateDone.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class StateDone implements State
|
||||
{
|
||||
public function proceedToNext(OrderContext $context)
|
||||
{
|
||||
// there is nothing more to do
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return 'done';
|
||||
}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
abstract class StateOrder
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $details;
|
||||
|
||||
/**
|
||||
* @var StateOrder $state
|
||||
*/
|
||||
protected static $state;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function done();
|
||||
|
||||
protected function setStatus(string $status)
|
||||
{
|
||||
$this->details['status'] = $status;
|
||||
$this->details['updatedTime'] = time();
|
||||
}
|
||||
|
||||
protected function getStatus(): string
|
||||
{
|
||||
return $this->details['status'];
|
||||
}
|
||||
}
|
16
Behavioral/State/StateShipped.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Behavioral\State;
|
||||
|
||||
class StateShipped implements State
|
||||
{
|
||||
public function proceedToNext(OrderContext $context)
|
||||
{
|
||||
$context->setState(new StateDone());
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return 'shipped';
|
||||
}
|
||||
}
|
@@ -2,30 +2,42 @@
|
||||
|
||||
namespace DesignPatterns\Behavioral\State\Tests;
|
||||
|
||||
use DesignPatterns\Behavioral\State\ContextOrder;
|
||||
use DesignPatterns\Behavioral\State\CreateOrder;
|
||||
use DesignPatterns\Behavioral\State\OrderContext;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class StateTest extends TestCase
|
||||
{
|
||||
public function testCanShipCreatedOrder()
|
||||
public function testIsCreatedWithStateCreated()
|
||||
{
|
||||
$order = new CreateOrder();
|
||||
$contextOrder = new ContextOrder();
|
||||
$contextOrder->setState($order);
|
||||
$contextOrder->done();
|
||||
$orderContext = OrderContext::create();
|
||||
|
||||
$this->assertEquals('shipping', $contextOrder->getStatus());
|
||||
$this->assertSame('created', $orderContext->toString());
|
||||
}
|
||||
|
||||
public function testCanCompleteShippedOrder()
|
||||
public function testCanProceedToStateShipped()
|
||||
{
|
||||
$order = new CreateOrder();
|
||||
$contextOrder = new ContextOrder();
|
||||
$contextOrder->setState($order);
|
||||
$contextOrder->done();
|
||||
$contextOrder->done();
|
||||
$contextOrder = OrderContext::create();
|
||||
$contextOrder->proceedToNext();
|
||||
|
||||
$this->assertEquals('completed', $contextOrder->getStatus());
|
||||
$this->assertSame('shipped', $contextOrder->toString());
|
||||
}
|
||||
|
||||
public function testCanProceedToStateDone()
|
||||
{
|
||||
$contextOrder = OrderContext::create();
|
||||
$contextOrder->proceedToNext();
|
||||
$contextOrder->proceedToNext();
|
||||
|
||||
$this->assertSame('done', $contextOrder->toString());
|
||||
}
|
||||
|
||||
public function testStateDoneIsTheLastPossibleState()
|
||||
{
|
||||
$contextOrder = OrderContext::create();
|
||||
$contextOrder->proceedToNext();
|
||||
$contextOrder->proceedToNext();
|
||||
$contextOrder->proceedToNext();
|
||||
|
||||
$this->assertSame('done', $contextOrder->toString());
|
||||
}
|
||||
}
|
||||
|
@@ -20,4 +20,4 @@ class Context
|
||||
|
||||
return $elements;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ class StrategyTest extends TestCase
|
||||
$elements = $obj->executeStrategy($collection);
|
||||
|
||||
$firstElement = array_shift($elements);
|
||||
$this->assertEquals($expected, $firstElement);
|
||||
$this->assertSame($expected, $firstElement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,6 +64,6 @@ class StrategyTest extends TestCase
|
||||
$elements = $obj->executeStrategy($collection);
|
||||
|
||||
$firstElement = array_shift($elements);
|
||||
$this->assertEquals($expected, $firstElement);
|
||||
$this->assertSame($expected, $firstElement);
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ class JourneyTest extends TestCase
|
||||
$beachJourney = new TemplateMethod\BeachJourney();
|
||||
$beachJourney->takeATrip();
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing', 'Taking the plane'],
|
||||
$beachJourney->getThingsToDo()
|
||||
);
|
||||
@@ -20,10 +20,10 @@ class JourneyTest extends TestCase
|
||||
|
||||
public function testCanGetOnAJourneyToACity()
|
||||
{
|
||||
$beachJourney = new TemplateMethod\CityJourney();
|
||||
$beachJourney->takeATrip();
|
||||
$cityJourney = new TemplateMethod\CityJourney();
|
||||
$cityJourney->takeATrip();
|
||||
|
||||
$this->assertEquals(
|
||||
$this->assertSame(
|
||||
[
|
||||
'Buy a flight ticket',
|
||||
'Taking the plane',
|
||||
@@ -31,7 +31,7 @@ class JourneyTest extends TestCase
|
||||
'Buy a gift',
|
||||
'Taking the plane'
|
||||
],
|
||||
$beachJourney->getThingsToDo()
|
||||
$cityJourney->getThingsToDo()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
/**
|
||||
* In this case, the abstract factory is a contract for creating some components
|
||||
* for the web. There are two ways of rendering text: HTML and JSON
|
||||
*/
|
||||
abstract class AbstractFactory
|
||||
{
|
||||
abstract public function createText(string $content): Text;
|
||||
}
|
36
Creational/AbstractFactory/CsvParser.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class CsvParser implements Parser
|
||||
{
|
||||
const OPTION_CONTAINS_HEADER = true;
|
||||
const OPTION_CONTAINS_NO_HEADER = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $skipHeaderLine;
|
||||
|
||||
public function __construct(bool $skipHeaderLine)
|
||||
{
|
||||
$this->skipHeaderLine = $skipHeaderLine;
|
||||
}
|
||||
|
||||
public function parse(string $input): array
|
||||
{
|
||||
$headerWasParsed = false;
|
||||
$parsedLines = [];
|
||||
|
||||
foreach (explode(PHP_EOL, $input) as $line) {
|
||||
if (!$headerWasParsed && $this->skipHeaderLine === self::OPTION_CONTAINS_HEADER) {
|
||||
$headerWasParsed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$parsedLines[] = str_getcsv($line);
|
||||
}
|
||||
|
||||
return $parsedLines;
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class HtmlFactory extends AbstractFactory
|
||||
{
|
||||
public function createText(string $content): Text
|
||||
{
|
||||
return new HtmlText($content);
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class HtmlText extends Text
|
||||
{
|
||||
// do something here
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class JsonFactory extends AbstractFactory
|
||||
{
|
||||
public function createText(string $content): Text
|
||||
{
|
||||
return new JsonText($content);
|
||||
}
|
||||
}
|
11
Creational/AbstractFactory/JsonParser.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class JsonParser implements Parser
|
||||
{
|
||||
public function parse(string $input): array
|
||||
{
|
||||
return json_decode($input, true);
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class JsonText extends Text
|
||||
{
|
||||
// do something here
|
||||
}
|
8
Creational/AbstractFactory/Parser.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
interface Parser
|
||||
{
|
||||
public function parse(string $input): array;
|
||||
}
|
16
Creational/AbstractFactory/ParserFactory.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
class ParserFactory
|
||||
{
|
||||
public function createCsvParser(bool $skipHeaderLine): CsvParser
|
||||
{
|
||||
return new CsvParser($skipHeaderLine);
|
||||
}
|
||||
|
||||
public function createJsonParser(): JsonParser
|
||||
{
|
||||
return new JsonParser();
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ Purpose
|
||||
To create series of related or dependent objects without specifying
|
||||
their concrete classes. Usually the created classes all implement the
|
||||
same interface. The client of the abstract factory does not care about
|
||||
how these objects are created, he just knows how they go together.
|
||||
how these objects are created, it just knows how they go together.
|
||||
|
||||
UML Diagram
|
||||
-----------
|
||||
@@ -21,39 +21,27 @@ Code
|
||||
|
||||
You can also find this code on `GitHub`_
|
||||
|
||||
AbstractFactory.php
|
||||
Parser.php
|
||||
|
||||
.. literalinclude:: AbstractFactory.php
|
||||
.. literalinclude:: Parser.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
JsonFactory.php
|
||||
CsvParser.php
|
||||
|
||||
.. literalinclude:: JsonFactory.php
|
||||
.. literalinclude:: CsvParser.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
HtmlFactory.php
|
||||
JsonParser.php
|
||||
|
||||
.. literalinclude:: HtmlFactory.php
|
||||
.. literalinclude:: JsonParser.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
Text.php
|
||||
ParserFactory.php
|
||||
|
||||
.. literalinclude:: Text.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
JsonText.php
|
||||
|
||||
.. literalinclude:: JsonText.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
HtmlText.php
|
||||
|
||||
.. literalinclude:: HtmlText.php
|
||||
.. literalinclude:: ParserFactory.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
|
@@ -2,27 +2,59 @@
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory\Tests;
|
||||
|
||||
use DesignPatterns\Creational\AbstractFactory\HtmlFactory;
|
||||
use DesignPatterns\Creational\AbstractFactory\HtmlText;
|
||||
use DesignPatterns\Creational\AbstractFactory\JsonFactory;
|
||||
use DesignPatterns\Creational\AbstractFactory\JsonText;
|
||||
use DesignPatterns\Creational\AbstractFactory\CsvParser;
|
||||
use DesignPatterns\Creational\AbstractFactory\JsonParser;
|
||||
use DesignPatterns\Creational\AbstractFactory\ParserFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AbstractFactoryTest extends TestCase
|
||||
{
|
||||
public function testCanCreateHtmlText()
|
||||
public function testCanCreateCsvParser()
|
||||
{
|
||||
$factory = new HtmlFactory();
|
||||
$text = $factory->createText('foobar');
|
||||
$factory = new ParserFactory();
|
||||
$parser = $factory->createCsvParser(CsvParser::OPTION_CONTAINS_HEADER);
|
||||
|
||||
$this->assertInstanceOf(HtmlText::class, $text);
|
||||
$this->assertInstanceOf(CsvParser::class, $parser);
|
||||
}
|
||||
|
||||
public function testCanCreateJsonText()
|
||||
public function testCsvParserCanParse()
|
||||
{
|
||||
$factory = new JsonFactory();
|
||||
$text = $factory->createText('foobar');
|
||||
$factory = new ParserFactory();
|
||||
$parser = $factory->createCsvParser(CsvParser::OPTION_CONTAINS_NO_HEADER);
|
||||
|
||||
$this->assertInstanceOf(JsonText::class, $text);
|
||||
$result = $parser->parse("A0,A1,A2\nB0,B1,B2\nC0,C1,C2");
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
['A0', 'A1', 'A2'],
|
||||
['B0', 'B1', 'B2'],
|
||||
['C0', 'C1', 'C2']
|
||||
],
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
public function testCsvParserCanSkipHeader()
|
||||
{
|
||||
$factory = new ParserFactory();
|
||||
$parser = $factory->createCsvParser(CsvParser::OPTION_CONTAINS_HEADER);
|
||||
|
||||
$result = $parser->parse("A0,A1,A2\nB0,B1,B2\nC0,C1,C2");
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
['B0', 'B1', 'B2'],
|
||||
['C0', 'C1', 'C2']
|
||||
],
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
public function testCanCreateJsonParser()
|
||||
{
|
||||
$factory = new ParserFactory();
|
||||
$parser = $factory->createJsonParser();
|
||||
|
||||
$this->assertInstanceOf(JsonParser::class, $parser);
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\AbstractFactory;
|
||||
|
||||
abstract class Text
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $text;
|
||||
|
||||
public function __construct(string $text)
|
||||
{
|
||||
$this->text = $text;
|
||||
}
|
||||
}
|
@@ -1,50 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Diagram>
|
||||
<ID>PHP</ID>
|
||||
<OriginalElement>\DesignPatterns\Creational\AbstractFactory\AbstractFactory</OriginalElement>
|
||||
<OriginalElement>\DesignPatterns\Creational\AbstractFactory\CsvParser</OriginalElement>
|
||||
<nodes>
|
||||
<node x="214.0" y="101.0">\DesignPatterns\Creational\AbstractFactory\JsonFactory</node>
|
||||
<node x="107.0" y="0.0">\DesignPatterns\Creational\AbstractFactory\AbstractFactory</node>
|
||||
<node x="0.0" y="101.0">\DesignPatterns\Creational\AbstractFactory\HtmlFactory</node>
|
||||
<node x="111.0" y="298.0">\DesignPatterns\Creational\AbstractFactory\JsonText</node>
|
||||
<node x="0.0" y="298.0">\DesignPatterns\Creational\AbstractFactory\HtmlText</node>
|
||||
<node x="51.5" y="197.0">\DesignPatterns\Creational\AbstractFactory\Text</node>
|
||||
<node x="116.5" y="0.0">\DesignPatterns\Creational\AbstractFactory\Parser</node>
|
||||
<node x="0.0" y="288.0">\DesignPatterns\Creational\AbstractFactory\ParserFactory</node>
|
||||
<node x="0.0" y="146.5">\DesignPatterns\Creational\AbstractFactory\JsonParser</node>
|
||||
<node x="168.0" y="101.0">\DesignPatterns\Creational\AbstractFactory\CsvParser</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\Creational\AbstractFactory\HtmlText" target="\DesignPatterns\Creational\AbstractFactory\Text">
|
||||
<point x="0.0" y="-14.5" />
|
||||
<point x="45.5" y="273.0" />
|
||||
<point x="75.75" y="273.0" />
|
||||
<point x="-24.25" y="25.5" />
|
||||
<edge source="\DesignPatterns\Creational\AbstractFactory\CsvParser" target="\DesignPatterns\Creational\AbstractFactory\Parser">
|
||||
<point x="0.0" y="-71.0" />
|
||||
<point x="307.0" y="76.0" />
|
||||
<point x="227.5" y="76.0" />
|
||||
<point x="37.0" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\AbstractFactory\JsonText" target="\DesignPatterns\Creational\AbstractFactory\Text">
|
||||
<point x="0.0" y="-14.5" />
|
||||
<point x="154.5" y="273.0" />
|
||||
<point x="124.25" y="273.0" />
|
||||
<point x="24.25" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\AbstractFactory\JsonFactory" target="\DesignPatterns\Creational\AbstractFactory\AbstractFactory">
|
||||
<edge source="\DesignPatterns\Creational\AbstractFactory\JsonParser" target="\DesignPatterns\Creational\AbstractFactory\Parser">
|
||||
<point x="0.0" y="-25.5" />
|
||||
<point x="311.0" y="76.0" />
|
||||
<point x="252.5" y="76.0" />
|
||||
<point x="48.5" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\AbstractFactory\HtmlFactory" target="\DesignPatterns\Creational\AbstractFactory\AbstractFactory">
|
||||
<point x="0.0" y="-25.5" />
|
||||
<point x="97.0" y="76.0" />
|
||||
<point x="155.5" y="76.0" />
|
||||
<point x="-48.5" y="25.5" />
|
||||
<point x="74.0" y="76.0" />
|
||||
<point x="153.5" y="76.0" />
|
||||
<point x="-37.0" y="25.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="117.0" y="130.5" />
|
||||
<SelectedNodes>
|
||||
<node>\DesignPatterns\Creational\AbstractFactory\AbstractFactory</node>
|
||||
</SelectedNodes>
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="107.5" y="91.0" />
|
||||
<SelectedNodes />
|
||||
<Categories>
|
||||
<Category>Methods</Category>
|
||||
<Category>Constants</Category>
|
||||
<Category>Fields</Category>
|
||||
<Category>Constants</Category>
|
||||
<Category>Constructors</Category>
|
||||
<Category>Methods</Category>
|
||||
</Categories>
|
||||
<VISIBILITY>private</VISIBILITY>
|
||||
</Diagram>
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 67 KiB |
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class Bicycle implements VehicleInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $color;
|
||||
|
||||
public function setColor(string $rgb)
|
||||
{
|
||||
$this->color = $rgb;
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class CarFerrari implements VehicleInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $color;
|
||||
|
||||
public function setColor(string $rgb)
|
||||
{
|
||||
$this->color = $rgb;
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class CarMercedes implements VehicleInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $color;
|
||||
|
||||
public function setColor(string $rgb)
|
||||
{
|
||||
$this->color = $rgb;
|
||||
}
|
||||
|
||||
public function addAMGTuning()
|
||||
{
|
||||
// do additional tuning here
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
abstract class FactoryMethod
|
||||
{
|
||||
const CHEAP = 'cheap';
|
||||
const FAST = 'fast';
|
||||
|
||||
abstract protected function createVehicle(string $type): VehicleInterface;
|
||||
|
||||
public function create(string $type): VehicleInterface
|
||||
{
|
||||
$obj = $this->createVehicle($type);
|
||||
$obj->setColor('black');
|
||||
|
||||
return $obj;
|
||||
}
|
||||
}
|
21
Creational/FactoryMethod/FileLogger.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class FileLogger implements Logger
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $filePath;
|
||||
|
||||
public function __construct(string $filePath)
|
||||
{
|
||||
$this->filePath = $filePath;
|
||||
}
|
||||
|
||||
public function log(string $message)
|
||||
{
|
||||
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
|
||||
}
|
||||
}
|
21
Creational/FactoryMethod/FileLoggerFactory.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class FileLoggerFactory implements LoggerFactory
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $filePath;
|
||||
|
||||
public function __construct(string $filePath)
|
||||
{
|
||||
$this->filePath = $filePath;
|
||||
}
|
||||
|
||||
public function createLogger(): Logger
|
||||
{
|
||||
return new FileLogger($this->filePath);
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class GermanFactory extends FactoryMethod
|
||||
{
|
||||
protected function createVehicle(string $type): VehicleInterface
|
||||
{
|
||||
switch ($type) {
|
||||
case parent::CHEAP:
|
||||
return new Bicycle();
|
||||
case parent::FAST:
|
||||
$carMercedes = new CarMercedes();
|
||||
// we can specialize the way we want some concrete Vehicle since we know the class
|
||||
$carMercedes->addAMGTuning();
|
||||
|
||||
return $carMercedes;
|
||||
default:
|
||||
throw new \InvalidArgumentException("$type is not a valid vehicle");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class ItalianFactory extends FactoryMethod
|
||||
{
|
||||
protected function createVehicle(string $type): VehicleInterface
|
||||
{
|
||||
switch ($type) {
|
||||
case parent::CHEAP:
|
||||
return new Bicycle();
|
||||
case parent::FAST:
|
||||
return new CarFerrari();
|
||||
default:
|
||||
throw new \InvalidArgumentException("$type is not a valid vehicle");
|
||||
}
|
||||
}
|
||||
}
|
8
Creational/FactoryMethod/Logger.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
interface Logger
|
||||
{
|
||||
public function log(string $message);
|
||||
}
|
8
Creational/FactoryMethod/LoggerFactory.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
interface LoggerFactory
|
||||
{
|
||||
public function createLogger(): Logger;
|
||||
}
|
@@ -5,12 +5,12 @@ Purpose
|
||||
-------
|
||||
|
||||
The good point over the SimpleFactory is you can subclass it to
|
||||
implement different ways to create objects
|
||||
implement different ways to create objects.
|
||||
|
||||
For simple case, this abstract class could be just an interface
|
||||
For simple cases, this abstract class could be just an interface.
|
||||
|
||||
This pattern is a "real" Design Pattern because it achieves the
|
||||
"Dependency Inversion Principle" a.k.a the "D" in S.O.L.I.D principles.
|
||||
Dependency Inversion principle a.k.a the "D" in SOLID principles.
|
||||
|
||||
It means the FactoryMethod class depends on abstractions, not concrete
|
||||
classes. This is the real trick compared to SimpleFactory or
|
||||
@@ -28,45 +28,39 @@ Code
|
||||
|
||||
You can also find this code on `GitHub`_
|
||||
|
||||
FactoryMethod.php
|
||||
Logger.php
|
||||
|
||||
.. literalinclude:: FactoryMethod.php
|
||||
.. literalinclude:: Logger.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
ItalianFactory.php
|
||||
StdoutLogger.php
|
||||
|
||||
.. literalinclude:: ItalianFactory.php
|
||||
.. literalinclude:: StdoutLogger.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
GermanFactory.php
|
||||
FileLogger.php
|
||||
|
||||
.. literalinclude:: GermanFactory.php
|
||||
.. literalinclude:: FileLogger.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
VehicleInterface.php
|
||||
LoggerFactory.php
|
||||
|
||||
.. literalinclude:: VehicleInterface.php
|
||||
.. literalinclude:: LoggerFactory.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
CarMercedes.php
|
||||
StdoutLoggerFactory.php
|
||||
|
||||
.. literalinclude:: CarMercedes.php
|
||||
.. literalinclude:: StdoutLoggerFactory.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
CarFerrari.php
|
||||
FileLoggerFactory.php
|
||||
|
||||
.. literalinclude:: CarFerrari.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
Bicycle.php
|
||||
|
||||
.. literalinclude:: Bicycle.php
|
||||
.. literalinclude:: FileLoggerFactory.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
|
11
Creational/FactoryMethod/StdoutLogger.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class StdoutLogger implements Logger
|
||||
{
|
||||
public function log(string $message)
|
||||
{
|
||||
echo $message;
|
||||
}
|
||||
}
|
11
Creational/FactoryMethod/StdoutLoggerFactory.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
class StdoutLoggerFactory implements LoggerFactory
|
||||
{
|
||||
public function createLogger(): Logger
|
||||
{
|
||||
return new StdoutLogger();
|
||||
}
|
||||
}
|
@@ -2,54 +2,27 @@
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod\Tests;
|
||||
|
||||
use DesignPatterns\Creational\FactoryMethod\Bicycle;
|
||||
use DesignPatterns\Creational\FactoryMethod\CarFerrari;
|
||||
use DesignPatterns\Creational\FactoryMethod\CarMercedes;
|
||||
use DesignPatterns\Creational\FactoryMethod\FactoryMethod;
|
||||
use DesignPatterns\Creational\FactoryMethod\GermanFactory;
|
||||
use DesignPatterns\Creational\FactoryMethod\ItalianFactory;
|
||||
use DesignPatterns\Creational\FactoryMethod\FileLogger;
|
||||
use DesignPatterns\Creational\FactoryMethod\FileLoggerFactory;
|
||||
use DesignPatterns\Creational\FactoryMethod\StdoutLogger;
|
||||
use DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class FactoryMethodTest extends TestCase
|
||||
{
|
||||
public function testCanCreateCheapVehicleInGermany()
|
||||
public function testCanCreateStdoutLogging()
|
||||
{
|
||||
$factory = new GermanFactory();
|
||||
$result = $factory->create(FactoryMethod::CHEAP);
|
||||
$loggerFactory = new StdoutLoggerFactory();
|
||||
$logger = $loggerFactory->createLogger();
|
||||
|
||||
$this->assertInstanceOf(Bicycle::class, $result);
|
||||
$this->assertInstanceOf(StdoutLogger::class, $logger);
|
||||
}
|
||||
|
||||
public function testCanCreateFastVehicleInGermany()
|
||||
public function testCanCreateFileLogging()
|
||||
{
|
||||
$factory = new GermanFactory();
|
||||
$result = $factory->create(FactoryMethod::FAST);
|
||||
$loggerFactory = new FileLoggerFactory(sys_get_temp_dir());
|
||||
$logger = $loggerFactory->createLogger();
|
||||
|
||||
$this->assertInstanceOf(CarMercedes::class, $result);
|
||||
}
|
||||
|
||||
public function testCanCreateCheapVehicleInItaly()
|
||||
{
|
||||
$factory = new ItalianFactory();
|
||||
$result = $factory->create(FactoryMethod::CHEAP);
|
||||
|
||||
$this->assertInstanceOf(Bicycle::class, $result);
|
||||
}
|
||||
|
||||
public function testCanCreateFastVehicleInItaly()
|
||||
{
|
||||
$factory = new ItalianFactory();
|
||||
$result = $factory->create(FactoryMethod::FAST);
|
||||
|
||||
$this->assertInstanceOf(CarFerrari::class, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage spaceship is not a valid vehicle
|
||||
*/
|
||||
public function testUnknownType()
|
||||
{
|
||||
(new ItalianFactory())->create('spaceship');
|
||||
$this->assertInstanceOf(FileLogger::class, $logger);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\FactoryMethod;
|
||||
|
||||
interface VehicleInterface
|
||||
{
|
||||
public function setColor(string $rgb);
|
||||
}
|
@@ -1,51 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Diagram>
|
||||
<ID>PHP</ID>
|
||||
<OriginalElement>\DesignPatterns\Creational\FactoryMethod\GermanFactory</OriginalElement>
|
||||
<OriginalElement>\DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory</OriginalElement>
|
||||
<nodes>
|
||||
<node x="87.0" y="242.0">\DesignPatterns\Creational\FactoryMethod\FactoryMethod</node>
|
||||
<node x="164.0" y="112.0">\DesignPatterns\Creational\FactoryMethod\Bicycle</node>
|
||||
<node x="308.0" y="112.0">\DesignPatterns\Creational\FactoryMethod\CarFerrari</node>
|
||||
<node x="194.0" y="365.0">\DesignPatterns\Creational\FactoryMethod\ItalianFactory</node>
|
||||
<node x="0.0" y="101.0">\DesignPatterns\Creational\FactoryMethod\CarMercedes</node>
|
||||
<node x="0.0" y="365.0">\DesignPatterns\Creational\FactoryMethod\GermanFactory</node>
|
||||
<node x="157.0" y="0.0">\DesignPatterns\Creational\FactoryMethod\VehicleInterface</node>
|
||||
<node x="119.25" y="0.0">\DesignPatterns\Creational\FactoryMethod\LoggerFactory</node>
|
||||
<node x="220.0" y="124.0">\DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory</node>
|
||||
<node x="183.0" y="-160.0">\DesignPatterns\Creational\FactoryMethod\FileLogger</node>
|
||||
<node x="0.0" y="101.0">\DesignPatterns\Creational\FactoryMethod\FileLoggerFactory</node>
|
||||
<node x="8.0" y="-137.0">\DesignPatterns\Creational\FactoryMethod\StdoutLogger</node>
|
||||
<node x="106.75" y="-261.0">\DesignPatterns\Creational\FactoryMethod\Logger</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\GermanFactory" target="\DesignPatterns\Creational\FactoryMethod\FactoryMethod">
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\StdoutLogger" target="\DesignPatterns\Creational\FactoryMethod\Logger">
|
||||
<point x="0.0" y="-25.5" />
|
||||
<point x="87.0" y="340.0" />
|
||||
<point x="135.5" y="340.0" />
|
||||
<point x="-48.5" y="36.5" />
|
||||
<point x="85.5" y="-185.0" />
|
||||
<point x="145.5" y="-185.0" />
|
||||
<point x="-38.75" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\CarMercedes" target="\DesignPatterns\Creational\FactoryMethod\VehicleInterface">
|
||||
<point x="0.0" y="-48.0" />
|
||||
<point x="72.0" y="76.0" />
|
||||
<point x="180.0" y="76.0" />
|
||||
<point x="-46.0" y="25.5" />
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\FileLogger" target="\DesignPatterns\Creational\FactoryMethod\Logger">
|
||||
<point x="0.0" y="-48.5" />
|
||||
<point x="283.0" y="-185.0" />
|
||||
<point x="223.0" y="-185.0" />
|
||||
<point x="38.75" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\Bicycle" target="\DesignPatterns\Creational\FactoryMethod\VehicleInterface">
|
||||
<point x="0.0" y="-37.0" />
|
||||
<point x="0.0" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\ItalianFactory" target="\DesignPatterns\Creational\FactoryMethod\FactoryMethod">
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory" target="\DesignPatterns\Creational\FactoryMethod\LoggerFactory">
|
||||
<point x="0.0" y="-25.5" />
|
||||
<point x="281.0" y="340.0" />
|
||||
<point x="232.5" y="340.0" />
|
||||
<point x="48.5" y="36.5" />
|
||||
<point x="301.5" y="76.0" />
|
||||
<point x="241.5" y="76.0" />
|
||||
<point x="40.75" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\CarFerrari" target="\DesignPatterns\Creational\FactoryMethod\VehicleInterface">
|
||||
<point x="0.0" y="-37.0" />
|
||||
<point x="370.0" y="76.0" />
|
||||
<point x="272.0" y="76.0" />
|
||||
<point x="46.0" y="25.5" />
|
||||
<edge source="\DesignPatterns\Creational\FactoryMethod\FileLoggerFactory" target="\DesignPatterns\Creational\FactoryMethod\LoggerFactory">
|
||||
<point x="0.0" y="-48.5" />
|
||||
<point x="100.0" y="76.0" />
|
||||
<point x="160.0" y="76.0" />
|
||||
<point x="-40.75" y="25.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="216.0" y="208.0" />
|
||||
<SelectedNodes />
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="375.0" y="60.5" />
|
||||
<SelectedNodes>
|
||||
<node>\DesignPatterns\Creational\FactoryMethod\FileLogger</node>
|
||||
<node>\DesignPatterns\Creational\FactoryMethod\StdoutLogger</node>
|
||||
<node>\DesignPatterns\Creational\FactoryMethod\Logger</node>
|
||||
</SelectedNodes>
|
||||
<Categories>
|
||||
<Category>Fields</Category>
|
||||
<Category>Constants</Category>
|
||||
<Category>Constructors</Category>
|
||||
<Category>Methods</Category>
|
||||
</Categories>
|
||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace DesignPatterns\Creational\StaticFactory;
|
||||
|
||||
class FormatNumber implements FormatterInterface
|
||||
class FormatNumber implements Formatter
|
||||
{
|
||||
public function format(string $input): string
|
||||
{
|
||||
return number_format($input);
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,10 @@
|
||||
|
||||
namespace DesignPatterns\Creational\StaticFactory;
|
||||
|
||||
class FormatString implements FormatterInterface
|
||||
class FormatString implements Formatter
|
||||
{
|
||||
public function format(string $input): string
|
||||
{
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
8
Creational/StaticFactory/Formatter.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\StaticFactory;
|
||||
|
||||
interface Formatter
|
||||
{
|
||||
public function format(string $input): string;
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Creational\StaticFactory;
|
||||
|
||||
interface FormatterInterface
|
||||
{
|
||||
}
|
@@ -14,7 +14,7 @@ Examples
|
||||
--------
|
||||
|
||||
- Zend Framework: ``Zend_Cache_Backend`` or ``_Frontend`` use a factory
|
||||
method create cache backends or frontends
|
||||
method to create cache backends and frontends
|
||||
|
||||
UML Diagram
|
||||
-----------
|
||||
@@ -34,9 +34,9 @@ StaticFactory.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
FormatterInterface.php
|
||||
Formatter.php
|
||||
|
||||
.. literalinclude:: FormatterInterface.php
|
||||
.. literalinclude:: Formatter.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
|
@@ -11,15 +11,13 @@ final class StaticFactory
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return FormatterInterface
|
||||
* @return Formatter
|
||||
*/
|
||||
public static function factory(string $type): FormatterInterface
|
||||
public static function factory(string $type): Formatter
|
||||
{
|
||||
if ($type == 'number') {
|
||||
return new FormatNumber();
|
||||
}
|
||||
|
||||
if ($type == 'string') {
|
||||
} elseif ($type == 'string') {
|
||||
return new FormatString();
|
||||
}
|
||||
|
||||
|
@@ -1,36 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Diagram>
|
||||
<ID>PHP</ID>
|
||||
<OriginalElement>\DesignPatterns\Creational\StaticFactory\FormatNumber</OriginalElement>
|
||||
<nodes>
|
||||
<node x="114.0" y="77.0">\DesignPatterns\Creational\StaticFactory\FormatNumber</node>
|
||||
<node x="-8.0" y="149.0">\DesignPatterns\Creational\StaticFactory\StaticFactory</node>
|
||||
<node x="0.0" y="77.0">\DesignPatterns\Creational\StaticFactory\FormatString</node>
|
||||
<node x="43.75" y="0.0">\DesignPatterns\Creational\StaticFactory\FormatterInterface</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\Creational\StaticFactory\FormatString" target="\DesignPatterns\Creational\StaticFactory\FormatterInterface">
|
||||
<point x="0.0" y="-13.5" />
|
||||
<point x="47.0" y="52.0" />
|
||||
<point x="75.0" y="52.0" />
|
||||
<point x="-31.25" y="13.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\StaticFactory\FormatNumber" target="\DesignPatterns\Creational\StaticFactory\FormatterInterface">
|
||||
<point x="0.0" y="-13.5" />
|
||||
<point x="165.5" y="52.0" />
|
||||
<point x="137.5" y="52.0" />
|
||||
<point x="31.25" y="13.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="108.5" y="98.0" />
|
||||
<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\Creational\StaticFactory\FormatNumber</OriginalElement>
|
||||
<nodes>
|
||||
<node x="0.0" y="101.0">\DesignPatterns\Creational\StaticFactory\FormatString</node>
|
||||
<node x="0.0" y="197.0">\DesignPatterns\Creational\StaticFactory\StaticFactory</node>
|
||||
<node x="79.5" y="0.0">\DesignPatterns\Creational\StaticFactory\Formatter</node>
|
||||
<node x="159.0" y="101.0">\DesignPatterns\Creational\StaticFactory\FormatNumber</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\Creational\StaticFactory\FormatString" target="\DesignPatterns\Creational\StaticFactory\Formatter">
|
||||
<point x="0.0" y="-25.5" />
|
||||
<point x="69.5" y="76.0" />
|
||||
<point x="114.25" y="76.0" />
|
||||
<point x="-34.75" y="25.5" />
|
||||
</edge>
|
||||
<edge source="\DesignPatterns\Creational\StaticFactory\FormatNumber" target="\DesignPatterns\Creational\StaticFactory\Formatter">
|
||||
<point x="0.0" y="-25.5" />
|
||||
<point x="228.5" y="76.0" />
|
||||
<point x="183.75" y="76.0" />
|
||||
<point x="34.75" y="25.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="128.0" y="102.0" />
|
||||
<SelectedNodes />
|
||||
<Categories>
|
||||
<Category>Fields</Category>
|
||||
<Category>Constants</Category>
|
||||
<Category>Constructors</Category>
|
||||
<Category>Methods</Category>
|
||||
</Categories>
|
||||
<VISIBILITY>private</VISIBILITY>
|
||||
</Diagram>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 41 KiB |
13
Dockerfile
@@ -1,7 +1,8 @@
|
||||
FROM php:7.2.0-cli
|
||||
WORKDIR /opt/php
|
||||
FROM php:7.3.0-cli
|
||||
WORKDIR /app
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y zlib1g-dev wget git-core \
|
||||
&& docker-php-ext-install zip
|
||||
ADD . /opt/php
|
||||
CMD [ "./docker/test_runner.sh" ]
|
||||
&& apt-get install -y libzip-dev wget git-core python-pip \
|
||||
&& docker-php-ext-install zip \
|
||||
&& pip install Sphinx
|
||||
ADD . /app
|
||||
CMD [ "make", "cs", "test" ]
|
||||
|
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2011-2017 Dominik Liebler and contributors
|
||||
Copyright (c) 2011-2018 Dominik Liebler and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
9
Makefile
@@ -191,14 +191,11 @@ pseudoxml:
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
|
||||
composer.phar:
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
php -r "if (hash_file('SHA384', 'composer-setup.php') === 'e115a8dc7871f15d853148a7fbac7da27d6c0030b848d9b3dc09e2a0388afed865e6a3d6b3c0fad45c48e2b5fc1196ae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
|
||||
php composer-setup.php
|
||||
php -r "unlink('composer-setup.php');"
|
||||
|
||||
install: vendor
|
||||
|
||||
composer.phar:
|
||||
docker/install-composer.sh
|
||||
|
||||
vendor: composer.phar
|
||||
php composer.phar install
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Delegation;
|
||||
|
||||
class JuniorDeveloper
|
||||
{
|
||||
public function writeBadCode(): string
|
||||
{
|
||||
return 'Some junior developer generated code...';
|
||||
}
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
`Delegation`__
|
||||
==============
|
||||
|
||||
Purpose
|
||||
-------
|
||||
|
||||
Demonstrate the Delegator pattern, where an object, instead of performing one
|
||||
of its stated tasks, delegates that task to an associated helper object. In
|
||||
this case TeamLead professes to writeCode and Usage uses this, while TeamLead
|
||||
delegates writeCode to JuniorDeveloper's writeBadCode function. This inverts
|
||||
the responsibility so that Usage is unknowingly executing writeBadCode.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Please review JuniorDeveloper.php, TeamLead.php, and then Usage.php to see it all tied together.
|
||||
|
||||
UML Diagram
|
||||
-----------
|
||||
|
||||
.. image:: uml/uml.png
|
||||
:alt: Alt Delegation UML Diagram
|
||||
:align: center
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
You can also find this code on `GitHub`_
|
||||
|
||||
TeamLead.php
|
||||
|
||||
.. literalinclude:: TeamLead.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
JuniorDeveloper.php
|
||||
|
||||
.. literalinclude:: JuniorDeveloper.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
Tests/DelegationTest.php
|
||||
|
||||
.. literalinclude:: Tests/DelegationTest.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
.. _`GitHub`: https://github.com/domnikl/DesignPatternsPHP/tree/master/More/Delegation
|
||||
.. __: http://en.wikipedia.org/wiki/Delegation_pattern
|
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Delegation;
|
||||
|
||||
class TeamLead
|
||||
{
|
||||
/**
|
||||
* @var JuniorDeveloper
|
||||
*/
|
||||
private $junior;
|
||||
|
||||
/**
|
||||
* @param JuniorDeveloper $junior
|
||||
*/
|
||||
public function __construct(JuniorDeveloper $junior)
|
||||
{
|
||||
$this->junior = $junior;
|
||||
}
|
||||
|
||||
public function writeCode(): string
|
||||
{
|
||||
return $this->junior->writeBadCode();
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Delegation\Tests;
|
||||
|
||||
use DesignPatterns\More\Delegation;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DelegationTest extends TestCase
|
||||
{
|
||||
public function testHowTeamLeadWriteCode()
|
||||
{
|
||||
$junior = new Delegation\JuniorDeveloper();
|
||||
$teamLead = new Delegation\TeamLead($junior);
|
||||
|
||||
$this->assertEquals($junior->writeBadCode(), $teamLead->writeCode());
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Diagram>
|
||||
<ID>PHP</ID>
|
||||
<OriginalElement>\DesignPatterns\More\Delegation\JuniorDeveloper</OriginalElement>
|
||||
<nodes>
|
||||
<node x="-8.0" y="134.0">\DesignPatterns\More\Delegation\JuniorDeveloper</node>
|
||||
<node x="0.0" y="0.0">\DesignPatterns\More\Delegation\TeamLead</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges />
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="68.5" y="90.5" />
|
||||
<SelectedNodes />
|
||||
<Categories>
|
||||
<Category>Fields</Category>
|
||||
<Category>Constants</Category>
|
||||
<Category>Constructors</Category>
|
||||
<Category>Methods</Category>
|
||||
</Categories>
|
||||
<VISIBILITY>private</VISIBILITY>
|
||||
</Diagram>
|
||||
|
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 28 KiB |
@@ -1,6 +1,5 @@
|
||||
# More
|
||||
|
||||
* [Delegation](Delegation) [:notebook:](http://en.wikipedia.org/wiki/Delegation_pattern)
|
||||
* [ServiceLocator](ServiceLocator) [:notebook:](http://en.wikipedia.org/wiki/Service_locator_pattern)
|
||||
* [Repository](Repository)
|
||||
* [EAV](EAV) [:notebook:](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model)
|
||||
|
@@ -4,7 +4,6 @@ More
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
Delegation/README
|
||||
ServiceLocator/README
|
||||
Repository/README
|
||||
EAV/README
|
||||
|
80
More/Repository/Domain/Post.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository\Domain;
|
||||
|
||||
class Post
|
||||
{
|
||||
/**
|
||||
* @var PostId
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var PostStatus
|
||||
*/
|
||||
private $status;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $text;
|
||||
|
||||
public static function draft(PostId $id, string $title, string $text): Post
|
||||
{
|
||||
return new self(
|
||||
$id,
|
||||
PostStatus::fromString(PostStatus::STATE_DRAFT),
|
||||
$title,
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
public static function fromState(array $state): Post
|
||||
{
|
||||
return new self(
|
||||
PostId::fromInt($state['id']),
|
||||
PostStatus::fromInt($state['statusId']),
|
||||
$state['title'],
|
||||
$state['text']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PostId $id
|
||||
* @param PostStatus $status
|
||||
* @param string $title
|
||||
* @param string $text
|
||||
*/
|
||||
private function __construct(PostId $id, PostStatus $status, string $title, string $text)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->status = $status;
|
||||
$this->text = $text;
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getId(): PostId
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getStatus(): PostStatus
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function getText(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
42
More/Repository/Domain/PostId.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository\Domain;
|
||||
|
||||
/**
|
||||
* This is a perfect example of a value object that is identifiable by it's value alone and
|
||||
* is guaranteed to be valid each time an instance is created. Another important property of value objects
|
||||
* is immutability.
|
||||
*
|
||||
* Notice also the use of a named constructor (fromInt) which adds a little context when creating an instance.
|
||||
*/
|
||||
class PostId
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
public static function fromInt(int $id)
|
||||
{
|
||||
self::ensureIsValid($id);
|
||||
|
||||
return new self($id);
|
||||
}
|
||||
|
||||
private function __construct(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function toInt(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
private static function ensureIsValid(int $id)
|
||||
{
|
||||
if ($id <= 0) {
|
||||
throw new \InvalidArgumentException('Invalid PostId given');
|
||||
}
|
||||
}
|
||||
}
|
80
More/Repository/Domain/PostStatus.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository\Domain;
|
||||
|
||||
/**
|
||||
* Like PostId, this is a value object which holds the value of the current status of a Post. It can be constructed
|
||||
* either from a string or int and is able to validate itself. An instance can then be converted back to int or string.
|
||||
*/
|
||||
class PostStatus
|
||||
{
|
||||
const STATE_DRAFT_ID = 1;
|
||||
const STATE_PUBLISHED_ID = 2;
|
||||
|
||||
const STATE_DRAFT = 'draft';
|
||||
const STATE_PUBLISHED = 'published';
|
||||
|
||||
private static $validStates = [
|
||||
self::STATE_DRAFT_ID => self::STATE_DRAFT,
|
||||
self::STATE_PUBLISHED_ID => self::STATE_PUBLISHED,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public static function fromInt(int $statusId)
|
||||
{
|
||||
self::ensureIsValidId($statusId);
|
||||
|
||||
return new self($statusId, self::$validStates[$statusId]);
|
||||
}
|
||||
|
||||
public static function fromString(string $status)
|
||||
{
|
||||
self::ensureIsValidName($status);
|
||||
|
||||
return new self(array_search($status, self::$validStates), $status);
|
||||
}
|
||||
|
||||
private function __construct(int $id, string $name)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function toInt(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* there is a reason that I avoid using __toString() as it operates outside of the stack in PHP
|
||||
* and is therefor not able to operate well with exceptions
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
private static function ensureIsValidId(int $status)
|
||||
{
|
||||
if (!in_array($status, array_keys(self::$validStates), true)) {
|
||||
throw new \InvalidArgumentException('Invalid status id given');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static function ensureIsValidName(string $status)
|
||||
{
|
||||
if (!in_array($status, self::$validStates, true)) {
|
||||
throw new \InvalidArgumentException('Invalid status name given');
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace DesignPatterns\More\Repository;
|
||||
|
||||
class MemoryStorage
|
||||
class InMemoryPersistence implements Persistence
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
@@ -14,20 +14,22 @@ class MemoryStorage
|
||||
*/
|
||||
private $lastId = 0;
|
||||
|
||||
public function persist(array $data): int
|
||||
public function generateId(): int
|
||||
{
|
||||
$this->lastId++;
|
||||
|
||||
$data['id'] = $this->lastId;
|
||||
$this->data[$this->lastId] = $data;
|
||||
|
||||
return $this->lastId;
|
||||
}
|
||||
|
||||
public function persist(array $data)
|
||||
{
|
||||
$this->data[$this->lastId] = $data;
|
||||
}
|
||||
|
||||
public function retrieve(int $id): array
|
||||
{
|
||||
if (!isset($this->data[$id])) {
|
||||
throw new \OutOfRangeException(sprintf('No data found for ID %d', $id));
|
||||
throw new \OutOfBoundsException(sprintf('No data found for ID %d', $id));
|
||||
}
|
||||
|
||||
return $this->data[$id];
|
||||
@@ -36,7 +38,7 @@ class MemoryStorage
|
||||
public function delete(int $id)
|
||||
{
|
||||
if (!isset($this->data[$id])) {
|
||||
throw new \OutOfRangeException(sprintf('No data found for ID %d', $id));
|
||||
throw new \OutOfBoundsException(sprintf('No data found for ID %d', $id));
|
||||
}
|
||||
|
||||
unset($this->data[$id]);
|
14
More/Repository/Persistence.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository;
|
||||
|
||||
interface Persistence
|
||||
{
|
||||
public function generateId(): int;
|
||||
|
||||
public function persist(array $data);
|
||||
|
||||
public function retrieve(int $id): array;
|
||||
|
||||
public function delete(int $id);
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository;
|
||||
|
||||
class Post
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $text;
|
||||
|
||||
public static function fromState(array $state): Post
|
||||
{
|
||||
return new self(
|
||||
$state['id'],
|
||||
$state['title'],
|
||||
$state['text']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $id
|
||||
* @param string $text
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct($id, string $title, string $text)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->text = $text;
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function setId(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getText(): string
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
@@ -2,8 +2,11 @@
|
||||
|
||||
namespace DesignPatterns\More\Repository;
|
||||
|
||||
use DesignPatterns\More\Repository\Domain\Post;
|
||||
use DesignPatterns\More\Repository\Domain\PostId;
|
||||
|
||||
/**
|
||||
* This class is situated between Entity layer (class Post) and access object layer (MemoryStorage).
|
||||
* This class is situated between Entity layer (class Post) and access object layer (Persistence).
|
||||
*
|
||||
* Repository encapsulates the set of objects persisted in a data store and the operations performed over them
|
||||
* providing a more object-oriented view of the persistence layer
|
||||
@@ -14,21 +17,26 @@ namespace DesignPatterns\More\Repository;
|
||||
class PostRepository
|
||||
{
|
||||
/**
|
||||
* @var MemoryStorage
|
||||
* @var Persistence
|
||||
*/
|
||||
private $persistence;
|
||||
|
||||
public function __construct(MemoryStorage $persistence)
|
||||
public function __construct(Persistence $persistence)
|
||||
{
|
||||
$this->persistence = $persistence;
|
||||
}
|
||||
|
||||
public function findById(int $id): Post
|
||||
public function generateId(): PostId
|
||||
{
|
||||
$arrayData = $this->persistence->retrieve($id);
|
||||
return PostId::fromInt($this->persistence->generateId());
|
||||
}
|
||||
|
||||
if (is_null($arrayData)) {
|
||||
throw new \InvalidArgumentException(sprintf('Post with ID %d does not exist', $id));
|
||||
public function findById(PostId $id): Post
|
||||
{
|
||||
try {
|
||||
$arrayData = $this->persistence->retrieve($id->toInt());
|
||||
} catch (\OutOfBoundsException $e) {
|
||||
throw new \OutOfBoundsException(sprintf('Post with id %d does not exist', $id->toInt()), 0, $e);
|
||||
}
|
||||
|
||||
return Post::fromState($arrayData);
|
||||
@@ -36,11 +44,11 @@ class PostRepository
|
||||
|
||||
public function save(Post $post)
|
||||
{
|
||||
$id = $this->persistence->persist([
|
||||
$this->persistence->persist([
|
||||
'id' => $post->getId()->toInt(),
|
||||
'statusId' => $post->getStatus()->toInt(),
|
||||
'text' => $post->getText(),
|
||||
'title' => $post->getTitle(),
|
||||
]);
|
||||
|
||||
$post->setId($id);
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,19 @@ You can also find this code on `GitHub`_
|
||||
|
||||
Post.php
|
||||
|
||||
.. literalinclude:: Post.php
|
||||
.. literalinclude:: Domain/Post.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
PostId.php
|
||||
|
||||
.. literalinclude:: Domain/PostId.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
PostStatus.php
|
||||
|
||||
.. literalinclude:: Domain/PostStatus.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
@@ -43,18 +55,24 @@ PostRepository.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
MemoryStorage.php
|
||||
Persistence.php
|
||||
|
||||
.. literalinclude:: MemoryStorage.php
|
||||
.. literalinclude:: Persistence.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
InMemoryPersistence.php
|
||||
|
||||
.. literalinclude:: InMemoryPersistence.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
Tests/RepositoryTest.php
|
||||
Tests/PostRepositoryTest.php
|
||||
|
||||
.. literalinclude:: Tests/RepositoryTest.php
|
||||
.. literalinclude:: Tests/PostRepositoryTest.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
|
49
More/Repository/Tests/PostRepositoryTest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository\Tests;
|
||||
|
||||
use DesignPatterns\More\Repository\Domain\PostId;
|
||||
use DesignPatterns\More\Repository\Domain\PostStatus;
|
||||
use DesignPatterns\More\Repository\InMemoryPersistence;
|
||||
use DesignPatterns\More\Repository\Domain\Post;
|
||||
use DesignPatterns\More\Repository\PostRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class PostRepositoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var PostRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->repository = new PostRepository(new InMemoryPersistence());
|
||||
}
|
||||
|
||||
public function testCanGenerateId()
|
||||
{
|
||||
$this->assertEquals(1, $this->repository->generateId()->toInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
* @expectedExceptionMessage Post with id 42 does not exist
|
||||
*/
|
||||
public function testThrowsExceptionWhenTryingToFindPostWhichDoesNotExist()
|
||||
{
|
||||
$this->repository->findById(PostId::fromInt(42));
|
||||
}
|
||||
|
||||
public function testCanPersistPostDraft()
|
||||
{
|
||||
$postId = $this->repository->generateId();
|
||||
$post = Post::draft($postId, 'Repository Pattern', 'Design Patterns PHP');
|
||||
$this->repository->save($post);
|
||||
|
||||
$this->repository->findById($postId);
|
||||
|
||||
$this->assertEquals($postId, $this->repository->findById($postId)->getId());
|
||||
$this->assertEquals(PostStatus::STATE_DRAFT, $post->getStatus()->toString());
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\More\Repository\Tests;
|
||||
|
||||
use DesignPatterns\More\Repository\MemoryStorage;
|
||||
use DesignPatterns\More\Repository\Post;
|
||||
use DesignPatterns\More\Repository\PostRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class RepositoryTest extends TestCase
|
||||
{
|
||||
public function testCanPersistAndFindPost()
|
||||
{
|
||||
$repository = new PostRepository(new MemoryStorage());
|
||||
$post = new Post(null, 'Repository Pattern', 'Design Patterns PHP');
|
||||
|
||||
$repository->save($post);
|
||||
|
||||
$this->assertEquals(1, $post->getId());
|
||||
$this->assertEquals($post->getId(), $repository->findById(1)->getId());
|
||||
}
|
||||
}
|
@@ -3,20 +3,24 @@
|
||||
<ID>PHP</ID>
|
||||
<OriginalElement>\DesignPatterns\More\Repository\PostRepository</OriginalElement>
|
||||
<nodes>
|
||||
<node x="415.0" y="0.0">\DesignPatterns\More\Repository\Storage</node>
|
||||
<node x="405.0" y="137.0">\DesignPatterns\More\Repository\MemoryStorage</node>
|
||||
<node x="0.0" y="0.0">\DesignPatterns\More\Repository\Post</node>
|
||||
<node x="0.0" y="373.0">\DesignPatterns\More\Repository\PostRepository</node>
|
||||
<node x="283.0" y="167.0">\DesignPatterns\More\Repository\InMemoryPersistence</node>
|
||||
<node x="0.0" y="0.0">\DesignPatterns\More\Repository\Domain\PostStatus</node>
|
||||
<node x="590.0" y="385.0">\DesignPatterns\More\Repository\Domain\PostId</node>
|
||||
<node x="0.0" y="385.0">\DesignPatterns\More\Repository\Domain\Post</node>
|
||||
<node x="289.0" y="0.0">\DesignPatterns\More\Repository\Persistence</node>
|
||||
<node x="320.0" y="385.0">\DesignPatterns\More\Repository\PostRepository</node>
|
||||
</nodes>
|
||||
<notes />
|
||||
<edges>
|
||||
<edge source="\DesignPatterns\More\Repository\MemoryStorage" target="\DesignPatterns\More\Repository\Storage">
|
||||
<point x="0.0" y="-74.5" />
|
||||
<point x="0.0" y="43.5" />
|
||||
<edge source="\DesignPatterns\More\Repository\InMemoryPersistence" target="\DesignPatterns\More\Repository\Persistence">
|
||||
<point x="0.0" y="-81.0" />
|
||||
<point x="0.0" y="58.5" />
|
||||
</edge>
|
||||
</edges>
|
||||
<settings layout="Hierarchic Group" zoom="0.7394636015325671" x="299.5" y="251.0" />
|
||||
<SelectedNodes />
|
||||
<settings layout="Hierarchic Group" zoom="1.0" x="666.5" y="488.0" />
|
||||
<SelectedNodes>
|
||||
<node>\DesignPatterns\More\Repository\PostRepository</node>
|
||||
</SelectedNodes>
|
||||
<Categories>
|
||||
<Category>Fields</Category>
|
||||
<Category>Constants</Category>
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 184 KiB |
36
README.md
Executable file → Normal file
@@ -1,6 +1,7 @@
|
||||
# DesignPatternsPHP
|
||||
|
||||
[](https://travis-ci.org/domnikl/DesignPatternsPHP)
|
||||
[](https://readthedocs.org/projects/designpatternsphp/?badge=latest)
|
||||
[](https://paypal.me/DominikLiebler)
|
||||
|
||||
[Read the Docs of DesignPatternsPHP](http://designpatternsphp.readthedocs.org)
|
||||
@@ -14,34 +15,33 @@ I think the problem with patterns is that often people do know them but don't kn
|
||||
You should look at and run the tests to see what happens in the example.
|
||||
To do this, you should install dependencies with `Composer` first:
|
||||
|
||||
### [optional] Using Docker
|
||||
|
||||
You can optionally run tests using [Docker for Mac](https://www.docker.com/docker-mac) or [Windows](https://www.docker.com/docker-windows) or native one for [Linux](https://www.docker.com/docker-debian).
|
||||
Just run:
|
||||
```
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
### Install dependencies
|
||||
|
||||
```bash
|
||||
$ composer install
|
||||
```
|
||||
|
||||
Read more about how to install and use `Composer` on your local machine [here](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx).
|
||||
|
||||
If you are using Docker, you just have to run:
|
||||
|
||||
```bash
|
||||
docker-compose run composer install
|
||||
```
|
||||
|
||||
### Running test suite
|
||||
To run the tests use `phpunit`:
|
||||
|
||||
```bash
|
||||
$ ./vendor/bin/phpunit
|
||||
```
|
||||
|
||||
## using Docker (optional)
|
||||
|
||||
You can optionally run tests using [Docker for Mac](https://www.docker.com/docker-mac) or [Windows](https://www.docker.com/docker-windows) or native one for [Linux](https://www.docker.com/docker-debian).
|
||||
Just run:
|
||||
|
||||
```bash
|
||||
$ docker-compose up
|
||||
```
|
||||
|
||||
To only install the dependencies, use `docker-compose` like this:
|
||||
|
||||
```bash
|
||||
$ docker-compose run composer install
|
||||
```
|
||||
|
||||
## Patterns
|
||||
|
||||
The patterns can be structured in roughly three different categories. Please click on the [:notebook:](http://en.wikipedia.org/wiki/Software_design_pattern) for a full explanation of the pattern on Wikipedia.
|
||||
@@ -88,7 +88,7 @@ The patterns can be structured in roughly three different categories. Please cli
|
||||
* [Visitor](Behavioral/Visitor) [:notebook:](http://en.wikipedia.org/wiki/Visitor_pattern)
|
||||
|
||||
### [More](More)
|
||||
* [Delegation](More/Delegation) [:notebook:](http://en.wikipedia.org/wiki/Delegation_pattern)
|
||||
|
||||
* [ServiceLocator](More/ServiceLocator) [:notebook:](http://en.wikipedia.org/wiki/Service_locator_pattern) (is considered an anti-pattern! :no_entry:)
|
||||
* [Repository](More/Repository)
|
||||
* [EAV](More/EAV) [:notebook:](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model)
|
||||
|
@@ -4,6 +4,10 @@
|
||||
DesignPatternsPHP
|
||||
=================
|
||||
|
||||
.. image:: https://travis-ci.org/domnikl/DesignPatternsPHP.svg?branch=master
|
||||
:target: https://travis-ci.org/domnikl/DesignPatternsPHP
|
||||
:alt: Build Status
|
||||
|
||||
.. image:: https://readthedocs.org/projects/designpatternsphp/badge/?version=latest
|
||||
:target: https://readthedocs.org/projects/designpatternsphp/?badge=latest
|
||||
:alt: Documentation Status
|
||||
@@ -44,3 +48,6 @@ To establish a consistent code quality, please check your code using
|
||||
`PHP CodeSniffer`_ against `PSR2 standard`_
|
||||
using ``./vendor/bin/phpcs -p --standard=PSR2 --ignore=vendor .``.
|
||||
|
||||
.. _`design patterns`: http://en.wikipedia.org/wiki/Software_design_pattern
|
||||
.. _`PHP CodeSniffer`: https://github.com/squizlabs/PHP_CodeSniffer
|
||||
.. _`PSR2 standard`: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
|
||||
|
@@ -15,7 +15,7 @@ class AdapterTest extends TestCase
|
||||
$book->open();
|
||||
$book->turnPage();
|
||||
|
||||
$this->assertEquals(2, $book->getPage());
|
||||
$this->assertSame(2, $book->getPage());
|
||||
}
|
||||
|
||||
public function testCanTurnPageOnKindleLikeInANormalBook()
|
||||
@@ -26,6 +26,6 @@ class AdapterTest extends TestCase
|
||||
$book->open();
|
||||
$book->turnPage();
|
||||
|
||||
$this->assertEquals(2, $book->getPage());
|
||||
$this->assertSame(2, $book->getPage());
|
||||
}
|
||||
}
|
||||
|
8
Structural/Bridge/Formatter.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Structural\Bridge;
|
||||
|
||||
interface Formatter
|
||||
{
|
||||
public function format(string $text): string;
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Structural\Bridge;
|
||||
|
||||
interface FormatterInterface
|
||||
{
|
||||
public function format(string $text);
|
||||
}
|
@@ -4,7 +4,7 @@ namespace DesignPatterns\Structural\Bridge;
|
||||
|
||||
class HelloWorldService extends Service
|
||||
{
|
||||
public function get()
|
||||
public function get(): string
|
||||
{
|
||||
return $this->implementation->format('Hello World');
|
||||
}
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace DesignPatterns\Structural\Bridge;
|
||||
|
||||
class HtmlFormatter implements FormatterInterface
|
||||
class HtmlFormatter implements Formatter
|
||||
{
|
||||
public function format(string $text)
|
||||
public function format(string $text): string
|
||||
{
|
||||
return sprintf('<p>%s</p>', $text);
|
||||
}
|
||||
|
11
Structural/Bridge/PingService.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace DesignPatterns\Structural\Bridge;
|
||||
|
||||
class PingService extends Service
|
||||
{
|
||||
public function get(): string
|
||||
{
|
||||
return $this->implementation->format('pong');
|
||||
}
|
||||
}
|
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace DesignPatterns\Structural\Bridge;
|
||||
|
||||
class PlainTextFormatter implements FormatterInterface
|
||||
class PlainTextFormatter implements Formatter
|
||||
{
|
||||
public function format(string $text)
|
||||
public function format(string $text): string
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
|
@@ -25,9 +25,9 @@ Code
|
||||
|
||||
You can also find this code on `GitHub`_
|
||||
|
||||
FormatterInterface.php
|
||||
Formatter.php
|
||||
|
||||
.. literalinclude:: FormatterInterface.php
|
||||
.. literalinclude:: Formatter.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
@@ -55,6 +55,12 @@ HelloWorldService.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
PingService.php
|
||||
|
||||
.. literalinclude:: PingService.php
|
||||
:language: php
|
||||
:linenos:
|
||||
|
||||
Test
|
||||
----
|
||||
|
||||
|