Merge pull request #1 from domnikl/master

Update
This commit is contained in:
Alexey Pyltsyn
2019-01-27 18:26:28 +03:00
committed by GitHub
442 changed files with 2492 additions and 14229 deletions

View File

@@ -3,9 +3,9 @@ language: php
sudo: false
php:
- 7.0
- 7.1
- 7.2
- 7.3
matrix:
fast_finish: true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 61 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

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

View File

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

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

View File

@@ -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:

View File

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

View File

@@ -0,0 +1,10 @@
<?php
namespace DesignPatterns\Behavioral\State;
interface State
{
public function proceedToNext(OrderContext $context);
public function toString(): string;
}

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

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

View File

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

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

View File

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

View File

@@ -20,4 +20,4 @@ class Context
return $elements;
}
}
}

View File

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

View File

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

View File

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

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

View File

@@ -1,11 +0,0 @@
<?php
namespace DesignPatterns\Creational\AbstractFactory;
class HtmlFactory extends AbstractFactory
{
public function createText(string $content): Text
{
return new HtmlText($content);
}
}

View File

@@ -1,8 +0,0 @@
<?php
namespace DesignPatterns\Creational\AbstractFactory;
class HtmlText extends Text
{
// do something here
}

View File

@@ -1,11 +0,0 @@
<?php
namespace DesignPatterns\Creational\AbstractFactory;
class JsonFactory extends AbstractFactory
{
public function createText(string $content): Text
{
return new JsonText($content);
}
}

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

View File

@@ -1,8 +0,0 @@
<?php
namespace DesignPatterns\Creational\AbstractFactory;
class JsonText extends Text
{
// do something here
}

View File

@@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\Creational\AbstractFactory;
interface Parser
{
public function parse(string $input): array;
}

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

View File

@@ -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:

View File

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

View File

@@ -1,16 +0,0 @@
<?php
namespace DesignPatterns\Creational\AbstractFactory;
abstract class Text
{
/**
* @var string
*/
protected $text;
public function __construct(string $text)
{
$this->text = $text;
}
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\Creational\FactoryMethod;
interface Logger
{
public function log(string $message);
}

View File

@@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\Creational\FactoryMethod;
interface LoggerFactory
{
public function createLogger(): Logger;
}

View File

@@ -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:

View File

@@ -0,0 +1,11 @@
<?php
namespace DesignPatterns\Creational\FactoryMethod;
class StdoutLogger implements Logger
{
public function log(string $message)
{
echo $message;
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace DesignPatterns\Creational\FactoryMethod;
class StdoutLoggerFactory implements LoggerFactory
{
public function createLogger(): Logger
{
return new StdoutLogger();
}
}

View File

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

View File

@@ -1,8 +0,0 @@
<?php
namespace DesignPatterns\Creational\FactoryMethod;
interface VehicleInterface
{
public function setColor(string $rgb);
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 52 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

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

View File

@@ -2,6 +2,10 @@
namespace DesignPatterns\Creational\StaticFactory;
class FormatString implements FormatterInterface
class FormatString implements Formatter
{
public function format(string $input): string
{
return $input;
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\Creational\StaticFactory;
interface Formatter
{
public function format(string $input): string;
}

View File

@@ -1,7 +0,0 @@
<?php
namespace DesignPatterns\Creational\StaticFactory;
interface FormatterInterface
{
}

View File

@@ -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:

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -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" ]

View File

@@ -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:

View File

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

View File

@@ -1,11 +0,0 @@
<?php
namespace DesignPatterns\More\Delegation;
class JuniorDeveloper
{
public function writeBadCode(): string
{
return 'Some junior developer generated code...';
}
}

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -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)

View File

@@ -4,7 +4,6 @@ More
.. toctree::
:titlesonly:
Delegation/README
ServiceLocator/README
Repository/README
EAV/README

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

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

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

View File

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

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

View File

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

View File

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

View File

@@ -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:

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 128 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 184 KiB

36
README.md Executable file → Normal file
View File

@@ -1,6 +1,7 @@
# DesignPatternsPHP
[![Build Status](https://travis-ci.org/domnikl/DesignPatternsPHP.svg?branch=master)](https://travis-ci.org/domnikl/DesignPatternsPHP)
[![Documentation Status](https://readthedocs.org/projects/designpatternsphp/badge/?version=latest)](https://readthedocs.org/projects/designpatternsphp/?badge=latest)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square)](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)

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
<?php
namespace DesignPatterns\Structural\Bridge;
interface Formatter
{
public function format(string $text): string;
}

View File

@@ -1,8 +0,0 @@
<?php
namespace DesignPatterns\Structural\Bridge;
interface FormatterInterface
{
public function format(string $text);
}

View File

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

View File

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

View File

@@ -0,0 +1,11 @@
<?php
namespace DesignPatterns\Structural\Bridge;
class PingService extends Service
{
public function get(): string
{
return $this->implementation->format('pong');
}
}

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More