diff --git a/Behavioral/ChainOfResponsibilities/Handler.php b/Behavioral/ChainOfResponsibilities/Handler.php
index 679734e..fe4e18b 100644
--- a/Behavioral/ChainOfResponsibilities/Handler.php
+++ b/Behavioral/ChainOfResponsibilities/Handler.php
@@ -2,77 +2,42 @@
namespace DesignPatterns\Behavioral\ChainOfResponsibilities;
-/**
- * Handler is a generic handler in the chain of responsibilities.
- *
- * Yes you could have a lighter CoR with a simpler handler but if you want your CoR
- * to be extendable and decoupled, it's a better idea to do things like that in real
- * situations. Usually, a CoR is meant to be changed everytime and evolves, that's
- * why we slice the workflow in little bits of code.
- */
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
abstract class Handler
{
/**
- * @var Handler
+ * @var Handler|null
*/
private $successor = null;
- /**
- * Append a responsibility to the end of chain.
- *
- * A prepend method could be done with the same spirit
- *
- * You could also send the successor in the constructor but in PHP that is a
- * bad idea because you have to remove the type-hint of the parameter because
- * the last handler has a null successor.
- *
- * And if you override the constructor, that Handler can no longer have a
- * successor. One solution is to provide a NullObject (see pattern).
- * It is more preferable to keep the constructor "free" to inject services
- * you need with the DiC of symfony2 for example.
- *
- * @param Handler $handler
- */
- final public function append(Handler $handler)
+ public function __construct(Handler $handler = null)
{
- if (is_null($this->successor)) {
- $this->successor = $handler;
- } else {
- $this->successor->append($handler);
- }
+ $this->successor = $handler;
}
/**
- * Handle the request.
- *
* This approach by using a template method pattern ensures you that
- * each subclass will not forget to call the successor. Besides, the returned
- * boolean value indicates you if the request have been processed or not.
+ * each subclass will not forget to call the successor
*
- * @param Request $req
+ * @param RequestInterface $request
*
- * @return bool
+ * @return string|null
*/
- final public function handle(Request $req)
+ final public function handle(RequestInterface $request)
{
- $req->forDebugOnly = get_called_class();
- $processed = $this->processing($req);
- if (!$processed) {
+ $processed = $this->processing($request);
+
+ if ($processed === null) {
// the request has not been processed by this handler => see the next
- if (!is_null($this->successor)) {
- $processed = $this->successor->handle($req);
+ if ($this->successor !== null) {
+ $processed = $this->successor->handle($request);
}
}
return $processed;
}
- /**
- * Each concrete handler has to implement the processing of the request.
- *
- * @param Request $req
- *
- * @return bool true if the request has been processed
- */
- abstract protected function processing(Request $req);
+ abstract protected function processing(RequestInterface $request);
}
diff --git a/Behavioral/ChainOfResponsibilities/README.rst b/Behavioral/ChainOfResponsibilities/README.rst
index b3f47b9..a3e3b66 100644
--- a/Behavioral/ChainOfResponsibilities/README.rst
+++ b/Behavioral/ChainOfResponsibilities/README.rst
@@ -33,12 +33,6 @@ Code
You can also find these code on `GitHub`_
-Request.php
-
-.. literalinclude:: Request.php
- :language: php
- :linenos:
-
Handler.php
.. literalinclude:: Handler.php
diff --git a/Behavioral/ChainOfResponsibilities/Request.php b/Behavioral/ChainOfResponsibilities/Request.php
deleted file mode 100644
index 68bf24e..0000000
--- a/Behavioral/ChainOfResponsibilities/Request.php
+++ /dev/null
@@ -1,19 +0,0 @@
-data = $data;
- }
-
- protected function processing(Request $req)
- {
- if ('get' === $req->verb) {
- if (array_key_exists($req->key, $this->data)) {
- // the handler IS responsible and then processes the request
- $req->response = $this->data[$req->key];
- // instead of returning true, I could return the value but it proves
- // to be a bad idea. What if the value IS "false" ?
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/Behavioral/ChainOfResponsibilities/Responsible/HttpInMemoryCacheHandler.php b/Behavioral/ChainOfResponsibilities/Responsible/HttpInMemoryCacheHandler.php
new file mode 100644
index 0000000..9391ceb
--- /dev/null
+++ b/Behavioral/ChainOfResponsibilities/Responsible/HttpInMemoryCacheHandler.php
@@ -0,0 +1,45 @@
+data = $data;
+ }
+
+ /**
+ * @param RequestInterface $request
+ *
+ * @return string|null
+ */
+ protected function processing(RequestInterface $request)
+ {
+ $key = sprintf(
+ '%s?%s',
+ $request->getUri()->getPath(),
+ $request->getUri()->getQuery()
+ );
+
+ if ($request->getMethod() == 'GET' && isset($this->data[$key])) {
+ return $this->data[$key];
+ }
+
+ return null;
+ }
+}
diff --git a/Behavioral/ChainOfResponsibilities/Responsible/SlowDatabaseHandler.php b/Behavioral/ChainOfResponsibilities/Responsible/SlowDatabaseHandler.php
new file mode 100644
index 0000000..975ed42
--- /dev/null
+++ b/Behavioral/ChainOfResponsibilities/Responsible/SlowDatabaseHandler.php
@@ -0,0 +1,21 @@
+data = $data;
- }
-
- protected function processing(Request $req)
- {
- if ('get' === $req->verb) {
- if (array_key_exists($req->key, $this->data)) {
- $req->response = $this->data[$req->key];
-
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php b/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php
index 62d1172..296a0c3 100644
--- a/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php
+++ b/Behavioral/ChainOfResponsibilities/Tests/ChainTest.php
@@ -2,80 +2,50 @@
namespace DesignPatterns\Behavioral\ChainOfResponsibilities\Tests;
-use DesignPatterns\Behavioral\ChainOfResponsibilities\Request;
-use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible;
-use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\FastStorage;
-use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage;
+use DesignPatterns\Behavioral\ChainOfResponsibilities\Handler;
+use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\HttpInMemoryCacheHandler;
+use DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowDatabaseHandler;
-/**
- * ChainTest tests the CoR.
- */
class ChainTest extends \PHPUnit_Framework_TestCase
{
/**
- * @var FastStorage
+ * @var Handler
*/
- protected $chain;
+ private $chain;
protected function setUp()
{
- $this->chain = new FastStorage(array('bar' => 'baz'));
- $this->chain->append(new SlowStorage(array('bar' => 'baz', 'foo' => 'bar')));
- }
-
- public function makeRequest()
- {
- $request = new Request();
- $request->verb = 'get';
-
- return array(
- array($request),
+ $this->chain = new HttpInMemoryCacheHandler(
+ ['/foo/bar?index=1' => 'Hello In Memory!'],
+ new SlowDatabaseHandler()
);
}
- /**
- * @dataProvider makeRequest
- */
- public function testFastStorage($request)
+ public function testCanRequestKeyInFastStorage()
{
- $request->key = 'bar';
- $ret = $this->chain->handle($request);
+ $uri = $this->createMock('Psr\Http\Message\UriInterface');
+ $uri->method('getPath')->willReturn('/foo/bar');
+ $uri->method('getQuery')->willReturn('index=1');
- $this->assertTrue($ret);
- $this->assertObjectHasAttribute('response', $request);
- $this->assertEquals('baz', $request->response);
- // despite both handle owns the 'bar' key, the FastStorage is responding first
- $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\FastStorage';
- $this->assertEquals($className, $request->forDebugOnly);
+ $request = $this->createMock('Psr\Http\Message\RequestInterface');
+ $request->method('getMethod')
+ ->willReturn('GET');
+ $request->method('getUri')->willReturn($uri);
+
+ $this->assertEquals('Hello In Memory!', $this->chain->handle($request));
}
- /**
- * @dataProvider makeRequest
- */
- public function testSlowStorage($request)
+ public function testCanRequestKeyInSlowStorage()
{
- $request->key = 'foo';
- $ret = $this->chain->handle($request);
+ $uri = $this->createMock('Psr\Http\Message\UriInterface');
+ $uri->method('getPath')->willReturn('/foo/baz');
+ $uri->method('getQuery')->willReturn('');
- $this->assertTrue($ret);
- $this->assertObjectHasAttribute('response', $request);
- $this->assertEquals('bar', $request->response);
- // FastStorage has no 'foo' key, the SlowStorage is responding
- $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage';
- $this->assertEquals($className, $request->forDebugOnly);
- }
+ $request = $this->createMock('Psr\Http\Message\RequestInterface');
+ $request->method('getMethod')
+ ->willReturn('GET');
+ $request->method('getUri')->willReturn($uri);
- /**
- * @dataProvider makeRequest
- */
- public function testFailure($request)
- {
- $request->key = 'kurukuku';
- $ret = $this->chain->handle($request);
-
- $this->assertFalse($ret);
- // the last responsible :
- $className = 'DesignPatterns\Behavioral\ChainOfResponsibilities\Responsible\SlowStorage';
- $this->assertEquals($className, $request->forDebugOnly);
+ $this->assertEquals('Hello World!', $this->chain->handle($request));
}
}
diff --git a/Behavioral/Command/AddMessageDateCommand.php b/Behavioral/Command/AddMessageDateCommand.php
index 11bb9af..fcaeb4f 100644
--- a/Behavioral/Command/AddMessageDateCommand.php
+++ b/Behavioral/Command/AddMessageDateCommand.php
@@ -4,14 +4,14 @@ namespace DesignPatterns\Behavioral\Command;
/**
* This concrete command tweaks receiver to add current date to messages
- * invoker just knows that it can call "execute".
+ * invoker just knows that it can call "execute"
*/
class AddMessageDateCommand implements UndoableCommandInterface
{
/**
* @var Receiver
*/
- protected $output;
+ private $output;
/**
* Each concrete command is built with different receivers.
diff --git a/Behavioral/Command/CommandInterface.php b/Behavioral/Command/CommandInterface.php
index cd9d9c6..265e862 100644
--- a/Behavioral/Command/CommandInterface.php
+++ b/Behavioral/Command/CommandInterface.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Behavioral\Command;
-/**
- * class CommandInterface.
- */
interface CommandInterface
{
/**
diff --git a/Behavioral/Command/HelloCommand.php b/Behavioral/Command/HelloCommand.php
index 94d4723..1dbfaf5 100644
--- a/Behavioral/Command/HelloCommand.php
+++ b/Behavioral/Command/HelloCommand.php
@@ -4,18 +4,18 @@ namespace DesignPatterns\Behavioral\Command;
/**
* This concrete command calls "print" on the Receiver, but an external
- * invoker just knows that it can call "execute".
+ * invoker just knows that it can call "execute"
*/
class HelloCommand implements CommandInterface
{
/**
* @var Receiver
*/
- protected $output;
+ private $output;
/**
* Each concrete command is built with different receivers.
- * There can be one, many or completely no receivers, but there can be other commands in the parameters.
+ * There can be one, many or completely no receivers, but there can be other commands in the parameters
*
* @param Receiver $console
*/
@@ -29,8 +29,7 @@ class HelloCommand implements CommandInterface
*/
public function execute()
{
- // sometimes, there is no receiver and this is the command which
- // does all the work
+ // sometimes, there is no receiver and this is the command which does all the work
$this->output->write('Hello World');
}
}
diff --git a/Behavioral/Command/Invoker.php b/Behavioral/Command/Invoker.php
index 7942adb..c6e7f93 100644
--- a/Behavioral/Command/Invoker.php
+++ b/Behavioral/Command/Invoker.php
@@ -11,11 +11,11 @@ class Invoker
/**
* @var CommandInterface
*/
- protected $command;
+ private $command;
/**
- * In the invoker we find this kind of method for subscribing the command.
- * There can be also a stack, a list, a fixed set...
+ * in the invoker we find this kind of method for subscribing the command
+ * There can be also a stack, a list, a fixed set ...
*
* @param CommandInterface $cmd
*/
@@ -25,12 +25,10 @@ class Invoker
}
/**
- * executes the command.
+ * executes the command; the invoker is the same whatever is the command
*/
public function run()
{
- // here is a key feature of the invoker
- // the invoker is the same whatever is the command
$this->command->execute();
}
}
diff --git a/Behavioral/Command/Receiver.php b/Behavioral/Command/Receiver.php
index 956ce74..7ca6343 100644
--- a/Behavioral/Command/Receiver.php
+++ b/Behavioral/Command/Receiver.php
@@ -7,14 +7,20 @@ namespace DesignPatterns\Behavioral\Command;
*/
class Receiver
{
+ /**
+ * @var bool
+ */
private $enableDate = false;
- private $output = array();
+ /**
+ * @var string[]
+ */
+ private $output = [];
/**
* @param string $str
*/
- public function write($str)
+ public function write(string $str)
{
if ($this->enableDate) {
$str .= ' ['.date('Y-m-d').']';
@@ -23,13 +29,13 @@ class Receiver
$this->output[] = $str;
}
- public function getOutput()
+ public function getOutput(): string
{
- return implode("\n", $this->output);
+ return join("\n", $this->output);
}
/**
- * Enable receiver to display message date.
+ * Enable receiver to display message date
*/
public function enableDate()
{
@@ -37,7 +43,7 @@ class Receiver
}
/**
- * Disable receiver to display message date.
+ * Disable receiver to display message date
*/
public function disableDate()
{
diff --git a/Behavioral/Command/Tests/CommandTest.php b/Behavioral/Command/Tests/CommandTest.php
index 3852495..cd3ea03 100644
--- a/Behavioral/Command/Tests/CommandTest.php
+++ b/Behavioral/Command/Tests/CommandTest.php
@@ -6,31 +6,15 @@ use DesignPatterns\Behavioral\Command\HelloCommand;
use DesignPatterns\Behavioral\Command\Invoker;
use DesignPatterns\Behavioral\Command\Receiver;
-/**
- * CommandTest has the role of the Client in the Command Pattern.
- */
class CommandTest extends \PHPUnit_Framework_TestCase
{
- /**
- * @var Invoker
- */
- protected $invoker;
-
- /**
- * @var Receiver
- */
- protected $receiver;
-
- protected function setUp()
- {
- $this->invoker = new Invoker();
- $this->receiver = new Receiver();
- }
-
public function testInvocation()
{
- $this->invoker->setCommand(new HelloCommand($this->receiver));
- $this->invoker->run();
- $this->assertEquals($this->receiver->getOutput(), 'Hello World');
+ $invoker = new Invoker();
+ $receiver = new Receiver();
+
+ $invoker->setCommand(new HelloCommand($receiver));
+ $invoker->run();
+ $this->assertEquals($receiver->getOutput(), 'Hello World');
}
}
diff --git a/Behavioral/Command/Tests/UndoableCommandTest.php b/Behavioral/Command/Tests/UndoableCommandTest.php
index 5302a7b..8d10259 100644
--- a/Behavioral/Command/Tests/UndoableCommandTest.php
+++ b/Behavioral/Command/Tests/UndoableCommandTest.php
@@ -6,44 +6,27 @@ use DesignPatterns\Behavioral\Command\AddMessageDateCommand;
use DesignPatterns\Behavioral\Command\HelloCommand;
use DesignPatterns\Behavioral\Command\Invoker;
use DesignPatterns\Behavioral\Command\Receiver;
-use PHPUnit_Framework_TestCase;
-/**
- * UndoableCommandTest has the role of the Client in the Command Pattern.
- */
-class UndoableCommandTest extends PHPUnit_Framework_TestCase
+class UndoableCommandTest extends \PHPUnit_Framework_TestCase
{
- /**
- * @var Invoker
- */
- protected $invoker;
-
- /**
- * @var Receiver
- */
- protected $receiver;
-
- protected function setUp()
- {
- $this->invoker = new Invoker();
- $this->receiver = new Receiver();
- }
-
public function testInvocation()
{
- $this->invoker->setCommand(new HelloCommand($this->receiver));
- $this->invoker->run();
- $this->assertEquals($this->receiver->getOutput(), 'Hello World');
+ $invoker = new Invoker();
+ $receiver = new Receiver();
- $messageDateCommand = new AddMessageDateCommand($this->receiver);
+ $invoker->setCommand(new HelloCommand($receiver));
+ $invoker->run();
+ $this->assertEquals($receiver->getOutput(), 'Hello World');
+
+ $messageDateCommand = new AddMessageDateCommand($receiver);
$messageDateCommand->execute();
- $this->invoker->run();
- $this->assertEquals($this->receiver->getOutput(), "Hello World\nHello World [".date('Y-m-d').']');
+ $invoker->run();
+ $this->assertEquals($receiver->getOutput(), "Hello World\nHello World [".date('Y-m-d').']');
$messageDateCommand->undo();
- $this->invoker->run();
- $this->assertEquals($this->receiver->getOutput(), "Hello World\nHello World [".date('Y-m-d')."]\nHello World");
+ $invoker->run();
+ $this->assertEquals($receiver->getOutput(), "Hello World\nHello World [".date('Y-m-d')."]\nHello World");
}
}
diff --git a/Behavioral/Command/UndoableCommandInterface.php b/Behavioral/Command/UndoableCommandInterface.php
index f9234ab..52b02e9 100644
--- a/Behavioral/Command/UndoableCommandInterface.php
+++ b/Behavioral/Command/UndoableCommandInterface.php
@@ -2,14 +2,10 @@
namespace DesignPatterns\Behavioral\Command;
-/**
- * Interface UndoableCommandInterface.
- */
interface UndoableCommandInterface extends CommandInterface
{
/**
* This method is used to undo change made by command execution
- * The Receiver goes in the constructor.
*/
public function undo();
}
diff --git a/Behavioral/Mediator/Colleague.php b/Behavioral/Mediator/Colleague.php
index c74dee5..3a83742 100644
--- a/Behavioral/Mediator/Colleague.php
+++ b/Behavioral/Mediator/Colleague.php
@@ -4,7 +4,7 @@ namespace DesignPatterns\Behavioral\Mediator;
/**
* Colleague is an abstract colleague who works together but he only knows
- * the Mediator, not other colleague.
+ * the Mediator, not other colleagues
*/
abstract class Colleague
{
@@ -13,21 +13,13 @@ abstract class Colleague
*
* @var MediatorInterface
*/
- private $mediator;
+ protected $mediator;
/**
- * @param MediatorInterface $medium
+ * @param MediatorInterface $mediator
*/
- public function __construct(MediatorInterface $medium)
+ public function setMediator(MediatorInterface $mediator)
{
- // in this way, we are sure the concrete colleague knows the mediator
- $this->mediator = $medium;
- }
-
- // for subclasses
-
- protected function getMediator()
- {
- return $this->mediator;
+ $this->mediator = $mediator;
}
}
diff --git a/Behavioral/Mediator/Mediator.php b/Behavioral/Mediator/Mediator.php
index 98a7890..08e1b71 100644
--- a/Behavioral/Mediator/Mediator.php
+++ b/Behavioral/Mediator/Mediator.php
@@ -3,59 +3,54 @@
namespace DesignPatterns\Behavioral\Mediator;
/**
- * Mediator is the concrete Mediator for this design pattern.
- * In this example, I have made a "Hello World" with the Mediator Pattern.
+ * Mediator is the concrete Mediator for this design pattern
+ *
+ * In this example, I have made a "Hello World" with the Mediator Pattern
*/
class Mediator implements MediatorInterface
{
/**
* @var Subsystem\Server
*/
- protected $server;
+ private $server;
/**
* @var Subsystem\Database
*/
- protected $database;
+ private $database;
/**
* @var Subsystem\Client
*/
- protected $client;
+ private $client;
/**
- * @param Subsystem\Database $db
- * @param Subsystem\Client $cl
- * @param Subsystem\Server $srv
+ * @param Subsystem\Database $database
+ * @param Subsystem\Client $client
+ * @param Subsystem\Server $server
*/
- public function setColleague(Subsystem\Database $db, Subsystem\Client $cl, Subsystem\Server $srv)
+ public function __construct(Subsystem\Database $database, Subsystem\Client $client, Subsystem\Server $server)
{
- $this->database = $db;
- $this->server = $srv;
- $this->client = $cl;
+ $this->database = $database;
+ $this->server = $server;
+ $this->client = $client;
+
+ $this->database->setMediator($this);
+ $this->server->setMediator($this);
+ $this->client->setMediator($this);
}
- /**
- * make request.
- */
public function makeRequest()
{
$this->server->process();
}
- /**
- * query db.
- *
- * @return mixed
- */
- public function queryDb()
+ public function queryDb(): string
{
return $this->database->getData();
}
/**
- * send response.
- *
* @param string $content
*/
public function sendResponse($content)
diff --git a/Behavioral/Mediator/MediatorInterface.php b/Behavioral/Mediator/MediatorInterface.php
index dbdd489..5ec717a 100644
--- a/Behavioral/Mediator/MediatorInterface.php
+++ b/Behavioral/Mediator/MediatorInterface.php
@@ -4,7 +4,7 @@ namespace DesignPatterns\Behavioral\Mediator;
/**
* MediatorInterface is a contract for the Mediator
- * This interface is not mandatory but it is better for LSP concerns.
+ * This interface is not mandatory but it is better for Liskov substitution principle concerns.
*/
interface MediatorInterface
{
@@ -16,12 +16,12 @@ interface MediatorInterface
public function sendResponse($content);
/**
- * makes a request.
+ * makes a request
*/
public function makeRequest();
/**
- * queries the DB.
+ * queries the DB
*/
public function queryDb();
}
diff --git a/Behavioral/Mediator/Subsystem/Client.php b/Behavioral/Mediator/Subsystem/Client.php
index f7a21c9..e7897a4 100644
--- a/Behavioral/Mediator/Subsystem/Client.php
+++ b/Behavioral/Mediator/Subsystem/Client.php
@@ -5,24 +5,16 @@ namespace DesignPatterns\Behavioral\Mediator\Subsystem;
use DesignPatterns\Behavioral\Mediator\Colleague;
/**
- * Client is a client that make request et get response.
+ * Client is a client that makes requests and gets the response response.
*/
class Client extends Colleague
{
- /**
- * request.
- */
public function request()
{
- $this->getMediator()->makeRequest();
+ $this->mediator->makeRequest();
}
- /**
- * output content.
- *
- * @param string $content
- */
- public function output($content)
+ public function output(string $content)
{
echo $content;
}
diff --git a/Behavioral/Mediator/Subsystem/Database.php b/Behavioral/Mediator/Subsystem/Database.php
index 69ad6cf..9255f22 100644
--- a/Behavioral/Mediator/Subsystem/Database.php
+++ b/Behavioral/Mediator/Subsystem/Database.php
@@ -4,15 +4,9 @@ namespace DesignPatterns\Behavioral\Mediator\Subsystem;
use DesignPatterns\Behavioral\Mediator\Colleague;
-/**
- * Database is a database service.
- */
class Database extends Colleague
{
- /**
- * @return string
- */
- public function getData()
+ public function getData(): string
{
return 'World';
}
diff --git a/Behavioral/Mediator/Subsystem/Server.php b/Behavioral/Mediator/Subsystem/Server.php
index 1602bcb..c05be5e 100644
--- a/Behavioral/Mediator/Subsystem/Server.php
+++ b/Behavioral/Mediator/Subsystem/Server.php
@@ -4,17 +4,11 @@ namespace DesignPatterns\Behavioral\Mediator\Subsystem;
use DesignPatterns\Behavioral\Mediator\Colleague;
-/**
- * Server serves responses.
- */
class Server extends Colleague
{
- /**
- * process on server.
- */
public function process()
{
- $data = $this->getMediator()->queryDb();
- $this->getMediator()->sendResponse("Hello $data");
+ $data = $this->mediator->queryDb();
+ $this->mediator->sendResponse(sprintf("Hello %s", $data));
}
}
diff --git a/Behavioral/Mediator/Tests/MediatorTest.php b/Behavioral/Mediator/Tests/MediatorTest.php
index 2bce947..2cd40a6 100644
--- a/Behavioral/Mediator/Tests/MediatorTest.php
+++ b/Behavioral/Mediator/Tests/MediatorTest.php
@@ -7,27 +7,14 @@ use DesignPatterns\Behavioral\Mediator\Subsystem\Client;
use DesignPatterns\Behavioral\Mediator\Subsystem\Database;
use DesignPatterns\Behavioral\Mediator\Subsystem\Server;
-/**
- * MediatorTest tests hello world.
- */
class MediatorTest extends \PHPUnit_Framework_TestCase
{
- protected $client;
-
- protected function setUp()
- {
- $media = new Mediator();
- $this->client = new Client($media);
- $media->setColleague(new Database($media), $this->client, new Server($media));
- }
-
public function testOutputHelloWorld()
{
- // testing if Hello World is output :
+ $client = new Client();
+ new Mediator(new Database(), $client, new Server());
+
$this->expectOutputString('Hello World');
- // as you see, the 3 components Client, Server and Database are totally decoupled
- $this->client->request();
- // Anyway, it remains complexity in the Mediator that's why the pattern
- // Observer is preferable in mnay situations.
+ $client->request();
}
}
diff --git a/Behavioral/Memento/Caretaker.php b/Behavioral/Memento/Caretaker.php
deleted file mode 100644
index d80454a..0000000
--- a/Behavioral/Memento/Caretaker.php
+++ /dev/null
@@ -1,49 +0,0 @@
-history[$id];
- }
-
- /**
- * @param Memento $state
- */
- public function saveToHistory(Memento $state)
- {
- $this->history[] = $state;
- }
-
- public function runCustomLogic()
- {
- $originator = new Originator();
-
- //Setting state to State1
- $originator->setState('State1');
- //Setting state to State2
- $originator->setState('State2');
- //Saving State2 to Memento
- $this->saveToHistory($originator->getStateAsMemento());
- //Setting state to State3
- $originator->setState('State3');
-
- // We can request multiple mementos, and choose which one to roll back to.
- // Saving State3 to Memento
- $this->saveToHistory($originator->getStateAsMemento());
- //Setting state to State4
- $originator->setState('State4');
-
- $originator->restoreFromMemento($this->getFromHistory(1));
- //State after restoring from Memento: State3
-
- return $originator->getStateAsMemento()->getState();
- }
-}
diff --git a/Behavioral/Memento/Memento.php b/Behavioral/Memento/Memento.php
index 4dd2fc8..f75fcc9 100644
--- a/Behavioral/Memento/Memento.php
+++ b/Behavioral/Memento/Memento.php
@@ -4,19 +4,21 @@ namespace DesignPatterns\Behavioral\Memento;
class Memento
{
- /* @var mixed */
+ /**
+ * @var State
+ */
private $state;
/**
- * @param mixed $stateToSave
+ * @param State $stateToSave
*/
- public function __construct($stateToSave)
+ public function __construct(State $stateToSave)
{
$this->state = $stateToSave;
}
/**
- * @return mixed
+ * @return State
*/
public function getState()
{
diff --git a/Behavioral/Memento/Originator.php b/Behavioral/Memento/Originator.php
deleted file mode 100644
index 3acb0c1..0000000
--- a/Behavioral/Memento/Originator.php
+++ /dev/null
@@ -1,38 +0,0 @@
-state = $state;
- }
-
- /**
- * @return Memento
- */
- public function getStateAsMemento()
- {
- // you must save a separate copy in Memento
- $state = is_object($this->state) ? clone $this->state : $this->state;
-
- return new Memento($state);
- }
-
- public function restoreFromMemento(Memento $memento)
- {
- $this->state = $memento->getState();
- }
-}
diff --git a/Behavioral/Memento/README.rst b/Behavioral/Memento/README.rst
index 911e30e..32ec703 100644
--- a/Behavioral/Memento/README.rst
+++ b/Behavioral/Memento/README.rst
@@ -6,8 +6,8 @@ Purpose
It provides the ability to restore an object to it's previous state (undo
via rollback) or to gain access to state of the object, without revealing
-it's implementation (i.e., the object is not required to have a functional
-for return the current state).
+it's implementation (i.e., the object is not required to have a function
+to return the current state).
The memento pattern is implemented with three objects: the Originator, a
Caretaker and a Memento.
@@ -66,9 +66,9 @@ Originator.php
:language: php
:linenos:
-Caretaker.php
+Ticket.php
-.. literalinclude:: Caretaker.php
+.. literalinclude:: Ticket.php
:language: php
:linenos:
diff --git a/Behavioral/Memento/State.php b/Behavioral/Memento/State.php
new file mode 100644
index 0000000..6cb5dd1
--- /dev/null
+++ b/Behavioral/Memento/State.php
@@ -0,0 +1,48 @@
+state = $state;
+ }
+
+ private static function ensureIsValidState(string $state)
+ {
+ if (!in_array($state, self::$validStates)) {
+ throw new \InvalidArgumentException('Invalid state given');
+ }
+ }
+
+ public function __toString(): string
+ {
+ return $this->state;
+ }
+}
diff --git a/Behavioral/Memento/Tests/MementoTest.php b/Behavioral/Memento/Tests/MementoTest.php
index 722dbfa..d4a44f3 100644
--- a/Behavioral/Memento/Tests/MementoTest.php
+++ b/Behavioral/Memento/Tests/MementoTest.php
@@ -2,161 +2,30 @@
namespace DesignPatterns\Behavioral\Memento\Tests;
-use DesignPatterns\Behavioral\Memento\Caretaker;
-use DesignPatterns\Behavioral\Memento\Memento;
-use DesignPatterns\Behavioral\Memento\Originator;
+use DesignPatterns\Behavioral\Memento\State;
+use DesignPatterns\Behavioral\Memento\Ticket;
-/**
- * MementoTest tests the memento pattern.
- */
class MementoTest extends \PHPUnit_Framework_TestCase
{
- public function testUsageExample()
+ public function testOpenTicketAssignAndSetBackToOpen()
{
- $originator = new Originator();
- $caretaker = new Caretaker();
+ $ticket = new Ticket();
- $character = new \stdClass();
- // new object
- $character->name = 'Gandalf';
- // connect Originator to character object
- $originator->setState($character);
+ // open the ticket
+ $ticket->open();
+ $openedState = $ticket->getState();
+ $this->assertEquals(State::STATE_OPENED, (string) $ticket->getState());
- // work on the object
- $character->name = 'Gandalf the Grey';
- // still change something
- $character->race = 'Maia';
- // time to save state
- $snapshot = $originator->getStateAsMemento();
- // put state to log
- $caretaker->saveToHistory($snapshot);
+ $memento = $ticket->saveToMemento();
- // change something
- $character->name = 'Sauron';
- // and again
- $character->race = 'Ainur';
- // state inside the Originator was equally changed
- $this->assertAttributeEquals($character, 'state', $originator);
+ // assign the ticket
+ $ticket->assign();
+ $this->assertEquals(State::STATE_ASSIGNED, (string) $ticket->getState());
- // time to save another state
- $snapshot = $originator->getStateAsMemento();
- // put state to log
- $caretaker->saveToHistory($snapshot);
+ // no restore to the opened state, but verify that the state object has been cloned for the memento
+ $ticket->restoreFromMemento($memento);
- $rollback = $caretaker->getFromHistory(0);
- // return to first state
- $originator->restoreFromMemento($rollback);
- // use character from old state
- $character = $rollback->getState();
-
- // yes, that what we need
- $this->assertEquals('Gandalf the Grey', $character->name);
- // make new changes
- $character->name = 'Gandalf the White';
-
- // and Originator linked to actual object again
- $this->assertAttributeEquals($character, 'state', $originator);
- }
-
- public function testStringState()
- {
- $originator = new Originator();
- $originator->setState('State1');
-
- $this->assertAttributeEquals('State1', 'state', $originator);
-
- $originator->setState('State2');
- $this->assertAttributeEquals('State2', 'state', $originator);
-
- $snapshot = $originator->getStateAsMemento();
- $this->assertAttributeEquals('State2', 'state', $snapshot);
-
- $originator->setState('State3');
- $this->assertAttributeEquals('State3', 'state', $originator);
-
- $originator->restoreFromMemento($snapshot);
- $this->assertAttributeEquals('State2', 'state', $originator);
- }
-
- public function testSnapshotIsClone()
- {
- $originator = new Originator();
- $object = new \stdClass();
-
- $originator->setState($object);
- $snapshot = $originator->getStateAsMemento();
- $object->new_property = 1;
-
- $this->assertAttributeEquals($object, 'state', $originator);
- $this->assertAttributeNotEquals($object, 'state', $snapshot);
-
- $originator->restoreFromMemento($snapshot);
- $this->assertAttributeNotEquals($object, 'state', $originator);
- }
-
- public function testCanChangeActualState()
- {
- $originator = new Originator();
- $first_state = new \stdClass();
-
- $originator->setState($first_state);
- $snapshot = $originator->getStateAsMemento();
- $second_state = $snapshot->getState();
-
- // still actual
- $first_state->first_property = 1;
- // just history
- $second_state->second_property = 2;
- $this->assertAttributeEquals($first_state, 'state', $originator);
- $this->assertAttributeNotEquals($second_state, 'state', $originator);
-
- $originator->restoreFromMemento($snapshot);
- // now it lost state
- $first_state->first_property = 11;
- // must be actual
- $second_state->second_property = 22;
- $this->assertAttributeEquals($second_state, 'state', $originator);
- $this->assertAttributeNotEquals($first_state, 'state', $originator);
- }
-
- public function testStateWithDifferentObjects()
- {
- $originator = new Originator();
-
- $first = new \stdClass();
- $first->data = 'foo';
-
- $originator->setState($first);
- $this->assertAttributeEquals($first, 'state', $originator);
-
- $first_snapshot = $originator->getStateAsMemento();
- $this->assertAttributeEquals($first, 'state', $first_snapshot);
-
- $second = new \stdClass();
- $second->data = 'bar';
- $originator->setState($second);
- $this->assertAttributeEquals($second, 'state', $originator);
-
- $originator->restoreFromMemento($first_snapshot);
- $this->assertAttributeEquals($first, 'state', $originator);
- }
-
- public function testCaretaker()
- {
- $caretaker = new Caretaker();
- $memento1 = new Memento('foo');
- $memento2 = new Memento('bar');
- $caretaker->saveToHistory($memento1);
- $caretaker->saveToHistory($memento2);
- $this->assertAttributeEquals(array($memento1, $memento2), 'history', $caretaker);
- $this->assertEquals($memento1, $caretaker->getFromHistory(0));
- $this->assertEquals($memento2, $caretaker->getFromHistory(1));
- }
-
- public function testCaretakerCustomLogic()
- {
- $caretaker = new Caretaker();
- $result = $caretaker->runCustomLogic();
- $this->assertEquals('State3', $result);
+ $this->assertEquals(State::STATE_OPENED, (string) $ticket->getState());
+ $this->assertNotSame($openedState, $ticket->getState());
}
}
diff --git a/Behavioral/Memento/Ticket.php b/Behavioral/Memento/Ticket.php
new file mode 100644
index 0000000..13f75e7
--- /dev/null
+++ b/Behavioral/Memento/Ticket.php
@@ -0,0 +1,49 @@
+currentState = new State(State::STATE_CREATED);
+ }
+
+ public function open()
+ {
+ $this->currentState = new State(State::STATE_OPENED);
+ }
+
+ public function assign()
+ {
+ $this->currentState = new State(State::STATE_ASSIGNED);
+ }
+
+ public function close()
+ {
+ $this->currentState = new State(State::STATE_CLOSED);
+ }
+
+ public function saveToMemento(): Memento
+ {
+ return new Memento(clone $this->currentState);
+ }
+
+ public function restoreFromMemento(Memento $memento)
+ {
+ $this->currentState = $memento->getState();
+ }
+
+ public function getState(): State
+ {
+ return $this->currentState;
+ }
+}
diff --git a/Behavioral/Memento/uml/Memento.uml b/Behavioral/Memento/uml/Memento.uml
new file mode 100644
index 0000000..194187a
--- /dev/null
+++ b/Behavioral/Memento/uml/Memento.uml
@@ -0,0 +1,21 @@
+
+
+ PHP
+ \DesignPatterns\Behavioral\Memento\Memento
+
+ \DesignPatterns\Behavioral\Memento\State
+ \DesignPatterns\Behavioral\Memento\Memento
+ \DesignPatterns\Behavioral\Memento\Ticket
+
+
+
+
+
+
+ Fields
+ Constants
+ Methods
+
+ private
+
+
diff --git a/Behavioral/Memento/uml/Momento.uml b/Behavioral/Memento/uml/Momento.uml
deleted file mode 100644
index c72667e..0000000
--- a/Behavioral/Memento/uml/Momento.uml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
- PHP
- \DesignPatterns\Behavioral\Memento\Caretaker
-
- \DesignPatterns\Behavioral\Memento\Caretaker
- \DesignPatterns\Behavioral\Memento\Originator
- \DesignPatterns\Behavioral\Memento\Memento
-
-
-
-
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
diff --git a/Behavioral/Memento/uml/uml.png b/Behavioral/Memento/uml/uml.png
index 0fde074..417b293 100644
Binary files a/Behavioral/Memento/uml/uml.png and b/Behavioral/Memento/uml/uml.png differ
diff --git a/Behavioral/Memento/uml/uml.svg b/Behavioral/Memento/uml/uml.svg
index 2cc47a8..aeb665a 100644
--- a/Behavioral/Memento/uml/uml.svg
+++ b/Behavioral/Memento/uml/uml.svg
@@ -1,310 +1,952 @@
-
+
diff --git a/Behavioral/NullObject/LoggerInterface.php b/Behavioral/NullObject/LoggerInterface.php
index 99a28c7..7c1ff7d 100644
--- a/Behavioral/NullObject/LoggerInterface.php
+++ b/Behavioral/NullObject/LoggerInterface.php
@@ -3,16 +3,9 @@
namespace DesignPatterns\Behavioral\NullObject;
/**
- * LoggerInterface is a contract for logging something.
- *
- * Key feature: NullLogger MUST inherit from this interface like any other Loggers
+ * Key feature: NullLogger must inherit from this interface like any other loggers
*/
interface LoggerInterface
{
- /**
- * @param string $str
- *
- * @return mixed
- */
- public function log($str);
+ public function log(string $str);
}
diff --git a/Behavioral/NullObject/NullLogger.php b/Behavioral/NullObject/NullLogger.php
index a4bf469..afddbd6 100644
--- a/Behavioral/NullObject/NullLogger.php
+++ b/Behavioral/NullObject/NullLogger.php
@@ -2,19 +2,9 @@
namespace DesignPatterns\Behavioral\NullObject;
-/**
- * Performance concerns : ok there is a call for nothing but we spare an "if is_null"
- * I didn't run a benchmark but I think it's equivalent.
- *
- * Key feature : of course this logger MUST implement the same interface (or abstract)
- * like the other loggers.
- */
class NullLogger implements LoggerInterface
{
- /**
- * {@inheritdoc}
- */
- public function log($str)
+ public function log(string $str)
{
// do nothing
}
diff --git a/Behavioral/NullObject/PrintLogger.php b/Behavioral/NullObject/PrintLogger.php
index 371c1ab..6c1d4ba 100644
--- a/Behavioral/NullObject/PrintLogger.php
+++ b/Behavioral/NullObject/PrintLogger.php
@@ -2,15 +2,9 @@
namespace DesignPatterns\Behavioral\NullObject;
-/**
- * PrintLogger is a logger that prints the log entry to standard output.
- */
class PrintLogger implements LoggerInterface
{
- /**
- * @param string $str
- */
- public function log($str)
+ public function log(string $str)
{
echo $str;
}
diff --git a/Behavioral/NullObject/Service.php b/Behavioral/NullObject/Service.php
index 56a3847..81baf8f 100644
--- a/Behavioral/NullObject/Service.php
+++ b/Behavioral/NullObject/Service.php
@@ -2,24 +2,19 @@
namespace DesignPatterns\Behavioral\NullObject;
-/**
- * Service is dummy service that uses a logger.
- */
class Service
{
/**
* @var LoggerInterface
*/
- protected $logger;
+ private $logger;
/**
- * we inject the logger in ctor and it is mandatory.
- *
- * @param LoggerInterface $log
+ * @param LoggerInterface $logger
*/
- public function __construct(LoggerInterface $log)
+ public function __construct(LoggerInterface $logger)
{
- $this->logger = $log;
+ $this->logger = $logger;
}
/**
@@ -27,8 +22,7 @@ class Service
*/
public function doSomething()
{
- // no more check "if (!is_null($this->logger))..." with the NullObject pattern
+ // notice here that you don't have to check if the logger is set with eg. is_null(), instead just use it
$this->logger->log('We are in '.__METHOD__);
- // something to do...
}
}
diff --git a/Behavioral/NullObject/Tests/LoggerTest.php b/Behavioral/NullObject/Tests/LoggerTest.php
index 034b577..0e35aa5 100644
--- a/Behavioral/NullObject/Tests/LoggerTest.php
+++ b/Behavioral/NullObject/Tests/LoggerTest.php
@@ -6,17 +6,12 @@ use DesignPatterns\Behavioral\NullObject\NullLogger;
use DesignPatterns\Behavioral\NullObject\PrintLogger;
use DesignPatterns\Behavioral\NullObject\Service;
-/**
- * LoggerTest tests for different loggers.
- */
class LoggerTest extends \PHPUnit_Framework_TestCase
{
public function testNullObject()
{
- // one can use a singleton for NullObjet : I don't think it's a good idea
- // because the purpose behind null object is to "avoid special case".
$service = new Service(new NullLogger());
- $this->expectOutputString(null); // no output
+ $this->expectOutputString(null);
$service->doSomething();
}
diff --git a/Behavioral/Observer/Tests/ObserverTest.php b/Behavioral/Observer/Tests/ObserverTest.php
index d9dacec..c263997 100644
--- a/Behavioral/Observer/Tests/ObserverTest.php
+++ b/Behavioral/Observer/Tests/ObserverTest.php
@@ -5,65 +5,16 @@ namespace DesignPatterns\Behavioral\Observer\Tests;
use DesignPatterns\Behavioral\Observer\User;
use DesignPatterns\Behavioral\Observer\UserObserver;
-/**
- * ObserverTest tests the Observer pattern.
- */
class ObserverTest extends \PHPUnit_Framework_TestCase
{
- protected $observer;
-
- protected function setUp()
+ public function testChangeInUserLeadsToUserObserverBeingNotified()
{
- $this->observer = new UserObserver();
- }
+ $observer = new UserObserver();
- /**
- * Tests the notification.
- */
- public function testNotify()
- {
- $this->expectOutputString('DesignPatterns\Behavioral\Observer\User has been updated');
- $subject = new User();
+ $user = new User();
+ $user->attach($observer);
- $subject->attach($this->observer);
- $subject->property = 123;
- }
-
- /**
- * Tests the subscribing.
- */
- public function testAttachDetach()
- {
- $subject = new User();
- $reflection = new \ReflectionProperty($subject, 'observers');
-
- $reflection->setAccessible(true);
- /** @var \SplObjectStorage $observers */
- $observers = $reflection->getValue($subject);
-
- $this->assertInstanceOf('SplObjectStorage', $observers);
- $this->assertFalse($observers->contains($this->observer));
-
- $subject->attach($this->observer);
- $this->assertTrue($observers->contains($this->observer));
-
- $subject->detach($this->observer);
- $this->assertFalse($observers->contains($this->observer));
- }
-
- /**
- * Tests the update() invocation on a mockup.
- */
- public function testUpdateCalling()
- {
- $subject = new User();
- $observer = $this->getMock('SplObserver');
- $subject->attach($observer);
-
- $observer->expects($this->once())
- ->method('update')
- ->with($subject);
-
- $subject->notify();
+ $user->changeEmail('foo@bar.com');
+ $this->assertCount(1, $observer->getChangedUsers());
}
}
diff --git a/Behavioral/Observer/User.php b/Behavioral/Observer/User.php
index 0d2a817..6fd58d4 100644
--- a/Behavioral/Observer/User.php
+++ b/Behavioral/Observer/User.php
@@ -3,60 +3,42 @@
namespace DesignPatterns\Behavioral\Observer;
/**
- * Observer pattern : The observed object (the subject).
- *
- * The subject maintains a list of Observers and sends notifications.
+ * User implements the observed object (called Subject), it maintains a list of observers and sends notifications to
+ * them in case changes are made on the User object
*/
class User implements \SplSubject
{
/**
- * user data.
- *
- * @var array
+ * @var string
*/
- protected $data = array();
+ private $email;
/**
- * observers.
- *
* @var \SplObjectStorage
*/
- protected $observers;
+ private $observers;
public function __construct()
{
$this->observers = new \SplObjectStorage();
}
- /**
- * attach a new observer.
- *
- * @param \SplObserver $observer
- *
- * @return void
- */
public function attach(\SplObserver $observer)
{
$this->observers->attach($observer);
}
- /**
- * detach an observer.
- *
- * @param \SplObserver $observer
- *
- * @return void
- */
public function detach(\SplObserver $observer)
{
$this->observers->detach($observer);
}
- /**
- * notify observers.
- *
- * @return void
- */
+ public function changeEmail(string $email)
+ {
+ $this->email = $email;
+ $this->notify();
+ }
+
public function notify()
{
/** @var \SplObserver $observer */
@@ -64,21 +46,4 @@ class User implements \SplSubject
$observer->update($this);
}
}
-
- /**
- * Ideally one would better write setter/getter for all valid attributes and only call notify()
- * on attributes that matter when changed.
- *
- * @param string $name
- * @param mixed $value
- *
- * @return void
- */
- public function __set($name, $value)
- {
- $this->data[$name] = $value;
-
- // notify the observers, that user has been updated
- $this->notify();
- }
}
diff --git a/Behavioral/Observer/UserObserver.php b/Behavioral/Observer/UserObserver.php
index f2673ba..243e740 100644
--- a/Behavioral/Observer/UserObserver.php
+++ b/Behavioral/Observer/UserObserver.php
@@ -2,19 +2,28 @@
namespace DesignPatterns\Behavioral\Observer;
-/**
- * class UserObserver.
- */
class UserObserver implements \SplObserver
{
/**
- * This is the only method to implement as an observer.
- * It is called by the Subject (usually by SplSubject::notify() ).
+ * @var User[]
+ */
+ private $changedUsers = [];
+
+ /**
+ * It is called by the Subject, usually by SplSubject::notify()
*
* @param \SplSubject $subject
*/
public function update(\SplSubject $subject)
{
- echo get_class($subject).' has been updated';
+ $this->changedUsers[] = clone $subject;
+ }
+
+ /**
+ * @return User[]
+ */
+ public function getChangedUsers(): array
+ {
+ return $this->changedUsers;
}
}
diff --git a/Behavioral/Observer/uml/Observer.uml b/Behavioral/Observer/uml/Observer.uml
index 0e5ddef..b74d770 100644
--- a/Behavioral/Observer/uml/Observer.uml
+++ b/Behavioral/Observer/uml/Observer.uml
@@ -1,27 +1,26 @@
-
-
- PHP
- \DesignPatterns\Behavioral\Observer\User
-
- \DesignPatterns\Behavioral\Observer\UserObserver
- \DesignPatterns\Behavioral\Observer\User
- \SplSubject
-
-
-
-
-
-
-
-
-
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Behavioral\Observer\UserObserver
+
+ \DesignPatterns\Behavioral\Observer\UserObserver
+ \SplObserver
+ \DesignPatterns\Behavioral\Observer\User
+
+
+
+
+
+
+
+
+
+
+
+ Fields
+ Constants
+ Methods
+
+ private
+
+
diff --git a/Behavioral/Observer/uml/uml.png b/Behavioral/Observer/uml/uml.png
index 0300bd4..3eff464 100644
Binary files a/Behavioral/Observer/uml/uml.png and b/Behavioral/Observer/uml/uml.png differ
diff --git a/Behavioral/Observer/uml/uml.svg b/Behavioral/Observer/uml/uml.svg
index 09c79c1..44f3c60 100644
--- a/Behavioral/Observer/uml/uml.svg
+++ b/Behavioral/Observer/uml/uml.svg
@@ -1,310 +1,649 @@
-
+
diff --git a/Behavioral/Specification/AbstractSpecification.php b/Behavioral/Specification/AbstractSpecification.php
deleted file mode 100644
index 66d61b8..0000000
--- a/Behavioral/Specification/AbstractSpecification.php
+++ /dev/null
@@ -1,52 +0,0 @@
-specifications = $specifications;
+ }
+
+ public function isSatisfiedBy(Item $item): bool
+ {
+ $satisfied = [];
+
+ foreach ($this->specifications as $specification) {
+ $satisfied[] = $specification->isSatisfiedBy($item);
+ }
+
+ return !in_array(false, $satisfied);
+ }
+}
diff --git a/Behavioral/Specification/Either.php b/Behavioral/Specification/Either.php
deleted file mode 100644
index a30372a..0000000
--- a/Behavioral/Specification/Either.php
+++ /dev/null
@@ -1,36 +0,0 @@
-left = $left;
- $this->right = $right;
- }
-
- /**
- * Returns the evaluation of both wrapped specifications as a logical OR.
- *
- * @param Item $item
- *
- * @return bool
- */
- public function isSatisfiedBy(Item $item)
- {
- return $this->left->isSatisfiedBy($item) || $this->right->isSatisfiedBy($item);
- }
-}
diff --git a/Behavioral/Specification/Item.php b/Behavioral/Specification/Item.php
index 8d639e0..a167f0a 100644
--- a/Behavioral/Specification/Item.php
+++ b/Behavioral/Specification/Item.php
@@ -2,29 +2,19 @@
namespace DesignPatterns\Behavioral\Specification;
-/**
- * An trivial item.
- */
class Item
{
- protected $price;
-
/**
- * An item must have a price.
- *
- * @param int $price
+ * @var float
*/
- public function __construct($price)
+ private $price;
+
+ public function __construct(float $price)
{
$this->price = $price;
}
- /**
- * Get the items price.
- *
- * @return int
- */
- public function getPrice()
+ public function getPrice(): float
{
return $this->price;
}
diff --git a/Behavioral/Specification/Not.php b/Behavioral/Specification/Not.php
deleted file mode 100644
index e99a6cc..0000000
--- a/Behavioral/Specification/Not.php
+++ /dev/null
@@ -1,33 +0,0 @@
-spec = $spec;
- }
-
- /**
- * Returns the negated result of the wrapped specification.
- *
- * @param Item $item
- *
- * @return bool
- */
- public function isSatisfiedBy(Item $item)
- {
- return !$this->spec->isSatisfiedBy($item);
- }
-}
diff --git a/Behavioral/Specification/NotSpecification.php b/Behavioral/Specification/NotSpecification.php
new file mode 100644
index 0000000..db47b51
--- /dev/null
+++ b/Behavioral/Specification/NotSpecification.php
@@ -0,0 +1,21 @@
+specification = $specification;
+ }
+
+ public function isSatisfiedBy(Item $item): bool
+ {
+ return !$this->specification->isSatisfiedBy($item);
+ }
+}
diff --git a/Behavioral/Specification/OrSpecification.php b/Behavioral/Specification/OrSpecification.php
new file mode 100644
index 0000000..62a4e08
--- /dev/null
+++ b/Behavioral/Specification/OrSpecification.php
@@ -0,0 +1,30 @@
+specifications = $specifications;
+ }
+
+ public function isSatisfiedBy(Item $item): bool
+ {
+ $satisfied = [];
+
+ foreach ($this->specifications as $specification) {
+ $satisfied[] = $specification->isSatisfiedBy($item);
+ }
+
+ return in_array(true, $satisfied);
+ }
+}
diff --git a/Behavioral/Specification/Plus.php b/Behavioral/Specification/Plus.php
deleted file mode 100644
index 26bd585..0000000
--- a/Behavioral/Specification/Plus.php
+++ /dev/null
@@ -1,36 +0,0 @@
-left = $left;
- $this->right = $right;
- }
-
- /**
- * Checks if the composite AND of specifications passes.
- *
- * @param Item $item
- *
- * @return bool
- */
- public function isSatisfiedBy(Item $item)
- {
- return $this->left->isSatisfiedBy($item) && $this->right->isSatisfiedBy($item);
- }
-}
diff --git a/Behavioral/Specification/PriceSpecification.php b/Behavioral/Specification/PriceSpecification.php
index 2019ad2..3b54586 100644
--- a/Behavioral/Specification/PriceSpecification.php
+++ b/Behavioral/Specification/PriceSpecification.php
@@ -2,47 +2,35 @@
namespace DesignPatterns\Behavioral\Specification;
-/**
- * A specification to check an Item is priced between min and max.
- */
-class PriceSpecification extends AbstractSpecification
+class PriceSpecification implements SpecificationInterface
{
- protected $maxPrice;
- protected $minPrice;
+ /**
+ * @var float|null
+ */
+ private $maxPrice;
/**
- * Sets the optional maximum price.
- *
- * @param int $maxPrice
+ * @var float|null
*/
- public function setMaxPrice($maxPrice)
+ private $minPrice;
+
+ /**
+ * @param float $minPrice
+ * @param float $maxPrice
+ */
+ public function __construct($minPrice, $maxPrice)
{
+ $this->minPrice = $minPrice;
$this->maxPrice = $maxPrice;
}
- /**
- * Sets the optional minimum price.
- *
- * @param int $minPrice
- */
- public function setMinPrice($minPrice)
+ public function isSatisfiedBy(Item $item): bool
{
- $this->minPrice = $minPrice;
- }
-
- /**
- * Checks if Item price falls between bounds.
- *
- * @param Item $item
- *
- * @return bool
- */
- public function isSatisfiedBy(Item $item)
- {
- if (!empty($this->maxPrice) && $item->getPrice() > $this->maxPrice) {
+ if ($this->maxPrice !== null && $item->getPrice() > $this->maxPrice) {
return false;
}
- if (!empty($this->minPrice) && $item->getPrice() < $this->minPrice) {
+
+ if ($this->minPrice !== null && $item->getPrice() < $this->minPrice) {
return false;
}
diff --git a/Behavioral/Specification/README.rst b/Behavioral/Specification/README.rst
index b6957e0..84d6913 100644
--- a/Behavioral/Specification/README.rst
+++ b/Behavioral/Specification/README.rst
@@ -38,15 +38,9 @@ SpecificationInterface.php
:language: php
:linenos:
-AbstractSpecification.php
+OrSpecification.php
-.. literalinclude:: AbstractSpecification.php
- :language: php
- :linenos:
-
-Either.php
-
-.. literalinclude:: Either.php
+.. literalinclude:: OrSpecification.php
:language: php
:linenos:
@@ -56,15 +50,15 @@ PriceSpecification.php
:language: php
:linenos:
-Plus.php
+AndSpecification.php
-.. literalinclude:: Plus.php
+.. literalinclude:: AndSpecification.php
:language: php
:linenos:
-Not.php
+NotSpecification.php
-.. literalinclude:: Not.php
+.. literalinclude:: NotSpecification.php
:language: php
:linenos:
diff --git a/Behavioral/Specification/SpecificationInterface.php b/Behavioral/Specification/SpecificationInterface.php
index 796af9f..7387700 100644
--- a/Behavioral/Specification/SpecificationInterface.php
+++ b/Behavioral/Specification/SpecificationInterface.php
@@ -2,36 +2,7 @@
namespace DesignPatterns\Behavioral\Specification;
-/**
- * An interface for a specification.
- */
interface SpecificationInterface
{
- /**
- * A boolean evaluation indicating if the object meets the specification.
- *
- * @param Item $item
- *
- * @return bool
- */
- public function isSatisfiedBy(Item $item);
-
- /**
- * Creates a logical AND specification.
- *
- * @param SpecificationInterface $spec
- */
- public function plus(SpecificationInterface $spec);
-
- /**
- * Creates a logical OR specification.
- *
- * @param SpecificationInterface $spec
- */
- public function either(SpecificationInterface $spec);
-
- /**
- * Creates a logical not specification.
- */
- public function not();
+ public function isSatisfiedBy(Item $item): bool;
}
diff --git a/Behavioral/Specification/Tests/SpecificationTest.php b/Behavioral/Specification/Tests/SpecificationTest.php
index 5abc82a..d7676a4 100644
--- a/Behavioral/Specification/Tests/SpecificationTest.php
+++ b/Behavioral/Specification/Tests/SpecificationTest.php
@@ -3,101 +3,44 @@
namespace DesignPatterns\Behavioral\Specification\Tests;
use DesignPatterns\Behavioral\Specification\Item;
+use DesignPatterns\Behavioral\Specification\NotSpecification;
+use DesignPatterns\Behavioral\Specification\OrSpecification;
+use DesignPatterns\Behavioral\Specification\AndSpecification;
use DesignPatterns\Behavioral\Specification\PriceSpecification;
-/**
- * SpecificationTest tests the specification pattern.
- */
class SpecificationTest extends \PHPUnit_Framework_TestCase
{
- public function testSimpleSpecification()
+ public function testCanOr()
{
- $item = new Item(100);
- $spec = new PriceSpecification();
+ $spec1 = new PriceSpecification(50, 99);
+ $spec2 = new PriceSpecification(101, 200);
- $this->assertTrue($spec->isSatisfiedBy($item));
+ $orSpec = new OrSpecification($spec1, $spec2);
- $spec->setMaxPrice(50);
- $this->assertFalse($spec->isSatisfiedBy($item));
-
- $spec->setMaxPrice(150);
- $this->assertTrue($spec->isSatisfiedBy($item));
-
- $spec->setMinPrice(101);
- $this->assertFalse($spec->isSatisfiedBy($item));
-
- $spec->setMinPrice(100);
- $this->assertTrue($spec->isSatisfiedBy($item));
+ $this->assertFalse($orSpec->isSatisfiedBy(new Item(100)));
+ $this->assertTrue($orSpec->isSatisfiedBy(new Item(51)));
+ $this->assertTrue($orSpec->isSatisfiedBy(new Item(150)));
}
- public function testNotSpecification()
+ public function testCanAnd()
{
- $item = new Item(100);
- $spec = new PriceSpecification();
- $not = $spec->not();
+ $spec1 = new PriceSpecification(50, 100);
+ $spec2 = new PriceSpecification(80, 200);
- $this->assertFalse($not->isSatisfiedBy($item));
+ $orSpec = new AndSpecification($spec1, $spec2);
- $spec->setMaxPrice(50);
- $this->assertTrue($not->isSatisfiedBy($item));
-
- $spec->setMaxPrice(150);
- $this->assertFalse($not->isSatisfiedBy($item));
-
- $spec->setMinPrice(101);
- $this->assertTrue($not->isSatisfiedBy($item));
-
- $spec->setMinPrice(100);
- $this->assertFalse($not->isSatisfiedBy($item));
+ $this->assertFalse($orSpec->isSatisfiedBy(new Item(150)));
+ $this->assertFalse($orSpec->isSatisfiedBy(new Item(1)));
+ $this->assertFalse($orSpec->isSatisfiedBy(new Item(51)));
+ $this->assertTrue($orSpec->isSatisfiedBy(new Item(100)));
}
- public function testPlusSpecification()
+ public function testCanNot()
{
- $spec1 = new PriceSpecification();
- $spec2 = new PriceSpecification();
- $plus = $spec1->plus($spec2);
+ $spec1 = new PriceSpecification(50, 100);
+ $orSpec = new NotSpecification($spec1);
- $item = new Item(100);
-
- $this->assertTrue($plus->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(150);
- $spec2->setMinPrice(50);
- $this->assertTrue($plus->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(150);
- $spec2->setMinPrice(101);
- $this->assertFalse($plus->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(99);
- $spec2->setMinPrice(50);
- $this->assertFalse($plus->isSatisfiedBy($item));
- }
-
- public function testEitherSpecification()
- {
- $spec1 = new PriceSpecification();
- $spec2 = new PriceSpecification();
- $either = $spec1->either($spec2);
-
- $item = new Item(100);
-
- $this->assertTrue($either->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(150);
- $spec2->setMaxPrice(150);
- $this->assertTrue($either->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(150);
- $spec2->setMaxPrice(0);
- $this->assertTrue($either->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(0);
- $spec2->setMaxPrice(150);
- $this->assertTrue($either->isSatisfiedBy($item));
-
- $spec1->setMaxPrice(99);
- $spec2->setMaxPrice(99);
- $this->assertFalse($either->isSatisfiedBy($item));
+ $this->assertTrue($orSpec->isSatisfiedBy(new Item(150)));
+ $this->assertFalse($orSpec->isSatisfiedBy(new Item(50)));
}
}
diff --git a/Behavioral/State/CreateOrder.php b/Behavioral/State/CreateOrder.php
index 7ac48d4..4a35ac3 100644
--- a/Behavioral/State/CreateOrder.php
+++ b/Behavioral/State/CreateOrder.php
@@ -2,49 +2,34 @@
namespace DesignPatterns\Behavioral\State;
-/**
- * Class CreateOrder.
- */
-class CreateOrder implements OrderInterface
+class CreateOrder implements Order
{
/**
* @var array
*/
- private $order;
+ private $details;
/**
- * @param array $order
- *
- * @throws \Exception
+ * @param array $details
*/
- public function __construct(array $order)
+ public function __construct(array $details)
{
- if (empty($order)) {
- throw new \Exception('Order can not be empty!');
- }
- $this->order = $order;
+ $this->details = $details;
}
- /**
- * @return mixed
- */
public function shipOrder()
{
- $this->order['status'] = 'shipping';
- $this->order['updatedTime'] = time();
-
- // Setting the new order status into database;
- return $this->updateOrder($this->order);
+ $this->details['status'] = 'shipping';
+ $this->details['updatedTime'] = time();
}
- /**
- * @throws \Exception
- *
- * @return mixed|void
- */
public function completeOrder()
{
- //Can not complete the order which status is created, throw exception;
- throw new \Exception('Can not complete the order which status is created!');
+ throw new \Exception('Can not complete the order which status is created');
+ }
+
+ public function getStatus(): string
+ {
+ return $this->details['status'];
}
}
diff --git a/Behavioral/State/OrderInterface.php b/Behavioral/State/Order.php
similarity index 77%
rename from Behavioral/State/OrderInterface.php
rename to Behavioral/State/Order.php
index 6a97e69..ea85159 100644
--- a/Behavioral/State/OrderInterface.php
+++ b/Behavioral/State/Order.php
@@ -2,10 +2,7 @@
namespace DesignPatterns\Behavioral\State;
-/**
- * Class OrderInterface.
- */
-interface OrderInterface
+interface Order
{
/**
* @return mixed
@@ -16,4 +13,6 @@ interface OrderInterface
* @return mixed
*/
public function completeOrder();
+
+ public function getStatus(): string;
}
diff --git a/Behavioral/State/OrderController.php b/Behavioral/State/OrderController.php
deleted file mode 100644
index 93ac542..0000000
--- a/Behavioral/State/OrderController.php
+++ /dev/null
@@ -1,37 +0,0 @@
-shipOrder();
- } catch (Exception $e) {
- //handle error!
- }
- // response to browser
- }
-
- /**
- * @param int $id
- */
- public function completeAction($id)
- {
- $order = OrderFactory::getOrder($id);
- try {
- $order->completeOrder();
- } catch (Exception $e) {
- //handle error!
- }
- // response to browser
- }
-}
diff --git a/Behavioral/State/OrderFactory.php b/Behavioral/State/OrderFactory.php
deleted file mode 100644
index 205d505..0000000
--- a/Behavioral/State/OrderFactory.php
+++ /dev/null
@@ -1,36 +0,0 @@
- ['status' => 'created'],
+ 2 => ['status' => 'shipping'],
+ 3 => ['status' => 'completed'],
+ ];
+
+ public static function findById(int $id): Order
+ {
+ if (!isset(self::$orders[$id])) {
+ throw new \InvalidArgumentException(sprintf('Order with id %d does not exist', $id));
+ }
+
+ $order = self::$orders[$id];
+
+ switch ($order['status']) {
+ case 'created':
+ return new CreateOrder($order);
+ case 'shipping':
+ return new ShippingOrder($order);
+ default:
+ throw new \InvalidArgumentException('Invalid order status given');
+ break;
+ }
+ }
+}
diff --git a/Behavioral/State/README.rst b/Behavioral/State/README.rst
index 3722d92..0aa3b9f 100644
--- a/Behavioral/State/README.rst
+++ b/Behavioral/State/README.rst
@@ -20,21 +20,15 @@ Code
You can also find these code on `GitHub`_
-OrderController.php
+OrderRepository.php
-.. literalinclude:: OrderController.php
+.. literalinclude:: OrderRepository.php
:language: php
:linenos:
-OrderFactory.php
+Order.php
-.. literalinclude:: OrderFactory.php
- :language: php
- :linenos:
-
-OrderInterface.php
-
-.. literalinclude:: OrderInterface.php
+.. literalinclude:: Order.php
:language: php
:linenos:
diff --git a/Behavioral/State/ShippingOrder.php b/Behavioral/State/ShippingOrder.php
index 943d714..41198b2 100644
--- a/Behavioral/State/ShippingOrder.php
+++ b/Behavioral/State/ShippingOrder.php
@@ -2,49 +2,34 @@
namespace DesignPatterns\Behavioral\State;
-/**
- * Class ShippingOrder.
- */
-class ShippingOrder implements OrderInterface
+class ShippingOrder implements Order
{
/**
* @var array
*/
- private $order;
+ private $details;
/**
- * @param array $order
- *
- * @throws \Exception
+ * @param array $details
*/
- public function __construct(array $order)
+ public function __construct(array $details)
{
- if (empty($order)) {
- throw new \Exception('Order can not be empty!');
- }
- $this->order = $order;
+ $this->details = $details;
}
- /**
- * @throws \Exception
- *
- * @return mixed|void
- */
public function shipOrder()
{
- //Can not ship the order which status is shipping, throw exception;
throw new \Exception('Can not ship the order which status is shipping!');
}
- /**
- * @return mixed
- */
public function completeOrder()
{
- $this->order['status'] = 'completed';
- $this->order['updatedTime'] = time();
+ $this->details['status'] = 'completed';
+ $this->details['updatedTime'] = time();
+ }
- // Setting the new order status into database;
- return $this->updateOrder($this->order);
+ public function getStatus(): string
+ {
+ return $this->details['status'];
}
}
diff --git a/Behavioral/State/Tests/StateTest.php b/Behavioral/State/Tests/StateTest.php
new file mode 100644
index 0000000..637ad9a
--- /dev/null
+++ b/Behavioral/State/Tests/StateTest.php
@@ -0,0 +1,33 @@
+findById(1);
+ $order->shipOrder();
+
+ $this->assertEquals('shipping', $order->getStatus());
+ }
+
+ public function testCanCompleteShippedOrder()
+ {
+ $order = (new OrderRepository())->findById(2);
+ $order->completeOrder();
+
+ $this->assertEquals('completed', $order->getStatus());
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function testThrowsExceptionWhenTryingToCompleteCreatedOrder()
+ {
+ $order = (new OrderRepository())->findById(1);
+ $order->completeOrder();
+ }
+}
diff --git a/Behavioral/Strategy/ComparatorInterface.php b/Behavioral/Strategy/ComparatorInterface.php
index f472fd9..4c5dcb2 100644
--- a/Behavioral/Strategy/ComparatorInterface.php
+++ b/Behavioral/Strategy/ComparatorInterface.php
@@ -2,16 +2,13 @@
namespace DesignPatterns\Behavioral\Strategy;
-/**
- * Class ComparatorInterface.
- */
interface ComparatorInterface
{
/**
* @param mixed $a
* @param mixed $b
*
- * @return bool
+ * @return int
*/
- public function compare($a, $b);
+ public function compare($a, $b): int;
}
diff --git a/Behavioral/Strategy/DateComparator.php b/Behavioral/Strategy/DateComparator.php
index d355dc9..3f59667 100644
--- a/Behavioral/Strategy/DateComparator.php
+++ b/Behavioral/Strategy/DateComparator.php
@@ -2,23 +2,19 @@
namespace DesignPatterns\Behavioral\Strategy;
-/**
- * Class DateComparator.
- */
class DateComparator implements ComparatorInterface
{
/**
- * {@inheritdoc}
+ * @param mixed $a
+ * @param mixed $b
+ *
+ * @return int
*/
- public function compare($a, $b)
+ public function compare($a, $b): int
{
$aDate = new \DateTime($a['date']);
$bDate = new \DateTime($b['date']);
- if ($aDate == $bDate) {
- return 0;
- }
-
- return $aDate < $bDate ? -1 : 1;
+ return $aDate <=> $bDate;
}
}
diff --git a/Behavioral/Strategy/IdComparator.php b/Behavioral/Strategy/IdComparator.php
index f829195..80c43cc 100644
--- a/Behavioral/Strategy/IdComparator.php
+++ b/Behavioral/Strategy/IdComparator.php
@@ -2,20 +2,16 @@
namespace DesignPatterns\Behavioral\Strategy;
-/**
- * Class IdComparator.
- */
class IdComparator implements ComparatorInterface
{
/**
- * {@inheritdoc}
+ * @param mixed $a
+ * @param mixed $b
+ *
+ * @return int
*/
- public function compare($a, $b)
+ public function compare($a, $b): int
{
- if ($a['id'] == $b['id']) {
- return 0;
- } else {
- return $a['id'] < $b['id'] ? -1 : 1;
- }
+ return $a['id'] <=> $b['id'];
}
}
diff --git a/Behavioral/Strategy/ObjectCollection.php b/Behavioral/Strategy/ObjectCollection.php
index b5c1bd2..ca8165b 100644
--- a/Behavioral/Strategy/ObjectCollection.php
+++ b/Behavioral/Strategy/ObjectCollection.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Behavioral\Strategy;
-/**
- * Class ObjectCollection.
- */
class ObjectCollection
{
/**
@@ -20,30 +17,24 @@ class ObjectCollection
/**
* @param array $elements
*/
- public function __construct(array $elements = array())
+ public function __construct(array $elements = [])
{
$this->elements = $elements;
}
- /**
- * @return array
- */
- public function sort()
+ public function sort(): array
{
if (!$this->comparator) {
throw new \LogicException('Comparator is not set');
}
- $callback = array($this->comparator, 'compare');
- uasort($this->elements, $callback);
+ uasort($this->elements, [$this->comparator, 'compare']);
return $this->elements;
}
/**
* @param ComparatorInterface $comparator
- *
- * @return void
*/
public function setComparator(ComparatorInterface $comparator)
{
diff --git a/Behavioral/Strategy/Tests/StrategyTest.php b/Behavioral/Strategy/Tests/StrategyTest.php
index 1911ba4..033a4a9 100644
--- a/Behavioral/Strategy/Tests/StrategyTest.php
+++ b/Behavioral/Strategy/Tests/StrategyTest.php
@@ -5,43 +5,42 @@ namespace DesignPatterns\Behavioral\Strategy\Tests;
use DesignPatterns\Behavioral\Strategy\DateComparator;
use DesignPatterns\Behavioral\Strategy\IdComparator;
use DesignPatterns\Behavioral\Strategy\ObjectCollection;
-use DesignPatterns\Behavioral\Strategy\Strategy;
-/**
- * Tests for Strategy pattern.
- */
class StrategyTest extends \PHPUnit_Framework_TestCase
{
- public function getIdCollection()
+ public function provideIntegers()
{
- return array(
- array(
- array(array('id' => 2), array('id' => 1), array('id' => 3)),
- array('id' => 1),
- ),
- array(
- array(array('id' => 3), array('id' => 2), array('id' => 1)),
- array('id' => 1),
- ),
- );
+ return [
+ [
+ [['id' => 2], ['id' => 1], ['id' => 3]],
+ ['id' => 1],
+ ],
+ [
+ [['id' => 3], ['id' => 2], ['id' => 1]],
+ ['id' => 1],
+ ],
+ ];
}
- public function getDateCollection()
+ public function providateDates()
{
- return array(
- array(
- array(array('date' => '2014-03-03'), array('date' => '2015-03-02'), array('date' => '2013-03-01')),
- array('date' => '2013-03-01'),
- ),
- array(
- array(array('date' => '2014-02-03'), array('date' => '2013-02-01'), array('date' => '2015-02-02')),
- array('date' => '2013-02-01'),
- ),
- );
+ return [
+ [
+ [['date' => '2014-03-03'], ['date' => '2015-03-02'], ['date' => '2013-03-01']],
+ ['date' => '2013-03-01'],
+ ],
+ [
+ [['date' => '2014-02-03'], ['date' => '2013-02-01'], ['date' => '2015-02-02']],
+ ['date' => '2013-02-01'],
+ ],
+ ];
}
/**
- * @dataProvider getIdCollection
+ * @dataProvider provideIntegers
+ *
+ * @param array $collection
+ * @param array $expected
*/
public function testIdComparator($collection, $expected)
{
@@ -54,7 +53,10 @@ class StrategyTest extends \PHPUnit_Framework_TestCase
}
/**
- * @dataProvider getDateCollection
+ * @dataProvider providateDates
+ *
+ * @param array $collection
+ * @param array $expected
*/
public function testDateComparator($collection, $expected)
{
diff --git a/Behavioral/TemplateMethod/BeachJourney.php b/Behavioral/TemplateMethod/BeachJourney.php
index d19845c..5007054 100644
--- a/Behavioral/TemplateMethod/BeachJourney.php
+++ b/Behavioral/TemplateMethod/BeachJourney.php
@@ -2,16 +2,10 @@
namespace DesignPatterns\Behavioral\TemplateMethod;
-/**
- * BeachJourney is vacation at the beach.
- */
class BeachJourney extends Journey
{
- /**
- * prints what to do to enjoy your vacation.
- */
- protected function enjoyVacation()
+ protected function enjoyVacation(): string
{
- echo "Swimming and sun-bathing\n";
+ return "Swimming and sun-bathing";
}
}
diff --git a/Behavioral/TemplateMethod/CityJourney.php b/Behavioral/TemplateMethod/CityJourney.php
index 3f36b61..f1f9335 100644
--- a/Behavioral/TemplateMethod/CityJourney.php
+++ b/Behavioral/TemplateMethod/CityJourney.php
@@ -2,16 +2,15 @@
namespace DesignPatterns\Behavioral\TemplateMethod;
-/**
- * CityJourney is a journey in a city.
- */
class CityJourney extends Journey
{
- /**
- * prints what to do in your journey to enjoy vacation.
- */
- protected function enjoyVacation()
+ protected function enjoyVacation(): string
{
- echo "Eat, drink, take photos and sleep\n";
+ return "Eat, drink, take photos and sleep";
+ }
+
+ protected function buyGift(): string
+ {
+ return "Buy a gift";
}
}
diff --git a/Behavioral/TemplateMethod/Journey.php b/Behavioral/TemplateMethod/Journey.php
index c7a6809..0a1407c 100644
--- a/Behavioral/TemplateMethod/Journey.php
+++ b/Behavioral/TemplateMethod/Journey.php
@@ -4,6 +4,11 @@ namespace DesignPatterns\Behavioral\TemplateMethod;
abstract class Journey
{
+ /**
+ * @var string[]
+ */
+ private $thingsToDo = [];
+
/**
* This is the public service provided by this class and its subclasses.
* Notice it is final to "freeze" the global behavior of algorithm.
@@ -12,46 +17,49 @@ abstract class Journey
*/
final public function takeATrip()
{
- $this->buyAFlight();
- $this->takePlane();
- $this->enjoyVacation();
- $this->buyGift();
- $this->takePlane();
+ $this->thingsToDo[] = $this->buyAFlight();
+ $this->thingsToDo[] = $this->takePlane();
+ $this->thingsToDo[] = $this->enjoyVacation();
+ $buyGift = $this->buyGift();
+
+ if ($buyGift !== null) {
+ $this->thingsToDo[] = $buyGift;
+ }
+
+ $this->thingsToDo[] = $this->takePlane();
}
/**
* This method must be implemented, this is the key-feature of this pattern.
*/
- abstract protected function enjoyVacation();
+ abstract protected function enjoyVacation(): string;
/**
* This method is also part of the algorithm but it is optional.
- * This is an "adapter" (do not confuse with the Adapter pattern, not related)
- * You can override it only if you need to.
+ * You can override it only if you need to
+ *
+ * @return null|string
*/
protected function buyGift()
{
+ return null;
+ }
+
+ private function buyAFlight(): string
+ {
+ return 'Buy a flight ticket';
+ }
+
+ private function takePlane(): string
+ {
+ return 'Taking the plane';
}
/**
- * This method will be unknown by subclasses (better).
+ * @return string[]
*/
- private function buyAFlight()
+ public function getThingsToDo(): array
{
- echo "Buying a flight\n";
+ return $this->thingsToDo;
}
-
- /**
- * Subclasses will get access to this method but cannot override it and
- * compromise this algorithm (warning : cause of cyclic dependencies).
- */
- final protected function takePlane()
- {
- echo "Taking the plane\n";
- }
-
- // A note regarding the keyword "final" : don't use it when you start coding :
- // add it after you narrow and know exactly what change and what remain unchanged
- // in this algorithm.
- // [abstract] x [3 access] x [final] = 12 combinations, it can be hard !
}
diff --git a/Behavioral/TemplateMethod/Tests/JourneyTest.php b/Behavioral/TemplateMethod/Tests/JourneyTest.php
index 82acef3..26c1763 100644
--- a/Behavioral/TemplateMethod/Tests/JourneyTest.php
+++ b/Behavioral/TemplateMethod/Tests/JourneyTest.php
@@ -4,40 +4,33 @@ namespace DesignPatterns\Behavioral\TemplateMethod\Tests;
use DesignPatterns\Behavioral\TemplateMethod;
-/**
- * JourneyTest tests all journeys.
- */
class JourneyTest extends \PHPUnit_Framework_TestCase
{
- public function testBeach()
+ public function testCanGetOnVacationOnTheBeach()
{
- $journey = new TemplateMethod\BeachJourney();
- $this->expectOutputRegex('#sun-bathing#');
- $journey->takeATrip();
+ $beachJourney = new TemplateMethod\BeachJourney();
+ $beachJourney->takeATrip();
+
+ $this->assertEquals(
+ ['Buy a flight ticket', 'Taking the plane', 'Swimming and sun-bathing', 'Taking the plane'],
+ $beachJourney->getThingsToDo()
+ );
}
- public function testCity()
+ public function testCanGetOnAJourneyToACity()
{
- $journey = new TemplateMethod\CityJourney();
- $this->expectOutputRegex('#drink#');
- $journey->takeATrip();
- }
+ $beachJourney = new TemplateMethod\CityJourney();
+ $beachJourney->takeATrip();
- /**
- * How to test an abstract template method with PHPUnit.
- */
- public function testLasVegas()
- {
- $journey = $this->getMockForAbstractClass('DesignPatterns\Behavioral\TemplateMethod\Journey');
- $journey->expects($this->once())
- ->method('enjoyVacation')
- ->will($this->returnCallback(array($this, 'mockUpVacation')));
- $this->expectOutputRegex('#Las Vegas#');
- $journey->takeATrip();
- }
-
- public function mockUpVacation()
- {
- echo "Fear and loathing in Las Vegas\n";
+ $this->assertEquals(
+ [
+ 'Buy a flight ticket',
+ 'Taking the plane',
+ 'Eat, drink, take photos and sleep',
+ 'Buy a gift',
+ 'Taking the plane'
+ ],
+ $beachJourney->getThingsToDo()
+ );
}
}
diff --git a/Behavioral/TemplateMethod/uml/TemplateMethod.uml b/Behavioral/TemplateMethod/uml/TemplateMethod.uml
index 063b022..29d6c04 100644
--- a/Behavioral/TemplateMethod/uml/TemplateMethod.uml
+++ b/Behavioral/TemplateMethod/uml/TemplateMethod.uml
@@ -1,35 +1,35 @@
-
-
- PHP
- \DesignPatterns\Behavioral\TemplateMethod\BeachJourney
-
- \DesignPatterns\Behavioral\TemplateMethod\CityJourney
- \DesignPatterns\Behavioral\TemplateMethod\Journey
- \DesignPatterns\Behavioral\TemplateMethod\BeachJourney
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Behavioral\TemplateMethod\BeachJourney
+
+ \DesignPatterns\Behavioral\TemplateMethod\BeachJourney
+ \DesignPatterns\Behavioral\TemplateMethod\Journey
+ \DesignPatterns\Behavioral\TemplateMethod\CityJourney
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fields
+ Constants
+ Constructors
+ Methods
+
+ private
+
+
diff --git a/Behavioral/TemplateMethod/uml/uml.png b/Behavioral/TemplateMethod/uml/uml.png
index c557e8f..d8e9f69 100644
Binary files a/Behavioral/TemplateMethod/uml/uml.png and b/Behavioral/TemplateMethod/uml/uml.png differ
diff --git a/Behavioral/TemplateMethod/uml/uml.svg b/Behavioral/TemplateMethod/uml/uml.svg
index d06adcb..6f7f555 100644
--- a/Behavioral/TemplateMethod/uml/uml.svg
+++ b/Behavioral/TemplateMethod/uml/uml.svg
@@ -1,256 +1,636 @@
-
+
diff --git a/Behavioral/Visitor/Group.php b/Behavioral/Visitor/Group.php
index b2c9d90..e4a9a65 100644
--- a/Behavioral/Visitor/Group.php
+++ b/Behavioral/Visitor/Group.php
@@ -2,29 +2,25 @@
namespace DesignPatterns\Behavioral\Visitor;
-/**
- * An example of a Visitor: Group.
- */
-class Group extends Role
+class Group implements Role
{
/**
* @var string
*/
- protected $name;
+ private $name;
- /**
- * @param string $name
- */
- public function __construct($name)
+ public function __construct(string $name)
{
- $this->name = (string) $name;
+ $this->name = $name;
}
- /**
- * @return string
- */
- public function getName()
+ public function getName(): string
{
- return 'Group: '.$this->name;
+ return sprintf('Group: %s', $this->name);
+ }
+
+ public function accept(RoleVisitorInterface $visitor)
+ {
+ $visitor->visitGroup($this);
}
}
diff --git a/Behavioral/Visitor/README.rst b/Behavioral/Visitor/README.rst
index ba256b9..7484356 100644
--- a/Behavioral/Visitor/README.rst
+++ b/Behavioral/Visitor/README.rst
@@ -31,9 +31,9 @@ RoleVisitorInterface.php
:language: php
:linenos:
-RolePrintVisitor.php
+RoleVisitor.php
-.. literalinclude:: RolePrintVisitor.php
+.. literalinclude:: RoleVisitor.php
:language: php
:linenos:
diff --git a/Behavioral/Visitor/Role.php b/Behavioral/Visitor/Role.php
index 011a0fb..1a55c74 100644
--- a/Behavioral/Visitor/Role.php
+++ b/Behavioral/Visitor/Role.php
@@ -2,32 +2,7 @@
namespace DesignPatterns\Behavioral\Visitor;
-/**
- * class Role.
- */
-abstract class Role
+interface Role
{
- /**
- * This method handles a double dispatch based on the short name of the Visitor.
- *
- * Feel free to override it if your object must call another visiting behavior
- *
- * @param \DesignPatterns\Behavioral\Visitor\RoleVisitorInterface $visitor
- *
- * @throws \InvalidArgumentException
- */
- public function accept(RoleVisitorInterface $visitor)
- {
- // this trick to simulate double-dispatch based on type-hinting
- $klass = get_called_class();
- preg_match('#([^\\\\]+)$#', $klass, $extract);
- $visitingMethod = 'visit'.$extract[1];
-
- // this ensures strong typing with visitor interface, not some visitor objects
- if (!method_exists(__NAMESPACE__.'\RoleVisitorInterface', $visitingMethod)) {
- throw new \InvalidArgumentException("The visitor you provide cannot visit a $klass instance");
- }
-
- call_user_func(array($visitor, $visitingMethod), $this);
- }
+ public function accept(RoleVisitorInterface $visitor);
}
diff --git a/Behavioral/Visitor/RolePrintVisitor.php b/Behavioral/Visitor/RolePrintVisitor.php
deleted file mode 100644
index 49777cf..0000000
--- a/Behavioral/Visitor/RolePrintVisitor.php
+++ /dev/null
@@ -1,27 +0,0 @@
-getName();
- }
-
- /**
- * {@inheritdoc}
- */
- public function visitUser(User $role)
- {
- echo 'Role: '.$role->getName();
- }
-}
diff --git a/Behavioral/Visitor/RoleVisitor.php b/Behavioral/Visitor/RoleVisitor.php
new file mode 100644
index 0000000..fc368b2
--- /dev/null
+++ b/Behavioral/Visitor/RoleVisitor.php
@@ -0,0 +1,29 @@
+visited[] = $role;
+ }
+
+ public function visitUser(User $role)
+ {
+ $this->visited[] = $role;
+ }
+
+ /**
+ * @return Role[]
+ */
+ public function getVisited(): array
+ {
+ return $this->visited;
+ }
+}
diff --git a/Behavioral/Visitor/RoleVisitorInterface.php b/Behavioral/Visitor/RoleVisitorInterface.php
index e8ba0ec..b2aff5e 100644
--- a/Behavioral/Visitor/RoleVisitorInterface.php
+++ b/Behavioral/Visitor/RoleVisitorInterface.php
@@ -3,29 +3,12 @@
namespace DesignPatterns\Behavioral\Visitor;
/**
- * Visitor Pattern.
- *
- * The contract for the visitor.
- *
- * Note 1 : in C++ or Java, with method polymorphism based on type-hint, there are many
- * methods visit() with different type for the 'role' parameter.
- *
- * Note 2 : the visitor must not choose itself which method to
- * invoke, it is the Visitee that make this decision.
+ * Note: the visitor must not choose itself which method to
+ * invoke, it is the Visitee that make this decision
*/
interface RoleVisitorInterface
{
- /**
- * Visit a User object.
- *
- * @param \DesignPatterns\Behavioral\Visitor\User $role
- */
public function visitUser(User $role);
- /**
- * Visit a Group object.
- *
- * @param \DesignPatterns\Behavioral\Visitor\Group $role
- */
public function visitGroup(Group $role);
}
diff --git a/Behavioral/Visitor/Tests/VisitorTest.php b/Behavioral/Visitor/Tests/VisitorTest.php
index f3b5bc7..ce6480b 100644
--- a/Behavioral/Visitor/Tests/VisitorTest.php
+++ b/Behavioral/Visitor/Tests/VisitorTest.php
@@ -4,42 +4,34 @@ namespace DesignPatterns\Tests\Visitor\Tests;
use DesignPatterns\Behavioral\Visitor;
-/**
- * VisitorTest tests the visitor pattern.
- */
class VisitorTest extends \PHPUnit_Framework_TestCase
{
- protected $visitor;
+ /**
+ * @var Visitor\RoleVisitor
+ */
+ private $visitor;
protected function setUp()
{
- $this->visitor = new Visitor\RolePrintVisitor();
+ $this->visitor = new Visitor\RoleVisitor();
}
- public function getRole()
+ public function provideRoles()
{
- return array(
- array(new Visitor\User('Dominik'), 'Role: User Dominik'),
- array(new Visitor\Group('Administrators'), 'Role: Group: Administrators'),
- );
+ return [
+ [new Visitor\User('Dominik')],
+ [new Visitor\Group('Administrators')],
+ ];
}
/**
- * @dataProvider getRole
+ * @dataProvider provideRoles
+ *
+ * @param Visitor\Role $role
*/
- public function testVisitSomeRole(Visitor\Role $role, $expect)
+ public function testVisitSomeRole(Visitor\Role $role)
{
- $this->expectOutputString($expect);
$role->accept($this->visitor);
- }
-
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage Mock
- */
- public function testUnknownObject()
- {
- $mock = $this->getMockForAbstractClass('DesignPatterns\Behavioral\Visitor\Role');
- $mock->accept($this->visitor);
+ $this->assertSame($role, $this->visitor->getVisited()[0]);
}
}
diff --git a/Behavioral/Visitor/User.php b/Behavioral/Visitor/User.php
index 5a95fbe..f3d9ad3 100644
--- a/Behavioral/Visitor/User.php
+++ b/Behavioral/Visitor/User.php
@@ -2,31 +2,25 @@
namespace DesignPatterns\Behavioral\Visitor;
-/**
- * Visitor Pattern.
- *
- * One example for a visitee. Each visitee has to extends Role
- */
-class User extends Role
+class User implements Role
{
/**
* @var string
*/
- protected $name;
+ private $name;
- /**
- * @param string $name
- */
- public function __construct($name)
+ public function __construct(string $name)
{
- $this->name = (string) $name;
+ $this->name = $name;
}
- /**
- * @return string
- */
- public function getName()
+ public function getName(): string
{
- return 'User '.$this->name;
+ return sprintf('User %s', $this->name);
+ }
+
+ public function accept(RoleVisitorInterface $visitor)
+ {
+ $visitor->visitUser($this);
}
}
diff --git a/Creational/AbstractFactory/AbstractFactory.php b/Creational/AbstractFactory/AbstractFactory.php
index fd69448..85bd878 100644
--- a/Creational/AbstractFactory/AbstractFactory.php
+++ b/Creational/AbstractFactory/AbstractFactory.php
@@ -3,38 +3,10 @@
namespace DesignPatterns\Creational\AbstractFactory;
/**
- * class AbstractFactory.
- *
- * Sometimes also known as "Kit" in a GUI libraries.
- *
- * This design pattern implements the Dependency Inversion Principle since
- * it is the concrete subclass which creates concrete components.
- *
* In this case, the abstract factory is a contract for creating some components
- * for the web. There are two components : Text and Picture. There are two ways
- * of rendering : HTML or JSON.
- *
- * Therefore 4 concrete classes, but the client just needs to know this contract
- * to build a correct HTTP response (for a HTML page or for an AJAX request).
+ * for the web. There are two ways of rendering text: HTML and JSON
*/
abstract class AbstractFactory
{
- /**
- * Creates a text component.
- *
- * @param string $content
- *
- * @return Text
- */
- abstract public function createText($content);
-
- /**
- * Creates a picture component.
- *
- * @param string $path
- * @param string $name
- *
- * @return Picture
- */
- abstract public function createPicture($path, $name = '');
+ abstract public function createText(string $content): Text;
}
diff --git a/Creational/AbstractFactory/Html/Picture.php b/Creational/AbstractFactory/Html/Picture.php
deleted file mode 100644
index 3abacb4..0000000
--- a/Creational/AbstractFactory/Html/Picture.php
+++ /dev/null
@@ -1,23 +0,0 @@
-', $this->path, $this->name);
- }
-}
diff --git a/Creational/AbstractFactory/Html/Text.php b/Creational/AbstractFactory/Html/Text.php
deleted file mode 100644
index 8c33da6..0000000
--- a/Creational/AbstractFactory/Html/Text.php
+++ /dev/null
@@ -1,23 +0,0 @@
-'.htmlspecialchars($this->text).'';
- }
-}
diff --git a/Creational/AbstractFactory/HtmlFactory.php b/Creational/AbstractFactory/HtmlFactory.php
index 5c22859..ba44216 100644
--- a/Creational/AbstractFactory/HtmlFactory.php
+++ b/Creational/AbstractFactory/HtmlFactory.php
@@ -2,35 +2,10 @@
namespace DesignPatterns\Creational\AbstractFactory;
-/**
- * Class HtmlFactory.
- *
- * HtmlFactory is a concrete factory for HTML component
- */
class HtmlFactory extends AbstractFactory
{
- /**
- * Creates a picture component.
- *
- * @param string $path
- * @param string $name
- *
- * @return Html\Picture|Picture
- */
- public function createPicture($path, $name = '')
+ public function createText(string $content): Text
{
- return new Html\Picture($path, $name);
- }
-
- /**
- * Creates a text component.
- *
- * @param string $content
- *
- * @return Html\Text|Text
- */
- public function createText($content)
- {
- return new Html\Text($content);
+ return new HtmlText($content);
}
}
diff --git a/Creational/AbstractFactory/HtmlText.php b/Creational/AbstractFactory/HtmlText.php
new file mode 100644
index 0000000..a3764da
--- /dev/null
+++ b/Creational/AbstractFactory/HtmlText.php
@@ -0,0 +1,8 @@
+ $this->name, 'path' => $this->path));
- }
-}
diff --git a/Creational/AbstractFactory/Json/Text.php b/Creational/AbstractFactory/Json/Text.php
deleted file mode 100644
index 4c51785..0000000
--- a/Creational/AbstractFactory/Json/Text.php
+++ /dev/null
@@ -1,23 +0,0 @@
- $this->text));
- }
-}
diff --git a/Creational/AbstractFactory/JsonFactory.php b/Creational/AbstractFactory/JsonFactory.php
index 63a9979..a767f7e 100644
--- a/Creational/AbstractFactory/JsonFactory.php
+++ b/Creational/AbstractFactory/JsonFactory.php
@@ -2,36 +2,10 @@
namespace DesignPatterns\Creational\AbstractFactory;
-/**
- * Class JsonFactory.
- *
- * JsonFactory is a factory for creating a family of JSON component
- * (example for ajax)
- */
class JsonFactory extends AbstractFactory
{
- /**
- * Creates a picture component.
- *
- * @param string $path
- * @param string $name
- *
- * @return Json\Picture|Picture
- */
- public function createPicture($path, $name = '')
+ public function createText(string $content): Text
{
- return new Json\Picture($path, $name);
- }
-
- /**
- * Creates a text component.
- *
- * @param string $content
- *
- * @return Json\Text|Text
- */
- public function createText($content)
- {
- return new Json\Text($content);
+ return new JsonText($content);
}
}
diff --git a/Creational/AbstractFactory/JsonText.php b/Creational/AbstractFactory/JsonText.php
new file mode 100644
index 0000000..a0386a0
--- /dev/null
+++ b/Creational/AbstractFactory/JsonText.php
@@ -0,0 +1,8 @@
+name = (string) $name;
- $this->path = (string) $path;
- }
-}
diff --git a/Creational/AbstractFactory/README.rst b/Creational/AbstractFactory/README.rst
index 69980eb..5251b6c 100644
--- a/Creational/AbstractFactory/README.rst
+++ b/Creational/AbstractFactory/README.rst
@@ -39,45 +39,21 @@ HtmlFactory.php
:language: php
:linenos:
-MediaInterface.php
-
-.. literalinclude:: MediaInterface.php
- :language: php
- :linenos:
-
-Picture.php
-
-.. literalinclude:: Picture.php
- :language: php
- :linenos:
-
Text.php
.. literalinclude:: Text.php
:language: php
:linenos:
-Json/Picture.php
+JsonText.php
-.. literalinclude:: Json/Picture.php
+.. literalinclude:: JsonText.php
:language: php
:linenos:
-Json/Text.php
+HtmlText.php
-.. literalinclude:: Json/Text.php
- :language: php
- :linenos:
-
-Html/Picture.php
-
-.. literalinclude:: Html/Picture.php
- :language: php
- :linenos:
-
-Html/Text.php
-
-.. literalinclude:: Html/Text.php
+.. literalinclude:: HtmlText.php
:language: php
:linenos:
diff --git a/Creational/AbstractFactory/Tests/AbstractFactoryTest.php b/Creational/AbstractFactory/Tests/AbstractFactoryTest.php
index 97f4417..1bdbd1c 100644
--- a/Creational/AbstractFactory/Tests/AbstractFactoryTest.php
+++ b/Creational/AbstractFactory/Tests/AbstractFactoryTest.php
@@ -2,43 +2,24 @@
namespace DesignPatterns\Creational\AbstractFactory\Tests;
-use DesignPatterns\Creational\AbstractFactory\AbstractFactory;
use DesignPatterns\Creational\AbstractFactory\HtmlFactory;
use DesignPatterns\Creational\AbstractFactory\JsonFactory;
-/**
- * AbstractFactoryTest tests concrete factories.
- */
class AbstractFactoryTest extends \PHPUnit_Framework_TestCase
{
- public function getFactories()
+ public function testCanCreateHtmlText()
{
- return array(
- array(new JsonFactory()),
- array(new HtmlFactory()),
- );
+ $factory = new HtmlFactory();
+ $text = $factory->createText('foobar');
+
+ $this->assertInstanceOf('DesignPatterns\Creational\AbstractFactory\HtmlText', $text);
}
- /**
- * This is the client of factories. Note that the client does not
- * care which factory is given to him, he can create any component he
- * wants and render how he wants.
- *
- * @dataProvider getFactories
- */
- public function testComponentCreation(AbstractFactory $factory)
+ public function testCanCreateJsonText()
{
- $article = array(
- $factory->createText('Lorem Ipsum'),
- $factory->createPicture('/image.jpg', 'caption'),
- $factory->createText('footnotes'),
- );
+ $factory = new JsonFactory();
+ $text = $factory->createText('foobar');
- $this->assertContainsOnly('DesignPatterns\Creational\AbstractFactory\MediaInterface', $article);
-
- /* this is the time to look at the Builder pattern. This pattern
- * helps you to create complex object like that article above with
- * a given Abstract Factory
- */
+ $this->assertInstanceOf('DesignPatterns\Creational\AbstractFactory\JsonText', $text);
}
}
diff --git a/Creational/AbstractFactory/Text.php b/Creational/AbstractFactory/Text.php
index 30984f3..60846bb 100644
--- a/Creational/AbstractFactory/Text.php
+++ b/Creational/AbstractFactory/Text.php
@@ -2,21 +2,15 @@
namespace DesignPatterns\Creational\AbstractFactory;
-/**
- * Class Text.
- */
-abstract class Text implements MediaInterface
+abstract class Text
{
/**
* @var string
*/
- protected $text;
+ private $text;
- /**
- * @param string $text
- */
- public function __construct($text)
+ public function __construct(string $text)
{
- $this->text = (string) $text;
+ $this->text = $text;
}
}
diff --git a/Creational/AbstractFactory/uml/AbstractFactory.uml b/Creational/AbstractFactory/uml/AbstractFactory.uml
index a44e64c..224cc06 100644
--- a/Creational/AbstractFactory/uml/AbstractFactory.uml
+++ b/Creational/AbstractFactory/uml/AbstractFactory.uml
@@ -1,38 +1,51 @@
-
-
- PHP
- \DesignPatterns\Creational\AbstractFactory\AbstractFactory
-
- \DesignPatterns\Creational\AbstractFactory\Html\Text
- \DesignPatterns\Creational\AbstractFactory\Html\Picture
- \DesignPatterns\Creational\AbstractFactory\HtmlFactory
- \DesignPatterns\Creational\AbstractFactory\JsonFactory
- \DesignPatterns\Creational\AbstractFactory\AbstractFactory
- \DesignPatterns\Creational\AbstractFactory\MediaInterface
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Creational\AbstractFactory\AbstractFactory
+
+ \DesignPatterns\Creational\AbstractFactory\JsonFactory
+ \DesignPatterns\Creational\AbstractFactory\AbstractFactory
+ \DesignPatterns\Creational\AbstractFactory\HtmlFactory
+ \DesignPatterns\Creational\AbstractFactory\JsonText
+ \DesignPatterns\Creational\AbstractFactory\HtmlText
+ \DesignPatterns\Creational\AbstractFactory\Text
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ \DesignPatterns\Creational\AbstractFactory\AbstractFactory
+
+
+ Methods
+ Constants
+ Fields
+
+ private
+
+
diff --git a/Creational/AbstractFactory/uml/uml.png b/Creational/AbstractFactory/uml/uml.png
index 6de2767..68d3a8b 100644
Binary files a/Creational/AbstractFactory/uml/uml.png and b/Creational/AbstractFactory/uml/uml.png differ
diff --git a/Creational/AbstractFactory/uml/uml.svg b/Creational/AbstractFactory/uml/uml.svg
index 17b4b70..e7eff24 100644
--- a/Creational/AbstractFactory/uml/uml.svg
+++ b/Creational/AbstractFactory/uml/uml.svg
@@ -1,379 +1,560 @@
-
+
diff --git a/Creational/Builder/BikeBuilder.php b/Creational/Builder/BikeBuilder.php
deleted file mode 100644
index f83c5db..0000000
--- a/Creational/Builder/BikeBuilder.php
+++ /dev/null
@@ -1,54 +0,0 @@
-bike->setPart('engine', new Parts\Engine());
- }
-
- /**
- * {@inheritdoc}
- */
- public function addWheel()
- {
- $this->bike->setPart('forwardWheel', new Parts\Wheel());
- $this->bike->setPart('rearWheel', new Parts\Wheel());
- }
-
- /**
- * {@inheritdoc}
- */
- public function createVehicle()
- {
- $this->bike = new Parts\Bike();
- }
-
- /**
- * {@inheritdoc}
- */
- public function getVehicle()
- {
- return $this->bike;
- }
-}
diff --git a/Creational/Builder/BuilderInterface.php b/Creational/Builder/BuilderInterface.php
index 563162f..3bcf961 100644
--- a/Creational/Builder/BuilderInterface.php
+++ b/Creational/Builder/BuilderInterface.php
@@ -2,30 +2,17 @@
namespace DesignPatterns\Creational\Builder;
+use DesignPatterns\Creational\Builder\Parts\Vehicle;
+
interface BuilderInterface
{
- /**
- * @return mixed
- */
public function createVehicle();
- /**
- * @return mixed
- */
public function addWheel();
- /**
- * @return mixed
- */
public function addEngine();
- /**
- * @return mixed
- */
public function addDoors();
- /**
- * @return mixed
- */
- public function getVehicle();
+ public function getVehicle(): Vehicle;
}
diff --git a/Creational/Builder/CarBuilder.php b/Creational/Builder/CarBuilder.php
index a0693d0..fb20822 100644
--- a/Creational/Builder/CarBuilder.php
+++ b/Creational/Builder/CarBuilder.php
@@ -2,36 +2,27 @@
namespace DesignPatterns\Creational\Builder;
-/**
- * CarBuilder builds car.
- */
+use DesignPatterns\Creational\Builder\Parts\Vehicle;
+
class CarBuilder implements BuilderInterface
{
/**
* @var Parts\Car
*/
- protected $car;
+ private $car;
- /**
- * @return void
- */
public function addDoors()
{
- $this->car->setPart('rightdoor', new Parts\Door());
+ $this->car->setPart('rightDoor', new Parts\Door());
$this->car->setPart('leftDoor', new Parts\Door());
+ $this->car->setPart('trunkLid', new Parts\Door());
}
- /**
- * @return void
- */
public function addEngine()
{
$this->car->setPart('engine', new Parts\Engine());
}
- /**
- * @return void
- */
public function addWheel()
{
$this->car->setPart('wheelLF', new Parts\Wheel());
@@ -40,18 +31,12 @@ class CarBuilder implements BuilderInterface
$this->car->setPart('wheelRR', new Parts\Wheel());
}
- /**
- * @return void
- */
public function createVehicle()
{
$this->car = new Parts\Car();
}
- /**
- * @return Parts\Car
- */
- public function getVehicle()
+ public function getVehicle(): Vehicle
{
return $this->car;
}
diff --git a/Creational/Builder/Director.php b/Creational/Builder/Director.php
index 642cd1b..9925d5a 100644
--- a/Creational/Builder/Director.php
+++ b/Creational/Builder/Director.php
@@ -2,22 +2,17 @@
namespace DesignPatterns\Creational\Builder;
+use DesignPatterns\Creational\Builder\Parts\Vehicle;
+
/**
* Director is part of the builder pattern. It knows the interface of the builder
- * and builds a complex object with the help of the builder.
+ * and builds a complex object with the help of the builder
*
* You can also inject many builders instead of one to build more complex objects
*/
class Director
{
- /**
- * The director don't know about concrete part.
- *
- * @param BuilderInterface $builder
- *
- * @return Parts\Vehicle
- */
- public function build(BuilderInterface $builder)
+ public function build(BuilderInterface $builder): Vehicle
{
$builder->createVehicle();
$builder->addDoors();
diff --git a/Creational/Builder/Parts/Car.php b/Creational/Builder/Parts/Car.php
index e345ea9..53eb0d4 100644
--- a/Creational/Builder/Parts/Car.php
+++ b/Creational/Builder/Parts/Car.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Builder\Parts;
-/**
- * Car is a car.
- */
class Car extends Vehicle
{
}
diff --git a/Creational/Builder/Parts/Door.php b/Creational/Builder/Parts/Door.php
index fc12608..f2732fe 100644
--- a/Creational/Builder/Parts/Door.php
+++ b/Creational/Builder/Parts/Door.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Builder\Parts;
-/**
- * Class Door.
- */
class Door
{
}
diff --git a/Creational/Builder/Parts/Engine.php b/Creational/Builder/Parts/Engine.php
index 5232ab3..48046b8 100644
--- a/Creational/Builder/Parts/Engine.php
+++ b/Creational/Builder/Parts/Engine.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Builder\Parts;
-/**
- * Class Engine.
- */
class Engine
{
}
diff --git a/Creational/Builder/Parts/Bike.php b/Creational/Builder/Parts/Truck.php
similarity index 53%
rename from Creational/Builder/Parts/Bike.php
rename to Creational/Builder/Parts/Truck.php
index e5adbba..23c137c 100644
--- a/Creational/Builder/Parts/Bike.php
+++ b/Creational/Builder/Parts/Truck.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Builder\Parts;
-/**
- * Bike is a bike.
- */
-class Bike extends Vehicle
+class Truck extends Vehicle
{
}
diff --git a/Creational/Builder/Parts/Vehicle.php b/Creational/Builder/Parts/Vehicle.php
index 18c47ba..c7e3c15 100644
--- a/Creational/Builder/Parts/Vehicle.php
+++ b/Creational/Builder/Parts/Vehicle.php
@@ -2,19 +2,16 @@
namespace DesignPatterns\Creational\Builder\Parts;
-/**
- * Vehicle class is an abstraction for a vehicle.
- */
abstract class Vehicle
{
/**
- * @var array
+ * @var object[]
*/
- protected $data;
+ private $data = [];
/**
* @param string $key
- * @param mixed $value
+ * @param object $value
*/
public function setPart($key, $value)
{
diff --git a/Creational/Builder/Parts/Wheel.php b/Creational/Builder/Parts/Wheel.php
index 0a1afbd..4e677e3 100644
--- a/Creational/Builder/Parts/Wheel.php
+++ b/Creational/Builder/Parts/Wheel.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Builder\Parts;
-/**
- * Class Wheel.
- */
class Wheel
{
}
diff --git a/Creational/Builder/README.rst b/Creational/Builder/README.rst
index de8971b..68f3586 100644
--- a/Creational/Builder/README.rst
+++ b/Creational/Builder/README.rst
@@ -44,9 +44,9 @@ BuilderInterface.php
:language: php
:linenos:
-BikeBuilder.php
+TruckBuilder.php
-.. literalinclude:: BikeBuilder.php
+.. literalinclude:: TruckBuilder.php
:language: php
:linenos:
@@ -62,9 +62,9 @@ Parts/Vehicle.php
:language: php
:linenos:
-Parts/Bike.php
+Parts/Truck.php
-.. literalinclude:: Parts/Bike.php
+.. literalinclude:: Parts/Truck.php
:language: php
:linenos:
diff --git a/Creational/Builder/Tests/DirectorTest.php b/Creational/Builder/Tests/DirectorTest.php
index 9f07b83..421107b 100644
--- a/Creational/Builder/Tests/DirectorTest.php
+++ b/Creational/Builder/Tests/DirectorTest.php
@@ -2,40 +2,25 @@
namespace DesignPatterns\Creational\Builder\Tests;
-use DesignPatterns\Creational\Builder\BikeBuilder;
-use DesignPatterns\Creational\Builder\BuilderInterface;
+use DesignPatterns\Creational\Builder\TruckBuilder;
use DesignPatterns\Creational\Builder\CarBuilder;
use DesignPatterns\Creational\Builder\Director;
-/**
- * DirectorTest tests the builder pattern.
- */
class DirectorTest extends \PHPUnit_Framework_TestCase
{
- protected $director;
-
- protected function setUp()
+ public function testCanBuildBike()
{
- $this->director = new Director();
+ $bikeBuilder = new TruckBuilder();
+ $newVehicle = (new Director())->build($bikeBuilder);
+
+ $this->assertInstanceOf('DesignPatterns\Creational\Builder\Parts\Truck', $newVehicle);
}
- public function getBuilder()
+ public function testCanBuildCar()
{
- return array(
- array(new CarBuilder()),
- array(new BikeBuilder()),
- );
- }
+ $carBuilder = new CarBuilder();
+ $newVehicle = (new Director())->build($carBuilder);
- /**
- * Here we test the build process. Notice that the client don't know
- * anything about the concrete builder.
- *
- * @dataProvider getBuilder
- */
- public function testBuild(BuilderInterface $builder)
- {
- $newVehicle = $this->director->build($builder);
- $this->assertInstanceOf('DesignPatterns\Creational\Builder\Parts\Vehicle', $newVehicle);
+ $this->assertInstanceOf('DesignPatterns\Creational\Builder\Parts\Car', $newVehicle);
}
}
diff --git a/Creational/Builder/TruckBuilder.php b/Creational/Builder/TruckBuilder.php
new file mode 100644
index 0000000..e686eac
--- /dev/null
+++ b/Creational/Builder/TruckBuilder.php
@@ -0,0 +1,44 @@
+truck->setPart('rightDoor', new Parts\Door());
+ $this->truck->setPart('leftDoor', new Parts\Door());
+ }
+
+ public function addEngine()
+ {
+ $this->truck->setPart('truckEngine', new Parts\Engine());
+ }
+
+ public function addWheel()
+ {
+ $this->truck->setPart('wheel1', new Parts\Wheel());
+ $this->truck->setPart('wheel2', new Parts\Wheel());
+ $this->truck->setPart('wheel3', new Parts\Wheel());
+ $this->truck->setPart('wheel4', new Parts\Wheel());
+ $this->truck->setPart('wheel5', new Parts\Wheel());
+ $this->truck->setPart('wheel6', new Parts\Wheel());
+ }
+
+ public function createVehicle()
+ {
+ $this->truck = new Parts\Truck();
+ }
+
+ public function getVehicle(): Vehicle
+ {
+ return $this->truck;
+ }
+}
diff --git a/Creational/FactoryMethod/Bicycle.php b/Creational/FactoryMethod/Bicycle.php
index fe6deee..95aacbf 100644
--- a/Creational/FactoryMethod/Bicycle.php
+++ b/Creational/FactoryMethod/Bicycle.php
@@ -2,22 +2,14 @@
namespace DesignPatterns\Creational\FactoryMethod;
-/**
- * Bicycle is a bicycle.
- */
class Bicycle implements VehicleInterface
{
/**
* @var string
*/
- protected $color;
+ private $color;
- /**
- * Sets the color of the bicycle.
- *
- * @param string $rgb
- */
- public function setColor($rgb)
+ public function setColor(string $rgb)
{
$this->color = $rgb;
}
diff --git a/Creational/FactoryMethod/CarFerrari.php b/Creational/FactoryMethod/CarFerrari.php
new file mode 100644
index 0000000..dfb9eca
--- /dev/null
+++ b/Creational/FactoryMethod/CarFerrari.php
@@ -0,0 +1,16 @@
+color = $rgb;
+ }
+}
diff --git a/Creational/FactoryMethod/CarMercedes.php b/Creational/FactoryMethod/CarMercedes.php
new file mode 100644
index 0000000..bc6e337
--- /dev/null
+++ b/Creational/FactoryMethod/CarMercedes.php
@@ -0,0 +1,21 @@
+color = $rgb;
+ }
+
+ public function addAMGTuning()
+ {
+ // do additional tuning here
+ }
+}
diff --git a/Creational/FactoryMethod/FactoryMethod.php b/Creational/FactoryMethod/FactoryMethod.php
index fa3d4a3..d999f7b 100644
--- a/Creational/FactoryMethod/FactoryMethod.php
+++ b/Creational/FactoryMethod/FactoryMethod.php
@@ -2,36 +2,17 @@
namespace DesignPatterns\Creational\FactoryMethod;
-/**
- * class FactoryMethod.
- */
abstract class FactoryMethod
{
- const CHEAP = 1;
- const FAST = 2;
+ const CHEAP = 'cheap';
+ const FAST = 'fast';
- /**
- * The children of the class must implement this method.
- *
- * Sometimes this method can be public to get "raw" object
- *
- * @param string $type a generic type
- *
- * @return VehicleInterface a new vehicle
- */
- abstract protected function createVehicle($type);
+ abstract protected function createVehicle(string $type): VehicleInterface;
- /**
- * Creates a new vehicle.
- *
- * @param int $type
- *
- * @return VehicleInterface a new vehicle
- */
- public function create($type)
+ public function create(string $type): VehicleInterface
{
$obj = $this->createVehicle($type);
- $obj->setColor('#f00');
+ $obj->setColor('black');
return $obj;
}
diff --git a/Creational/FactoryMethod/Ferrari.php b/Creational/FactoryMethod/Ferrari.php
deleted file mode 100644
index 9434e3d..0000000
--- a/Creational/FactoryMethod/Ferrari.php
+++ /dev/null
@@ -1,22 +0,0 @@
-color = $rgb;
- }
-}
diff --git a/Creational/FactoryMethod/GermanFactory.php b/Creational/FactoryMethod/GermanFactory.php
index 1f65eb4..7abb750 100644
--- a/Creational/FactoryMethod/GermanFactory.php
+++ b/Creational/FactoryMethod/GermanFactory.php
@@ -2,28 +2,19 @@
namespace DesignPatterns\Creational\FactoryMethod;
-/**
- * GermanFactory is a vehicle factory in Germany.
- */
class GermanFactory extends FactoryMethod
{
- /**
- * {@inheritdoc}
- */
- protected function createVehicle($type)
+ protected function createVehicle(string $type): VehicleInterface
{
switch ($type) {
case parent::CHEAP:
return new Bicycle();
- break;
case parent::FAST:
- $obj = new Porsche();
- // we can specialize the way we want some concrete Vehicle since
- // we know the class
- $obj->addTuningAMG();
+ $carMercedes = new CarMercedes();
+ // we can specialize the way we want some concrete Vehicle since we know the class
+ $carMercedes->addAMGTuning();
- return $obj;
- break;
+ return $carMercedes;
default:
throw new \InvalidArgumentException("$type is not a valid vehicle");
}
diff --git a/Creational/FactoryMethod/ItalianFactory.php b/Creational/FactoryMethod/ItalianFactory.php
index 25eeaf1..58a54f4 100644
--- a/Creational/FactoryMethod/ItalianFactory.php
+++ b/Creational/FactoryMethod/ItalianFactory.php
@@ -2,23 +2,15 @@
namespace DesignPatterns\Creational\FactoryMethod;
-/**
- * ItalianFactory is vehicle factory in Italy.
- */
class ItalianFactory extends FactoryMethod
{
- /**
- * {@inheritdoc}
- */
- protected function createVehicle($type)
+ protected function createVehicle(string $type): VehicleInterface
{
switch ($type) {
case parent::CHEAP:
return new Bicycle();
- break;
case parent::FAST:
- return new Ferrari();
- break;
+ return new CarFerrari();
default:
throw new \InvalidArgumentException("$type is not a valid vehicle");
}
diff --git a/Creational/FactoryMethod/Porsche.php b/Creational/FactoryMethod/Porsche.php
deleted file mode 100644
index bdabb87..0000000
--- a/Creational/FactoryMethod/Porsche.php
+++ /dev/null
@@ -1,30 +0,0 @@
-color = $rgb;
- }
-
- /**
- * although tuning by AMG is only offered for Mercedes Cars,
- * this is a valid coding example ...
- */
- public function addTuningAMG()
- {
- }
-}
diff --git a/Creational/FactoryMethod/README.rst b/Creational/FactoryMethod/README.rst
index 7e69a8d..3664631 100644
--- a/Creational/FactoryMethod/README.rst
+++ b/Creational/FactoryMethod/README.rst
@@ -52,23 +52,23 @@ VehicleInterface.php
:language: php
:linenos:
-Porsche.php
+CarMercedes.php
-.. literalinclude:: Porsche.php
+.. literalinclude:: CarMercedes.php
:language: php
:linenos:
-Bicycle.php
+CarFerrari.php
+
+.. literalinclude:: CarFerrari.php
+ :language: php
+ :linenos:
+
+ Bicycle.php
.. literalinclude:: Bicycle.php
- :language: php
- :linenos:
-
-Ferrari.php
-
-.. literalinclude:: Ferrari.php
- :language: php
- :linenos:
+:language: php
+ :linenos:
Test
----
diff --git a/Creational/FactoryMethod/Tests/FactoryMethodTest.php b/Creational/FactoryMethod/Tests/FactoryMethodTest.php
index 6b1c4d2..263b0cb 100644
--- a/Creational/FactoryMethod/Tests/FactoryMethodTest.php
+++ b/Creational/FactoryMethod/Tests/FactoryMethodTest.php
@@ -6,44 +6,46 @@ use DesignPatterns\Creational\FactoryMethod\FactoryMethod;
use DesignPatterns\Creational\FactoryMethod\GermanFactory;
use DesignPatterns\Creational\FactoryMethod\ItalianFactory;
-/**
- * FactoryMethodTest tests the factory method pattern.
- */
class FactoryMethodTest extends \PHPUnit_Framework_TestCase
{
- protected $type = array(
- FactoryMethod::CHEAP,
- FactoryMethod::FAST,
- );
-
- public function getShop()
+ public function testCanCreateCheapVehicleInGermany()
{
- return array(
- array(new GermanFactory()),
- array(new ItalianFactory()),
- );
+ $factory = new GermanFactory();
+ $result = $factory->create(FactoryMethod::CHEAP);
+
+ $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\Bicycle', $result);
+ }
+
+ public function testCanCreateFastVehicleInGermany()
+ {
+ $factory = new GermanFactory();
+ $result = $factory->create(FactoryMethod::FAST);
+
+ $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\CarMercedes', $result);
+ }
+
+ public function testCanCreateCheapVehicleInItaly()
+ {
+ $factory = new ItalianFactory();
+ $result = $factory->create(FactoryMethod::CHEAP);
+
+ $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\Bicycle', $result);
+ }
+
+ public function testCanCreateFastVehicleInItaly()
+ {
+ $factory = new ItalianFactory();
+ $result = $factory->create(FactoryMethod::FAST);
+
+ $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\CarFerrari', $result);
}
/**
- * @dataProvider getShop
- */
- public function testCreation(FactoryMethod $shop)
- {
- // this test method acts as a client for the factory. We don't care
- // about the factory, all we know is it can produce vehicle
- foreach ($this->type as $oneType) {
- $vehicle = $shop->create($oneType);
- $this->assertInstanceOf('DesignPatterns\Creational\FactoryMethod\VehicleInterface', $vehicle);
- }
- }
-
- /**
- * @dataProvider getShop
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage spaceship is not a valid vehicle
*/
- public function testUnknownType(FactoryMethod $shop)
+ public function testUnknownType()
{
- $shop->create('spaceship');
+ (new ItalianFactory())->create('spaceship');
}
}
diff --git a/Creational/FactoryMethod/VehicleInterface.php b/Creational/FactoryMethod/VehicleInterface.php
index ccb05ee..9f5fb15 100644
--- a/Creational/FactoryMethod/VehicleInterface.php
+++ b/Creational/FactoryMethod/VehicleInterface.php
@@ -2,15 +2,7 @@
namespace DesignPatterns\Creational\FactoryMethod;
-/**
- * VehicleInterface is a contract for a vehicle.
- */
interface VehicleInterface
{
- /**
- * sets the color of the vehicle.
- *
- * @param string $rgb
- */
- public function setColor($rgb);
+ public function setColor(string $rgb);
}
diff --git a/Creational/Multiton/Multiton.php b/Creational/Multiton/Multiton.php
index 5338e62..9df179c 100644
--- a/Creational/Multiton/Multiton.php
+++ b/Creational/Multiton/Multiton.php
@@ -2,46 +2,26 @@
namespace DesignPatterns\Creational\Multiton;
-/**
- * class Multiton.
- */
class Multiton
{
- /**
- * the first instance.
- */
const INSTANCE_1 = '1';
-
- /**
- * the second instance.
- */
const INSTANCE_2 = '2';
/**
- * holds the named instances.
- *
- * @var array
+ * @var Multiton[]
*/
- private static $instances = array();
+ private static $instances = [];
/**
- * should not be called from outside: private!
+ * this is private to prevent from creating arbitrary instances
*/
private function __construct()
{
}
- /**
- * gets the instance with the given name, e.g. Multiton::INSTANCE_1
- * uses lazy initialization.
- *
- * @param string $instanceName
- *
- * @return Multiton
- */
- public static function getInstance($instanceName)
+ public static function getInstance(string $instanceName): Multiton
{
- if (!array_key_exists($instanceName, self::$instances)) {
+ if (!isset(self::$instances[$instanceName])) {
self::$instances[$instanceName] = new self();
}
@@ -49,18 +29,14 @@ class Multiton
}
/**
- * prevent instance from being cloned.
- *
- * @return void
+ * prevent instance from being cloned
*/
private function __clone()
{
}
/**
- * prevent instance from being unserialized.
- *
- * @return void
+ * prevent instance from being unserialized
*/
private function __wakeup()
{
diff --git a/Creational/Multiton/Tests/MultitonTest.php b/Creational/Multiton/Tests/MultitonTest.php
new file mode 100644
index 0000000..dd0383f
--- /dev/null
+++ b/Creational/Multiton/Tests/MultitonTest.php
@@ -0,0 +1,27 @@
+assertInstanceOf('DesignPatterns\Creational\Multiton\Multiton', $firstCall);
+ $this->assertSame($firstCall, $secondCall);
+ }
+
+ public function testUniquenessForEveryInstance()
+ {
+ $firstCall = Multiton::getInstance(Multiton::INSTANCE_1);
+ $secondCall = Multiton::getInstance(Multiton::INSTANCE_2);
+
+ $this->assertInstanceOf('DesignPatterns\Creational\Multiton\Multiton', $firstCall);
+ $this->assertInstanceOf('DesignPatterns\Creational\Multiton\Multiton', $secondCall);
+ $this->assertNotSame($firstCall, $secondCall);
+ }
+}
diff --git a/Creational/Pool/Pool.php b/Creational/Pool/Pool.php
deleted file mode 100644
index 7dcc6e3..0000000
--- a/Creational/Pool/Pool.php
+++ /dev/null
@@ -1,28 +0,0 @@
-class = $class;
- }
-
- public function get()
- {
- if (count($this->instances) > 0) {
- return array_pop($this->instances);
- }
-
- return new $this->class();
- }
-
- public function dispose($instance)
- {
- $this->instances[] = $instance;
- }
-}
diff --git a/Creational/Pool/Processor.php b/Creational/Pool/Processor.php
deleted file mode 100644
index 89179b9..0000000
--- a/Creational/Pool/Processor.php
+++ /dev/null
@@ -1,51 +0,0 @@
-pool = $pool;
- }
-
- public function process($image)
- {
- if ($this->processing++ < $this->maxProcesses) {
- $this->createWorker($image);
- } else {
- $this->pushToWaitingQueue($image);
- }
- }
-
- private function createWorker($image)
- {
- $worker = $this->pool->get();
- $worker->run($image, array($this, 'processDone'));
- }
-
- public function processDone($worker)
- {
- $this->processing--;
- $this->pool->dispose($worker);
-
- if (count($this->waitingQueue) > 0) {
- $this->createWorker($this->popFromWaitingQueue());
- }
- }
-
- private function pushToWaitingQueue($image)
- {
- $this->waitingQueue[] = $image;
- }
-
- private function popFromWaitingQueue()
- {
- return array_pop($this->waitingQueue);
- }
-}
diff --git a/Creational/Pool/README.rst b/Creational/Pool/README.rst
index 3fac0e9..78dc910 100644
--- a/Creational/Pool/README.rst
+++ b/Creational/Pool/README.rst
@@ -36,21 +36,15 @@ Code
You can also find these code on `GitHub`_
-Pool.php
+WorkerPool.php
-.. literalinclude:: Pool.php
+.. literalinclude:: WorkerPool.php
:language: php
:linenos:
-Processor.php
+StringReverseWorker.php
-.. literalinclude:: Processor.php
- :language: php
- :linenos:
-
-Worker.php
-
-.. literalinclude:: Worker.php
+.. literalinclude:: StringReverseWorker.php
:language: php
:linenos:
@@ -63,11 +57,5 @@ Tests/PoolTest.php
:language: php
:linenos:
-Tests/TestWorker.php
-
-.. literalinclude:: Tests/TestWorker.php
- :language: php
- :linenos:
-
.. _`GitHub`: https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/Pool
.. __: http://en.wikipedia.org/wiki/Object_pool_pattern
diff --git a/Creational/Pool/StringReverseWorker.php b/Creational/Pool/StringReverseWorker.php
new file mode 100644
index 0000000..92c2ce5
--- /dev/null
+++ b/Creational/Pool/StringReverseWorker.php
@@ -0,0 +1,21 @@
+createdAt = new \DateTime();
+ }
+
+ public function run(string $text)
+ {
+ return strrev($text);
+ }
+}
diff --git a/Creational/Pool/Tests/PoolTest.php b/Creational/Pool/Tests/PoolTest.php
index 7ff546a..99bf874 100644
--- a/Creational/Pool/Tests/PoolTest.php
+++ b/Creational/Pool/Tests/PoolTest.php
@@ -2,21 +2,28 @@
namespace DesignPatterns\Creational\Pool\Tests;
-use DesignPatterns\Creational\Pool\Pool;
+use DesignPatterns\Creational\Pool\WorkerPool;
class PoolTest extends \PHPUnit_Framework_TestCase
{
- public function testPool()
+ public function testCanGetNewInstancesWithGet()
{
- $pool = new Pool('DesignPatterns\Creational\Pool\Tests\TestWorker');
- $worker = $pool->get();
+ $pool = new WorkerPool();
+ $worker1 = $pool->get();
+ $worker2 = $pool->get();
- $this->assertEquals(1, $worker->id);
+ $this->assertCount(2, $pool);
+ $this->assertNotSame($worker1, $worker2);
+ }
- $worker->id = 5;
- $pool->dispose($worker);
+ public function testCanGetSameInstanceTwiceWhenDisposingItFirst()
+ {
+ $pool = new WorkerPool();
+ $worker1 = $pool->get();
+ $pool->dispose($worker1);
+ $worker2 = $pool->get();
- $this->assertEquals(5, $pool->get()->id);
- $this->assertEquals(1, $pool->get()->id);
+ $this->assertCount(1, $pool);
+ $this->assertSame($worker1, $worker2);
}
}
diff --git a/Creational/Pool/Tests/TestWorker.php b/Creational/Pool/Tests/TestWorker.php
deleted file mode 100644
index 3cfbef5..0000000
--- a/Creational/Pool/Tests/TestWorker.php
+++ /dev/null
@@ -1,8 +0,0 @@
-freeWorkers) == 0) {
+ $worker = new StringReverseWorker();
+ } else {
+ $worker = array_pop($this->freeWorkers);
+ }
+
+ $this->occupiedWorkers[spl_object_hash($worker)] = $worker;
+
+ return $worker;
+ }
+
+ public function dispose(StringReverseWorker $worker)
+ {
+ $key = spl_object_hash($worker);
+
+ if (isset($this->occupiedWorkers[$key])) {
+ unset($this->occupiedWorkers[$key]);
+ $this->freeWorkers[$key] = $worker;
+ }
+ }
+
+ public function count(): int
+ {
+ return count($this->occupiedWorkers) + count($this->freeWorkers);
+ }
+}
diff --git a/Creational/Prototype/BarBookPrototype.php b/Creational/Prototype/BarBookPrototype.php
index 7c9b72b..5afadad 100644
--- a/Creational/Prototype/BarBookPrototype.php
+++ b/Creational/Prototype/BarBookPrototype.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Prototype;
-/**
- * Class BarBookPrototype.
- */
class BarBookPrototype extends BookPrototype
{
/**
@@ -12,9 +9,6 @@ class BarBookPrototype extends BookPrototype
*/
protected $category = 'Bar';
- /**
- * empty clone.
- */
public function __clone()
{
}
diff --git a/Creational/Prototype/BookPrototype.php b/Creational/Prototype/BookPrototype.php
index e0fafa6..2f4d55f 100644
--- a/Creational/Prototype/BookPrototype.php
+++ b/Creational/Prototype/BookPrototype.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\Prototype;
-/**
- * class BookPrototype.
- */
abstract class BookPrototype
{
/**
@@ -17,24 +14,13 @@ abstract class BookPrototype
*/
protected $category;
- /**
- * @abstract
- *
- * @return void
- */
abstract public function __clone();
- /**
- * @return string
- */
- public function getTitle()
+ public function getTitle(): string
{
return $this->title;
}
- /**
- * @param string $title
- */
public function setTitle($title)
{
$this->title = $title;
diff --git a/Creational/Prototype/FooBookPrototype.php b/Creational/Prototype/FooBookPrototype.php
index 95ea9e6..51550f9 100644
--- a/Creational/Prototype/FooBookPrototype.php
+++ b/Creational/Prototype/FooBookPrototype.php
@@ -2,16 +2,13 @@
namespace DesignPatterns\Creational\Prototype;
-/**
- * Class FooBookPrototype.
- */
class FooBookPrototype extends BookPrototype
{
+ /**
+ * @var string
+ */
protected $category = 'Foo';
- /**
- * empty clone.
- */
public function __clone()
{
}
diff --git a/Creational/Prototype/README.rst b/Creational/Prototype/README.rst
index af38f8f..28606a6 100644
--- a/Creational/Prototype/README.rst
+++ b/Creational/Prototype/README.rst
@@ -25,12 +25,6 @@ Code
You can also find these code on `GitHub`_
-index.php
-
-.. literalinclude:: index.php
- :language: php
- :linenos:
-
BookPrototype.php
.. literalinclude:: BookPrototype.php
@@ -52,5 +46,11 @@ FooBookPrototype.php
Test
----
+Tests/PrototypeTest.php
+
+.. literalinclude:: Tests/PrototypeTest.php
+ :language: php
+ :linenos:
+
.. _`GitHub`: https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/Prototype
.. __: http://en.wikipedia.org/wiki/Prototype_pattern
diff --git a/Creational/Prototype/Tests/PrototypeTest.php b/Creational/Prototype/Tests/PrototypeTest.php
new file mode 100644
index 0000000..31d906f
--- /dev/null
+++ b/Creational/Prototype/Tests/PrototypeTest.php
@@ -0,0 +1,27 @@
+setTitle('Foo Book No ' . $i);
+ $this->assertInstanceOf('DesignPatterns\Creational\Prototype\FooBookPrototype', $book);
+ }
+
+ for ($i = 0; $i < 5; $i++) {
+ $book = clone $barPrototype;
+ $book->setTitle('Bar Book No ' . $i);
+ $this->assertInstanceOf('DesignPatterns\Creational\Prototype\BarBookPrototype', $book);
+ }
+ }
+}
diff --git a/Creational/Prototype/index.php b/Creational/Prototype/index.php
deleted file mode 100644
index d0f6e94..0000000
--- a/Creational/Prototype/index.php
+++ /dev/null
@@ -1,17 +0,0 @@
-setTitle('Foo Book No '.$i);
-}
-
-for ($i = 0; $i < 5000; $i++) {
- $book = clone $barPrototype;
- $book->setTitle('Bar Book No '.$i);
-}
diff --git a/Creational/SimpleFactory/Bicycle.php b/Creational/SimpleFactory/Bicycle.php
index defa801..552b986 100644
--- a/Creational/SimpleFactory/Bicycle.php
+++ b/Creational/SimpleFactory/Bicycle.php
@@ -2,17 +2,9 @@
namespace DesignPatterns\Creational\SimpleFactory;
-/**
- * Bicycle is a bicycle.
- */
-class Bicycle implements VehicleInterface
+class Bicycle
{
- /**
- * @param mixed $destination
- *
- * @return mixed|void
- */
- public function driveTo($destination)
+ public function driveTo(string $destination)
{
}
}
diff --git a/Creational/SimpleFactory/README.rst b/Creational/SimpleFactory/README.rst
index 8f6ce81..3905367 100644
--- a/Creational/SimpleFactory/README.rst
+++ b/Creational/SimpleFactory/README.rst
@@ -6,11 +6,9 @@ Purpose
SimpleFactory is a simple factory pattern.
-It differs from the static factory because it is NOT static and as you
-know: static => global => evil!
-
-Therefore, you can have multiple factories, differently parametrized,
-you can subclass it and you can mock-up it.
+It differs from the static factory because it is not static.
+Therefore, you can have multiple factories, differently parametrized, you can subclass it and you can mock it.
+It always should be preferred over a static factory!
UML Diagram
-----------
@@ -42,12 +40,6 @@ Bicycle.php
:language: php
:linenos:
-Scooter.php
-
-.. literalinclude:: Scooter.php
- :language: php
- :linenos:
-
Usage
-----
diff --git a/Creational/SimpleFactory/Scooter.php b/Creational/SimpleFactory/Scooter.php
deleted file mode 100644
index e1db734..0000000
--- a/Creational/SimpleFactory/Scooter.php
+++ /dev/null
@@ -1,16 +0,0 @@
-typeList = array(
- 'bicycle' => __NAMESPACE__.'\Bicycle',
- 'other' => __NAMESPACE__.'\Scooter',
- );
- }
-
- /**
- * Creates a vehicle.
- *
- * @param string $type a known type key
- *
- * @throws \InvalidArgumentException
- *
- * @return VehicleInterface a new instance of VehicleInterface
- */
- public function createVehicle($type)
- {
- if (!array_key_exists($type, $this->typeList)) {
- throw new \InvalidArgumentException("$type is not valid vehicle");
- }
- $className = $this->typeList[$type];
-
- return new $className();
+ return new Bicycle();
}
}
diff --git a/Creational/SimpleFactory/Tests/SimpleFactoryTest.php b/Creational/SimpleFactory/Tests/SimpleFactoryTest.php
index c2f5379..16899eb 100644
--- a/Creational/SimpleFactory/Tests/SimpleFactoryTest.php
+++ b/Creational/SimpleFactory/Tests/SimpleFactoryTest.php
@@ -4,40 +4,11 @@ namespace DesignPatterns\Creational\SimpleFactory\Tests;
use DesignPatterns\Creational\SimpleFactory\SimpleFactory;
-/**
- * SimpleFactoryTest tests the Simple Factory pattern.
- */
class SimpleFactoryTest extends \PHPUnit_Framework_TestCase
{
- protected $factory;
-
- protected function setUp()
+ public function testCanCreateBicycle()
{
- $this->factory = new SimpleFactory();
- }
-
- public function getType()
- {
- return array(
- array('bicycle'),
- array('other'),
- );
- }
-
- /**
- * @dataProvider getType
- */
- public function testCreation($type)
- {
- $obj = $this->factory->createVehicle($type);
- $this->assertInstanceOf('DesignPatterns\Creational\SimpleFactory\VehicleInterface', $obj);
- }
-
- /**
- * @expectedException \InvalidArgumentException
- */
- public function testBadType()
- {
- $this->factory->createVehicle('car');
+ $bicycle = (new SimpleFactory())->createBicycle();
+ $this->assertInstanceOf('DesignPatterns\Creational\SimpleFactory\Bicycle', $bicycle);
}
}
diff --git a/Creational/SimpleFactory/VehicleInterface.php b/Creational/SimpleFactory/VehicleInterface.php
deleted file mode 100644
index f2c8d3f..0000000
--- a/Creational/SimpleFactory/VehicleInterface.php
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
- PHP
- \DesignPatterns\Creational\SimpleFactory\SimpleFactory
-
- \DesignPatterns\Creational\SimpleFactory\Scooter
- \DesignPatterns\Creational\SimpleFactory\SimpleFactory
- \DesignPatterns\Creational\SimpleFactory\VehicleInterface
- \DesignPatterns\Creational\SimpleFactory\Bicycle
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Creational\SimpleFactory\Bicycle
+
+ \DesignPatterns\Creational\SimpleFactory\Bicycle
+ \DesignPatterns\Creational\SimpleFactory\SimpleFactory
+
+
+
+
+
+
+ Methods
+ Constants
+ Fields
+
+ private
+
+
diff --git a/Creational/SimpleFactory/uml/uml.png b/Creational/SimpleFactory/uml/uml.png
index d22c00a..93d2838 100644
Binary files a/Creational/SimpleFactory/uml/uml.png and b/Creational/SimpleFactory/uml/uml.png differ
diff --git a/Creational/SimpleFactory/uml/uml.svg b/Creational/SimpleFactory/uml/uml.svg
index 7da8077..73d07c1 100644
--- a/Creational/SimpleFactory/uml/uml.svg
+++ b/Creational/SimpleFactory/uml/uml.svg
@@ -1,286 +1,245 @@
-
+
diff --git a/Creational/Singleton/Singleton.php b/Creational/Singleton/Singleton.php
index 650810e..c7cacc7 100644
--- a/Creational/Singleton/Singleton.php
+++ b/Creational/Singleton/Singleton.php
@@ -2,22 +2,17 @@
namespace DesignPatterns\Creational\Singleton;
-/**
- * class Singleton.
- */
-class Singleton
+final class Singleton
{
/**
- * @var Singleton reference to singleton instance
+ * @var Singleton
*/
private static $instance;
/**
- * gets the instance via lazy initialization (created on first usage).
- *
- * @return self
+ * gets the instance via lazy initialization (created on first usage)
*/
- public static function getInstance()
+ public static function getInstance(): Singleton
{
if (null === static::$instance) {
static::$instance = new static();
@@ -27,33 +22,24 @@ class Singleton
}
/**
- * is not allowed to call from outside: private!
+ * is not allowed to call from outside to prevent from creating multiple instances,
+ * to use the singleton, you have to obtain the instance from Singleton::getInstance() instead
*/
private function __construct()
{
}
/**
- * prevent the instance from being cloned.
- *
- * @throws SingletonPatternViolationException
- *
- * @return void
+ * prevent the instance from being cloned (which would create a second instance of it)
*/
- final public function __clone()
+ private function __clone()
{
- throw new SingletonPatternViolationException('This is a Singleton. Clone is forbidden');
}
/**
- * prevent from being unserialized.
- *
- * @throws SingletonPatternViolationException
- *
- * @return void
+ * prevent from being unserialized (which would create a second instance of it)
*/
- final public function __wakeup()
+ private function __wakeup()
{
- throw new SingletonPatternViolationException('This is a Singleton. __wakeup usage is forbidden');
}
}
diff --git a/Creational/Singleton/SingletonPatternViolationException.php b/Creational/Singleton/SingletonPatternViolationException.php
deleted file mode 100644
index b025b4a..0000000
--- a/Creational/Singleton/SingletonPatternViolationException.php
+++ /dev/null
@@ -1,7 +0,0 @@
-assertInstanceOf('DesignPatterns\Creational\Singleton\Singleton', $firstCall);
$secondCall = Singleton::getInstance();
+
+ $this->assertInstanceOf('DesignPatterns\Creational\Singleton\Singleton', $firstCall);
$this->assertSame($firstCall, $secondCall);
}
-
- public function testNoConstructor()
- {
- $obj = Singleton::getInstance();
-
- $refl = new \ReflectionObject($obj);
- $meth = $refl->getMethod('__construct');
- $this->assertTrue($meth->isPrivate());
- }
-
- /**
- * @expectedException \DesignPatterns\Creational\Singleton\SingletonPatternViolationException
- *
- * @return void
- */
- public function testNoCloneAllowed()
- {
- $obj1 = Singleton::getInstance();
- $obj2 = clone $obj1;
- }
-
- /**
- * @expectedException \DesignPatterns\Creational\Singleton\SingletonPatternViolationException
- *
- * @return void
- */
- public function testNoSerializationAllowed()
- {
- $obj1 = Singleton::getInstance();
- $serialized = serialize($obj1);
- $obj2 = unserialize($serialized);
- }
}
diff --git a/Creational/StaticFactory/FormatNumber.php b/Creational/StaticFactory/FormatNumber.php
index e577ab2..69fd9e1 100644
--- a/Creational/StaticFactory/FormatNumber.php
+++ b/Creational/StaticFactory/FormatNumber.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\StaticFactory;
-/**
- * Class FormatNumber.
- */
class FormatNumber implements FormatterInterface
{
}
diff --git a/Creational/StaticFactory/FormatString.php b/Creational/StaticFactory/FormatString.php
index 5cb9e28..14c2055 100644
--- a/Creational/StaticFactory/FormatString.php
+++ b/Creational/StaticFactory/FormatString.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\StaticFactory;
-/**
- * Class FormatString.
- */
class FormatString implements FormatterInterface
{
}
diff --git a/Creational/StaticFactory/FormatterInterface.php b/Creational/StaticFactory/FormatterInterface.php
index 349f8b6..22ea0de 100644
--- a/Creational/StaticFactory/FormatterInterface.php
+++ b/Creational/StaticFactory/FormatterInterface.php
@@ -2,9 +2,6 @@
namespace DesignPatterns\Creational\StaticFactory;
-/**
- * Class FormatterInterface.
- */
interface FormatterInterface
{
}
diff --git a/Creational/StaticFactory/StaticFactory.php b/Creational/StaticFactory/StaticFactory.php
index e4c4f4b..237ec68 100644
--- a/Creational/StaticFactory/StaticFactory.php
+++ b/Creational/StaticFactory/StaticFactory.php
@@ -3,30 +3,26 @@
namespace DesignPatterns\Creational\StaticFactory;
/**
- * Note1: Remember, static => global => evil
+ * Note1: Remember, static means global state which is evil because it can't be mocked for tests
* Note2: Cannot be subclassed or mock-upped or have multiple different instances.
*/
-class StaticFactory
+final class StaticFactory
{
/**
- * the parametrized function to get create an instance.
- *
* @param string $type
*
- * @static
- *
- * @throws \InvalidArgumentException
- *
* @return FormatterInterface
*/
- public static function factory($type)
+ public static function factory(string $type): FormatterInterface
{
- $className = __NAMESPACE__.'\Format'.ucfirst($type);
-
- if (!class_exists($className)) {
- throw new \InvalidArgumentException('Missing format class.');
+ if ($type == 'number') {
+ return new FormatNumber();
}
- return new $className();
+ if ($type == 'string') {
+ return new FormatString();
+ }
+
+ throw new \InvalidArgumentException('Unknown format given');
}
}
diff --git a/Creational/StaticFactory/Tests/StaticFactoryTest.php b/Creational/StaticFactory/Tests/StaticFactoryTest.php
index 61f65af..cd1362d 100644
--- a/Creational/StaticFactory/Tests/StaticFactoryTest.php
+++ b/Creational/StaticFactory/Tests/StaticFactoryTest.php
@@ -4,33 +4,29 @@ namespace DesignPatterns\Creational\StaticFactory\Tests;
use DesignPatterns\Creational\StaticFactory\StaticFactory;
-/**
- * Tests for Static Factory pattern.
- */
class StaticFactoryTest extends \PHPUnit_Framework_TestCase
{
- public function getTypeList()
+ public function testCanCreateNumberFormatter()
{
- return array(
- array('string'),
- array('number'),
+ $this->assertInstanceOf(
+ 'DesignPatterns\Creational\StaticFactory\FormatNumber',
+ StaticFactory::factory('number')
+ );
+ }
+
+ public function testCanCreateStringFormatter()
+ {
+ $this->assertInstanceOf(
+ 'DesignPatterns\Creational\StaticFactory\FormatString',
+ StaticFactory::factory('string')
);
}
/**
- * @dataProvider getTypeList
- */
- public function testCreation($type)
- {
- $obj = StaticFactory::factory($type);
- $this->assertInstanceOf('DesignPatterns\Creational\StaticFactory\FormatterInterface', $obj);
- }
-
- /**
- * @expectedException InvalidArgumentException
+ * @expectedException \InvalidArgumentException
*/
public function testException()
{
- StaticFactory::factory('');
+ StaticFactory::factory('object');
}
}
diff --git a/LICENSE b/LICENSE
index 5f1ce83..97e323b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011 Dominik Liebler
+Copyright (c) 2011-2016 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:
diff --git a/Makefile b/Makefile
index 52169e6..c3841a2 100644
--- a/Makefile
+++ b/Makefile
@@ -190,3 +190,20 @@ pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/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
+
+vendor: composer.phar
+ php composer.phar install
+
+cs: install
+ ./vendor/bin/phpcs -p --standard=PSR2 --ignore=vendor .
+
+test: install cs
+ ./vendor/bin/phpunit
diff --git a/More/Delegation/JuniorDeveloper.php b/More/Delegation/JuniorDeveloper.php
index 0946dad..dbaa30a 100644
--- a/More/Delegation/JuniorDeveloper.php
+++ b/More/Delegation/JuniorDeveloper.php
@@ -2,12 +2,9 @@
namespace DesignPatterns\More\Delegation;
-/**
- * Class JuniorDeveloper.
- */
class JuniorDeveloper
{
- public function writeBadCode()
+ public function writeBadCode(): string
{
return 'Some junior developer generated code...';
}
diff --git a/More/Delegation/README.rst b/More/Delegation/README.rst
index e89e833..9c43254 100644
--- a/More/Delegation/README.rst
+++ b/More/Delegation/README.rst
@@ -27,12 +27,6 @@ Code
You can also find these code on `GitHub`_
-Usage.php
-
-.. literalinclude:: Usage.php
- :language: php
- :linenos:
-
TeamLead.php
.. literalinclude:: TeamLead.php
diff --git a/More/Delegation/TeamLead.php b/More/Delegation/TeamLead.php
index 9b75190..67d9bdf 100644
--- a/More/Delegation/TeamLead.php
+++ b/More/Delegation/TeamLead.php
@@ -2,31 +2,23 @@
namespace DesignPatterns\More\Delegation;
-/**
- * Class TeamLead.
- */
class TeamLead
{
- /** @var JuniorDeveloper */
- protected $slave;
+ /**
+ * @var JuniorDeveloper
+ */
+ private $junior;
/**
- * Give junior developer into teamlead submission.
- *
* @param JuniorDeveloper $junior
*/
public function __construct(JuniorDeveloper $junior)
{
- $this->slave = $junior;
+ $this->junior = $junior;
}
- /**
- * TeamLead drink coffee, junior work.
- *
- * @return mixed
- */
- public function writeCode()
+ public function writeCode(): string
{
- return $this->slave->writeBadCode();
+ return $this->junior->writeBadCode();
}
}
diff --git a/More/Delegation/Tests/DelegationTest.php b/More/Delegation/Tests/DelegationTest.php
index 7d0a33b..8660ef7 100644
--- a/More/Delegation/Tests/DelegationTest.php
+++ b/More/Delegation/Tests/DelegationTest.php
@@ -4,15 +4,13 @@ namespace DesignPatterns\More\Delegation\Tests;
use DesignPatterns\More\Delegation;
-/**
- * DelegationTest tests the delegation pattern.
- */
class DelegationTest extends \PHPUnit_Framework_TestCase
{
public function testHowTeamLeadWriteCode()
{
$junior = new Delegation\JuniorDeveloper();
$teamLead = new Delegation\TeamLead($junior);
+
$this->assertEquals($junior->writeBadCode(), $teamLead->writeCode());
}
}
diff --git a/More/Delegation/Usage.php b/More/Delegation/Usage.php
deleted file mode 100644
index a1d9837..0000000
--- a/More/Delegation/Usage.php
+++ /dev/null
@@ -1,9 +0,0 @@
-writeCode();
diff --git a/More/EAV/Attribute.php b/More/EAV/Attribute.php
index 3874360..10ac638 100644
--- a/More/EAV/Attribute.php
+++ b/More/EAV/Attribute.php
@@ -2,15 +2,10 @@
namespace DesignPatterns\More\EAV;
-use SplObjectStorage;
-
-/**
- * Class Attribute.
- */
-class Attribute implements ValueAccessInterface
+class Attribute
{
/**
- * @var SplObjectStorage
+ * @var \SplObjectStorage
*/
private $values;
@@ -19,64 +14,27 @@ class Attribute implements ValueAccessInterface
*/
private $name;
- public function __construct()
+ public function __construct(string $name)
{
- $this->values = new SplObjectStorage();
+ $this->values = new \SplObjectStorage();
+ $this->name = $name;
+ }
+
+ public function addValue(Value $value)
+ {
+ $this->values->attach($value);
}
/**
- * @return SplObjectStorage
+ * @return \SplObjectStorage
*/
- public function getValues()
+ public function getValues(): \SplObjectStorage
{
return $this->values;
}
- /**
- * @param ValueInterface $value
- *
- * @return $this
- */
- public function addValue(ValueInterface $value)
- {
- if (!$this->values->contains($value)) {
- $this->values->attach($value);
- }
-
- return $this;
- }
-
- /**
- * @param ValueInterface $value
- *
- * @return $this
- */
- public function removeValue(ValueInterface $value)
- {
- if ($this->values->contains($value)) {
- $this->values->detach($value);
- }
-
- return $this;
- }
-
- /**
- * @return string
- */
- public function getName()
+ public function __toString(): string
{
return $this->name;
}
-
- /**
- * @param string $name
- *
- * @return $this
- */
- public function setName($name)
- {
- $this->name = $name;
-
- return $this;
- }
}
diff --git a/More/EAV/Entity.php b/More/EAV/Entity.php
index ff26589..96d6583 100644
--- a/More/EAV/Entity.php
+++ b/More/EAV/Entity.php
@@ -2,15 +2,10 @@
namespace DesignPatterns\More\EAV;
-use SplObjectStorage;
-
-/**
- * Class Entity.
- */
-class Entity implements ValueAccessInterface
+class Entity
{
/**
- * @var SplObjectStorage
+ * @var \SplObjectStorage
*/
private $values;
@@ -19,64 +14,28 @@ class Entity implements ValueAccessInterface
*/
private $name;
- public function __construct()
- {
- $this->values = new SplObjectStorage();
- }
-
- /**
- * @return SplObjectStorage
- */
- public function getValues()
- {
- return $this->values;
- }
-
- /**
- * @param ValueInterface $value
- *
- * @return $this
- */
- public function addValue(ValueInterface $value)
- {
- if (!$this->values->contains($value)) {
- $this->values->attach($value);
- }
-
- return $this;
- }
-
- /**
- * @param ValueInterface $value
- *
- * @return $this
- */
- public function removeValue(ValueInterface $value)
- {
- if ($this->values->contains($value)) {
- $this->values->detach($value);
- }
-
- return $this;
- }
-
- /**
- * @return string
- */
- public function getName()
- {
- return $this->name;
- }
-
/**
* @param string $name
- *
- * @return $this
+ * @param Value[] $values
*/
- public function setName($name)
+ public function __construct(string $name, $values)
{
+ $this->values = new \SplObjectStorage();
$this->name = $name;
- return $this;
+ foreach ($values as $value) {
+ $this->values->attach($value);
+ }
+ }
+
+ public function __toString(): string
+ {
+ $text = [$this->name];
+
+ foreach ($this->values as $value) {
+ $text[] = (string) $value;
+ }
+
+ return join(', ', $text);
}
}
diff --git a/More/EAV/README.rst b/More/EAV/README.rst
index b8a8fbe..f23c71b 100644
--- a/More/EAV/README.rst
+++ b/More/EAV/README.rst
@@ -11,81 +11,6 @@ where the number of attributes (properties, parameters) that can be used
to describe them is potentially vast, but the number that will actually apply
to a given entity is relatively modest.
-Examples
---------
-
-Check full work example in `example.php`_ file.
-
-.. code-block:: php
-
- use DesignPatterns\More\EAV\Entity;
- use DesignPatterns\More\EAV\Attribute;
- use DesignPatterns\More\EAV\Value;
-
- // Create color attribute
- $color = (new Attribute())->setName('Color');
- // Create color values
- $colorSilver = (new Value($color))->setName('Silver');
- $colorGold = (new Value($color))->setName('Gold');
- $colorSpaceGrey = (new Value($color))->setName('Space Grey');
-
- // Create memory attribute
- $memory = (new Attribute())->setName('Memory');
- // Create memory values
- $memory4Gb = (new Value($memory))->setName('4GB');
- $memory8Gb = (new Value($memory))->setName('8GB');
- $memory16Gb = (new Value($memory))->setName('16GB');
-
- // Create storage attribute
- $storage = (new Attribute())->setName('Storage');
- // Create storage values
- $storage128Gb = (new Value($storage))->setName('128GB');
- $storage256Gb = (new Value($storage))->setName('256GB');
- $storage512Gb = (new Value($storage))->setName('512GB');
- $storage1Tb = (new Value($storage))->setName('1TB');
-
- // Create entities with specific values
- $mac = (new Entity())
- ->setName('MacBook')
- // colors
- ->addValue($colorSilver)
- ->addValue($colorGold)
- ->addValue($colorSpaceGrey)
- // memories
- ->addValue($memory8Gb)
- // storages
- ->addValue($storage256Gb)
- ->addValue($storage512Gb)
- ;
-
- $macAir = (new Entity())
- ->setName('MacBook Air')
- // colors
- ->addValue($colorSilver)
- // memories
- ->addValue($memory4Gb)
- ->addValue($memory8Gb)
- // storages
- ->addValue($storage128Gb)
- ->addValue($storage256Gb)
- ->addValue($storage512Gb)
- ;
-
- $macPro = (new Entity())
- ->setName('MacBook Pro')
- // colors
- ->addValue($colorSilver)
- // memories
- ->addValue($memory8Gb)
- ->addValue($memory16Gb)
- // storages
- ->addValue($storage128Gb)
- ->addValue($storage256Gb)
- ->addValue($storage512Gb)
- ->addValue($storage1Tb)
- ;
-
-
UML Diagram
-----------
@@ -98,28 +23,32 @@ Code
You can also find these code on `GitHub`_
+Entity.php
+
+.. literalinclude:: Entity.php
+ :language: php
+ :linenos:
+
+Attribute.php
+
+.. literalinclude:: Attribute.php
+ :language: php
+ :linenos:
+
+Value.php
+
+.. literalinclude:: Value.php
+ :language: php
+ :linenos:
+
Test
----
-Tests/EntityTest.php
+Tests/EAVTest.php
.. literalinclude:: Tests/EntityTest.php
:language: php
:linenos:
-Tests/AttributeTest.php
-
-.. literalinclude:: Tests/AttributeTest.php
- :language: php
- :linenos:
-
-Tests/ValueTest.php
-
-.. literalinclude:: Tests/ValueTest.php
- :language: php
- :linenos:
-
-
-.. _`example.php`: https://github.com/domnikl/DesignPatternsPHP/tree/master/More/EAV/example.php
.. _`GitHub`: https://github.com/domnikl/DesignPatternsPHP/tree/master/More/EAV
.. __: https://en.wikipedia.org/wiki/Entity–attribute–value_model
diff --git a/More/EAV/Tests/AttributeTest.php b/More/EAV/Tests/AttributeTest.php
deleted file mode 100644
index 4affe41..0000000
--- a/More/EAV/Tests/AttributeTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-assertInstanceOf('\DesignPatterns\More\EAV\Attribute', $attribute);
- }
-
- /**
- * @depends testCreationSuccess
- */
- public function testSetGetName()
- {
- $attribute = new Attribute();
- $attribute->setName('Color');
-
- $this->assertEquals('Color', $attribute->getName());
- }
-
- /**
- * @depends testCreationSuccess
- */
- public function testAddValue()
- {
- $attribute = new Attribute();
- $attribute->setName('Color');
-
- $colorSilver = new Value($attribute);
- $colorSilver->setName('Silver');
- $colorGold = new Value($attribute);
- $colorGold->setName('Gold');
-
- $this->assertTrue($attribute->getValues()->contains($colorSilver));
- $this->assertTrue($attribute->getValues()->contains($colorGold));
- }
-
- /**
- * @depends testAddValue
- */
- public function testRemoveValue()
- {
- $attribute = new Attribute();
- $attribute->setName('Color');
-
- $colorSilver = new Value($attribute);
- $colorSilver->setName('Silver');
- $colorGold = new Value($attribute);
- $colorGold->setName('Gold');
-
- $attribute->removeValue($colorSilver);
-
- $this->assertFalse($attribute->getValues()->contains($colorSilver));
- $this->assertTrue($attribute->getValues()->contains($colorGold));
- }
-}
diff --git a/More/EAV/Tests/EAVTest.php b/More/EAV/Tests/EAVTest.php
new file mode 100644
index 0000000..a210098
--- /dev/null
+++ b/More/EAV/Tests/EAVTest.php
@@ -0,0 +1,24 @@
+assertEquals('MacBook Pro, color: silver, color: black, memory: 8GB', (string) $entity);
+ }
+}
diff --git a/More/EAV/Tests/EntityTest.php b/More/EAV/Tests/EntityTest.php
deleted file mode 100644
index ecd6c40..0000000
--- a/More/EAV/Tests/EntityTest.php
+++ /dev/null
@@ -1,145 +0,0 @@
-setName($name);
-
- $this->assertEquals($name, $macBook->getName());
- }
-
- /**
- * @dataProvider valueProvider
- *
- * @var string
- * @var Value[] $values
- */
- public function testAddValue($name, array $values)
- {
- $macBook = new Entity();
- $macBook->setName($name);
-
- foreach ($values as $value) {
- $macBook->addValue($value);
- $this->assertTrue($macBook->getValues()->contains($value));
- }
-
- $this->assertCount(count($values), $macBook->getValues());
- }
-
- /**
- * @depends testAddValue
- * @dataProvider valueProvider
- *
- * @var string
- * @var Value[] $values
- */
- public function testRemoveValue($name, array $values)
- {
- $macBook = new Entity();
- $macBook->setName($name);
-
- foreach ($values as $value) {
- $macBook->addValue($value);
- }
- $macBook->removeValue($values[0]);
-
- $this->assertFalse($macBook->getValues()->contains($values[0]));
- unset($values[0]);
- $this->assertCount(count($values), $macBook->getValues());
- }
-
- /**
- * @return array
- */
- public function valueProvider()
- {
- // color attribute
- $color = new Attribute();
- $color->setName('Color');
- // color values
- $colorSilver = new Value($color);
- $colorSilver->setName('Silver');
- $colorGold = new Value($color);
- $colorGold->setName('Gold');
- $colorSpaceGrey = new Value($color);
- $colorSpaceGrey->setName('Space Grey');
-
- // memory attribute
- $memory = new Attribute();
- $memory->setName('Memory');
- // memory values
- $memory4Gb = new Value($memory);
- $memory4Gb->setName('4GB');
- $memory8Gb = new Value($memory);
- $memory8Gb->setName('8GB');
- $memory16Gb = new Value($memory);
- $memory16Gb->setName('16GB');
-
- // storage attribute
- $storage = new Attribute();
- $storage->setName('Storage');
- // storage values
- $storage128Gb = new Value($storage);
- $storage128Gb->setName('128GB');
- $storage256Gb = new Value($storage);
- $storage256Gb->setName('256GB');
- $storage512Gb = new Value($storage);
- $storage512Gb->setName('512GB');
- $storage1Tb = new Value($storage);
- $storage1Tb->setName('1TB');
-
- return array(
- array(
- 'MacBook',
- array(
- $colorSilver,
- $colorGold,
- $colorSpaceGrey,
- $memory8Gb,
- $storage256Gb,
- $storage512Gb,
- ),
- ),
- array(
- 'MacBook Air',
- array(
- $colorSilver,
- $memory4Gb,
- $memory8Gb,
- $storage128Gb,
- $storage256Gb,
- $storage512Gb,
- ),
- ),
- array(
- 'MacBook Pro',
- array(
- $colorSilver,
- $memory8Gb,
- $memory16Gb,
- $storage128Gb,
- $storage256Gb,
- $storage512Gb,
- $storage1Tb,
- ),
- ),
- );
- }
-}
diff --git a/More/EAV/Tests/ValueTest.php b/More/EAV/Tests/ValueTest.php
deleted file mode 100644
index f80f070..0000000
--- a/More/EAV/Tests/ValueTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-setName('Color');
-
- $value = new Value($attribute);
-
- $this->assertInstanceOf('\DesignPatterns\More\EAV\Value', $value);
- }
-
- public function testSetGetName()
- {
- $attribute = new Attribute();
- $attribute->setName('Color');
-
- $value = new Value($attribute);
- $value->setName('Silver');
-
- $this->assertEquals('Silver', $value->getName());
- }
-
- public function testSetGetAttribute()
- {
- $attribute = new Attribute();
- $attribute->setName('Color');
-
- $value = new Value($attribute);
- $value->setName('Silver');
- $this->assertSame($attribute, $value->getAttribute());
-
- $value->setAttribute($attribute);
- $this->assertSame($attribute, $value->getAttribute());
- }
-}
diff --git a/More/EAV/Value.php b/More/EAV/Value.php
index da4370f..f316728 100644
--- a/More/EAV/Value.php
+++ b/More/EAV/Value.php
@@ -2,10 +2,7 @@
namespace DesignPatterns\More\EAV;
-/**
- * Class Value.
- */
-class Value implements ValueInterface
+class Value
{
/**
* @var Attribute
@@ -17,53 +14,16 @@ class Value implements ValueInterface
*/
private $name;
- /**
- * @param Attribute $attribute
- */
- public function __construct(Attribute $attribute)
- {
- $this->setAttribute($attribute);
- }
-
- /**
- * @param Attribute $attribute
- *
- * @return $this
- */
- public function setAttribute(Attribute $attribute)
- {
- $this->attribute && $this->attribute->removeValue($this); // Remove value from current attribute
- $attribute->addValue($this); // Add value to new attribute
- $this->attribute = $attribute;
-
- return $this;
- }
-
- /**
- * @return Attribute
- */
- public function getAttribute()
- {
- return $this->attribute;
- }
-
- /**
- * @return string
- */
- public function getName()
- {
- return $this->name;
- }
-
- /**
- * @param string $name
- *
- * @return $this
- */
- public function setName($name)
+ public function __construct(Attribute $attribute, string $name)
{
$this->name = $name;
+ $this->attribute = $attribute;
- return $this;
+ $attribute->addValue($this);
+ }
+
+ public function __toString(): string
+ {
+ return sprintf('%s: %s', $this->attribute, $this->name);
}
}
diff --git a/More/EAV/ValueAccessInterface.php b/More/EAV/ValueAccessInterface.php
deleted file mode 100644
index dde67b2..0000000
--- a/More/EAV/ValueAccessInterface.php
+++ /dev/null
@@ -1,24 +0,0 @@
-setName('Color');
-// Create color values
-$colorSilver = (new Value($color))->setName('Silver');
-$colorGold = (new Value($color))->setName('Gold');
-$colorSpaceGrey = (new Value($color))->setName('Space Grey');
-
-// Create memory attribute
-$memory = (new Attribute())->setName('Memory');
-// Create memory values
-$memory4Gb = (new Value($memory))->setName('4GB');
-$memory8Gb = (new Value($memory))->setName('8GB');
-$memory16Gb = (new Value($memory))->setName('16GB');
-
-// Create storage attribute
-$storage = (new Attribute())->setName('Storage');
-// Create storage values
-$storage128Gb = (new Value($storage))->setName('128GB');
-$storage256Gb = (new Value($storage))->setName('256GB');
-$storage512Gb = (new Value($storage))->setName('512GB');
-$storage1Tb = (new Value($storage))->setName('1TB');
-
-// Create entities with specific values
-$mac = (new Entity())
- ->setName('MacBook')
- // colors
- ->addValue($colorSilver)
- ->addValue($colorGold)
- ->addValue($colorSpaceGrey)
- // memories
- ->addValue($memory8Gb)
- // storages
- ->addValue($storage256Gb)
- ->addValue($storage512Gb);
-
-$macAir = (new Entity())
- ->setName('MacBook Air')
- // colors
- ->addValue($colorSilver)
- // memories
- ->addValue($memory4Gb)
- ->addValue($memory8Gb)
- // storages
- ->addValue($storage128Gb)
- ->addValue($storage256Gb)
- ->addValue($storage512Gb);
-
-$macPro = (new Entity())
- ->setName('MacBook Pro')
- // colors
- ->addValue($colorSilver)
- // memories
- ->addValue($memory8Gb)
- ->addValue($memory16Gb)
- // storages
- ->addValue($storage128Gb)
- ->addValue($storage256Gb)
- ->addValue($storage512Gb)
- ->addValue($storage1Tb);
diff --git a/More/Repository/MemoryStorage.php b/More/Repository/MemoryStorage.php
index 44276d5..f0fd69a 100644
--- a/More/Repository/MemoryStorage.php
+++ b/More/Repository/MemoryStorage.php
@@ -2,50 +2,43 @@
namespace DesignPatterns\More\Repository;
-/**
- * Class MemoryStorage.
- */
-class MemoryStorage implements Storage
+class MemoryStorage
{
- private $data;
- private $lastId;
-
- public function __construct()
- {
- $this->data = array();
- $this->lastId = 0;
- }
+ /**
+ * @var array
+ */
+ private $data = [];
/**
- * {@inheritdoc}
+ * @var int
*/
- public function persist($data)
+ private $lastId = 0;
+
+ public function persist(array $data): int
{
- $this->data[++$this->lastId] = $data;
+ $this->lastId++;
+
+ $data['id'] = $this->lastId;
+ $this->data[$this->lastId] = $data;
return $this->lastId;
}
- /**
- * {@inheritdoc}
- */
- public function retrieve($id)
- {
- return isset($this->data[$id]) ? $this->data[$id] : null;
- }
-
- /**
- * {@inheritdoc}
- */
- public function delete($id)
+ public function retrieve(int $id): array
{
if (!isset($this->data[$id])) {
- return false;
+ throw new \OutOfRangeException(sprintf('No data found for ID %d', $id));
}
- $this->data[$id] = null;
- unset($this->data[$id]);
+ return $this->data[$id];
+ }
- return true;
+ public function delete(int $id)
+ {
+ if (!isset($this->data[$id])) {
+ throw new \OutOfRangeException(sprintf('No data found for ID %d', $id));
+ }
+
+ unset($this->data[$id]);
}
}
diff --git a/More/Repository/Post.php b/More/Repository/Post.php
index e9990b4..44aaefa 100644
--- a/More/Repository/Post.php
+++ b/More/Repository/Post.php
@@ -2,15 +2,10 @@
namespace DesignPatterns\More\Repository;
-/**
- * Post represents entity for some post that user left on the site.
- *
- * Class Post
- */
class Post
{
/**
- * @var int
+ * @var int|null
*/
private $id;
@@ -24,92 +19,43 @@ class Post
*/
private $text;
- /**
- * @var string
- */
- private $author;
+ public static function fromState(array $state): Post
+ {
+ return new self(
+ $state['id'],
+ $state['title'],
+ $state['text']
+ );
+ }
/**
- * @var \DateTime
+ * @param int|null $id
+ * @param string $text
+ * @param string $title
*/
- private $created;
+ public function __construct($id, string $title, string $text)
+ {
+ $this->id = $id;
+ $this->text = $text;
+ $this->title = $title;
+ }
- /**
- * @param int $id
- */
- public function setId($id)
+ public function setId(int $id)
{
$this->id = $id;
}
- /**
- * @return int
- */
- public function getId()
+ public function getId(): int
{
return $this->id;
}
- /**
- * @param string $author
- */
- public function setAuthor($author)
- {
- $this->author = $author;
- }
-
- /**
- * @return string
- */
- public function getAuthor()
- {
- return $this->author;
- }
-
- /**
- * @param \DateTime $created
- */
- public function setCreated($created)
- {
- $this->created = $created;
- }
-
- /**
- * @return \DateTime
- */
- public function getCreated()
- {
- return $this->created;
- }
-
- /**
- * @param string $text
- */
- public function setText($text)
- {
- $this->text = $text;
- }
-
- /**
- * @return string
- */
- public function getText()
+ public function getText(): string
{
return $this->text;
}
- /**
- * @param string $title
- */
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- /**
- * @return string
- */
- public function getTitle()
+ public function getTitle(): string
{
return $this->title;
}
diff --git a/More/Repository/PostRepository.php b/More/Repository/PostRepository.php
index e7687f3..991a398 100644
--- a/More/Repository/PostRepository.php
+++ b/More/Repository/PostRepository.php
@@ -3,80 +3,44 @@
namespace DesignPatterns\More\Repository;
/**
- * Repository for class Post
- * This class is between Entity layer(class Post) and access object layer(interface Storage).
+ * This class is situated between Entity layer (class Post) and access object layer (MemoryStorage).
*
* 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
*
* Repository also supports the objective of achieving a clean separation and one-way dependency
* between the domain and data mapping layers
- *
- * Class PostRepository
*/
class PostRepository
{
+ /**
+ * @var MemoryStorage
+ */
private $persistence;
- public function __construct(Storage $persistence)
+ public function __construct(MemoryStorage $persistence)
{
$this->persistence = $persistence;
}
- /**
- * Returns Post object by specified id.
- *
- * @param int $id
- *
- * @return Post|null
- */
- public function getById($id)
+ public function findById(int $id): Post
{
$arrayData = $this->persistence->retrieve($id);
+
if (is_null($arrayData)) {
- return;
+ throw new \InvalidArgumentException(sprintf('Post with ID %d does not exist'));
}
- $post = new Post();
- $post->setId($arrayData['id']);
- $post->setAuthor($arrayData['author']);
- $post->setCreated($arrayData['created']);
- $post->setText($arrayData['text']);
- $post->setTitle($arrayData['title']);
-
- return $post;
+ return Post::fromState($arrayData);
}
- /**
- * Save post object and populate it with id.
- *
- * @param Post $post
- *
- * @return Post
- */
public function save(Post $post)
{
- $id = $this->persistence->persist(array(
- 'author' => $post->getAuthor(),
- 'created' => $post->getCreated(),
+ $id = $this->persistence->persist([
'text' => $post->getText(),
'title' => $post->getTitle(),
- ));
+ ]);
$post->setId($id);
-
- return $post;
- }
-
- /**
- * Deletes specified Post object.
- *
- * @param Post $post
- *
- * @return bool
- */
- public function delete(Post $post)
- {
- return $this->persistence->delete($post->getId());
}
}
diff --git a/More/Repository/README.rst b/More/Repository/README.rst
index 48ac018..d8e1c72 100644
--- a/More/Repository/README.rst
+++ b/More/Repository/README.rst
@@ -43,12 +43,6 @@ PostRepository.php
:language: php
:linenos:
-Storage.php
-
-.. literalinclude:: Storage.php
- :language: php
- :linenos:
-
MemoryStorage.php
.. literalinclude:: MemoryStorage.php
@@ -58,4 +52,10 @@ MemoryStorage.php
Test
----
+Tests/RepositoryTest.php
+
+.. literalinclude:: Tests/RepositoryTest.php
+ :language: php
+ :linenos:
+
.. _`GitHub`: https://github.com/domnikl/DesignPatternsPHP/tree/master/More/Repository
diff --git a/More/Repository/Storage.php b/More/Repository/Storage.php
deleted file mode 100644
index a730f09..0000000
--- a/More/Repository/Storage.php
+++ /dev/null
@@ -1,42 +0,0 @@
-save($post);
+
+ $this->assertEquals(1, $post->getId());
+ $this->assertEquals($post->getId(), $repository->findById(1)->getId());
+ }
+}
diff --git a/More/ServiceLocator/DatabaseService.php b/More/ServiceLocator/DatabaseService.php
deleted file mode 100644
index 776ba2d..0000000
--- a/More/ServiceLocator/DatabaseService.php
+++ /dev/null
@@ -1,7 +0,0 @@
-services = array();
- $this->instantiated = array();
- $this->shared = array();
+ $this->services[$class] = $service;
+ $this->instantiated[$class] = $service;
+ $this->shared[$class] = $share;
}
/**
- * Registers a service with specific interface.
+ * instead of supplying a class here, you could also store a service for an interface
*
- * @param string $interface
- * @param string|object $service
- * @param bool $share
+ * @param string $class
+ * @param array $params
+ * @param bool $share
*/
- public function add($interface, $service, $share = true)
+ public function addClass(string $class, array $params, bool $share = true)
{
- /*
- * When you add a service, you should register it
- * with its interface or with a string that you can use
- * in the future even if you will change the service implementation.
- */
-
- if (is_object($service) && $share) {
- $this->instantiated[$interface] = $service;
- }
- $this->services[$interface] = (is_object($service) ? get_class($service) : $service);
- $this->shared[$interface] = $share;
+ $this->services[$class] = $params;
+ $this->shared[$class] = $share;
}
- /**
- * Checks if a service is registered.
- *
- * @param string $interface
- *
- * @return bool
- */
- public function has($interface)
+ public function has(string $interface): bool
{
return isset($this->services[$interface]) || isset($this->instantiated[$interface]);
}
/**
- * Gets the service registered for the interface.
+ * @param string $class
*
- * @param string $interface
- *
- * @return mixed
+ * @return object
*/
- public function get($interface)
+ public function get(string $class)
{
- // Retrieves the instance if it exists and it is shared
- if (isset($this->instantiated[$interface]) && $this->shared[$interface]) {
- return $this->instantiated[$interface];
+ if (isset($this->instantiated[$class]) && $this->shared[$class]) {
+ return $this->instantiated[$class];
}
- // otherwise gets the service registered.
- $service = $this->services[$interface];
+ $args = $this->services[$class];
- // You should check if the service class exists and
- // the class is instantiable.
+ switch (count($args)) {
+ case 0:
+ $object = new $class();
+ break;
+ case 1:
+ $object = new $class($args[0]);
+ break;
+ case 2:
+ $object = new $class($args[0], $args[1]);
+ break;
+ case 3:
+ $object = new $class($args[0], $args[1], $args[2]);
+ break;
+ default:
+ throw new \OutOfRangeException('Too many arguments given');
+ }
- // This example is a simple implementation, but
- // when you create a service, you can decide
- // if $service is a factory or a class.
- // By registering a factory you can create your services
- // using the DependencyInjection pattern.
-
- // ...
-
- // Creates the service object
- $object = new $service();
-
- // and saves it if the service must be shared.
- if ($this->shared[$interface]) {
- $this->instantiated[$interface] = $object;
+ if ($this->shared[$class]) {
+ $this->instantiated[$class] = $object;
}
return $object;
diff --git a/More/ServiceLocator/ServiceLocatorInterface.php b/More/ServiceLocator/ServiceLocatorInterface.php
deleted file mode 100644
index 3738624..0000000
--- a/More/ServiceLocator/ServiceLocatorInterface.php
+++ /dev/null
@@ -1,24 +0,0 @@
-serviceLocator = new ServiceLocator();
- $this->logService = new LogService();
- $this->databaseService = new DatabaseService();
}
public function testHasServices()
{
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\LogServiceInterface',
- $this->logService
- );
+ $this->serviceLocator->addInstance(LogService::class, new LogService());
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
- $this->databaseService
- );
-
- $this->assertTrue($this->serviceLocator->has('DesignPatterns\More\ServiceLocator\LogServiceInterface'));
- $this->assertTrue($this->serviceLocator->has('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface'));
-
- $this->assertFalse($this->serviceLocator->has('DesignPatterns\More\ServiceLocator\FakeServiceInterface'));
+ $this->assertTrue($this->serviceLocator->has(LogService::class));
+ $this->assertFalse($this->serviceLocator->has(self::class));
}
- public function testServicesWithObject()
+ public function testGetWillInstantiateLogServiceIfNoInstanceHasBeenCreatedYet()
{
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\LogServiceInterface',
- $this->logService
- );
+ $this->serviceLocator->addClass(LogService::class, []);
+ $logger = $this->serviceLocator->get(LogService::class);
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
- $this->databaseService
- );
-
- $this->assertSame(
- $this->logService,
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
- );
-
- $this->assertSame(
- $this->databaseService,
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
- );
- }
-
- public function testServicesWithClass()
- {
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\LogServiceInterface',
- get_class($this->logService)
- );
-
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
- get_class($this->databaseService)
- );
-
- $this->assertNotSame(
- $this->logService,
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
- );
-
- $this->assertInstanceOf(
- 'DesignPatterns\More\ServiceLocator\LogServiceInterface',
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
- );
-
- $this->assertNotSame(
- $this->databaseService,
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
- );
-
- $this->assertInstanceOf(
- 'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
- );
- }
-
- public function testServicesNotShared()
- {
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\LogServiceInterface',
- $this->logService,
- false
- );
-
- $this->serviceLocator->add(
- 'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
- $this->databaseService,
- false
- );
-
- $this->assertNotSame(
- $this->logService,
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
- );
-
- $this->assertInstanceOf(
- 'DesignPatterns\More\ServiceLocator\LogServiceInterface',
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\LogServiceInterface')
- );
-
- $this->assertNotSame(
- $this->databaseService,
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
- );
-
- $this->assertInstanceOf(
- 'DesignPatterns\More\ServiceLocator\DatabaseServiceInterface',
- $this->serviceLocator->get('DesignPatterns\More\ServiceLocator\DatabaseServiceInterface')
- );
+ $this->assertInstanceOf('DesignPatterns\More\ServiceLocator\LogService', $logger);
}
}
diff --git a/README.md b/README.md
index 9f3bef6..d21efab 100755
--- a/README.md
+++ b/README.md
@@ -92,7 +92,7 @@ The patterns can be structured in roughly three different categories. Please cli
## Contribute
-Please feel free to fork and extend existing or add your own examples and send a pull request with your changes!
+If you encounter any bugs or missing translations, please feel free to fork and send a pull request with your changes.
To establish a consistent code quality, please check your code using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) against [PSR2 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) using `./vendor/bin/phpcs -p --standard=PSR2 --ignore=vendor .`.
## License
diff --git a/README.rst b/README.rst
index bc32f77..bec81fe 100644
--- a/README.rst
+++ b/README.rst
@@ -34,9 +34,9 @@ Please click on **the title of every pattern's page** for a full explanation of
Contribute
----------
-Please feel free to fork and extend existing or add your own examples
-and send a pull request with your changes! To establish a consistent
-code quality, please check your code using
+If you encounter any bugs or missing translations, please feel free
+to fork and send a pull request with your changes.
+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 .``.
diff --git a/Structural/Adapter/Book.php b/Structural/Adapter/Book.php
index 6458beb..05d15cc 100644
--- a/Structural/Adapter/Book.php
+++ b/Structural/Adapter/Book.php
@@ -2,22 +2,25 @@
namespace DesignPatterns\Structural\Adapter;
-/**
- * Book is a concrete and standard paper book.
- */
-class Book implements PaperBookInterface
+class Book implements BookInterface
{
/**
- * {@inheritdoc}
+ * @var int
*/
+ private $page;
+
public function open()
{
+ $this->page = 1;
}
- /**
- * {@inheritdoc}
- */
public function turnPage()
{
+ $this->page++;
+ }
+
+ public function getPage(): int
+ {
+ return $this->page;
}
}
diff --git a/Structural/Adapter/BookInterface.php b/Structural/Adapter/BookInterface.php
new file mode 100644
index 0000000..6ebabf4
--- /dev/null
+++ b/Structural/Adapter/BookInterface.php
@@ -0,0 +1,12 @@
+eBook = $ebook;
+ $this->eBook = $eBook;
}
/**
@@ -30,14 +26,22 @@ class EBookAdapter implements PaperBookInterface
*/
public function open()
{
- $this->eBook->pressStart();
+ $this->eBook->unlock();
}
- /**
- * turns pages.
- */
public function turnPage()
{
$this->eBook->pressNext();
}
+
+ /**
+ * notice the adapted behavior here: EBookInterface::getPage() will return two integers, but BookInterface
+ * supports only a current page getter, so we adapt the behavior here
+ *
+ * @return int
+ */
+ public function getPage(): int
+ {
+ return $this->eBook->getPage()[0];
+ }
}
diff --git a/Structural/Adapter/EBookInterface.php b/Structural/Adapter/EBookInterface.php
index bd3dc26..bae5033 100644
--- a/Structural/Adapter/EBookInterface.php
+++ b/Structural/Adapter/EBookInterface.php
@@ -2,22 +2,16 @@
namespace DesignPatterns\Structural\Adapter;
-/**
- * EBookInterface is a contract for an electronic book.
- */
interface EBookInterface
{
- /**
- * go to next page.
- *
- * @return mixed
- */
+ public function unlock();
+
public function pressNext();
/**
- * start the book.
+ * returns current page and total number of pages, like [10, 100] is page 10 of 100
*
- * @return mixed
+ * @return int[]
*/
- public function pressStart();
+ public function getPage(): array;
}
diff --git a/Structural/Adapter/Kindle.php b/Structural/Adapter/Kindle.php
index 06d4489..a150767 100644
--- a/Structural/Adapter/Kindle.php
+++ b/Structural/Adapter/Kindle.php
@@ -3,21 +3,37 @@
namespace DesignPatterns\Structural\Adapter;
/**
- * Kindle is a concrete electronic book.
+ * this is the adapted class. In production code, this could be a class from another package, some vendor code.
+ * Notice that it uses another naming scheme and the implementation does something similar but in another way
*/
class Kindle implements EBookInterface
{
/**
- * {@inheritdoc}
+ * @var int
*/
+ private $page = 1;
+
+ /**
+ * @var int
+ */
+ private $totalPages = 100;
+
public function pressNext()
+ {
+ $this->page++;
+ }
+
+ public function unlock()
{
}
/**
- * {@inheritdoc}
+ * returns current page and total number of pages, like [10, 100] is page 10 of 100
+ *
+ * @return int[]
*/
- public function pressStart()
+ public function getPage(): array
{
+ return [$this->page, $this->totalPages];
}
}
diff --git a/Structural/Adapter/PaperBookInterface.php b/Structural/Adapter/PaperBookInterface.php
deleted file mode 100644
index 4b62149..0000000
--- a/Structural/Adapter/PaperBookInterface.php
+++ /dev/null
@@ -1,23 +0,0 @@
-open();
+ $book->turnPage();
+
+ $this->assertEquals(2, $book->getPage());
}
- /**
- * This client only knows paper book but surprise, surprise, the second book
- * is in fact an electronic book, but both work the same way.
- *
- * @param PaperBookInterface $book
- *
- * @dataProvider getBook
- */
- public function testIAmAnOldClient(PaperBookInterface $book)
+ public function testCanTurnPageOnKindleLikeInANormalBook()
{
- $this->assertTrue(method_exists($book, 'open'));
- $this->assertTrue(method_exists($book, 'turnPage'));
+ $kindle = new Kindle();
+ $book = new EBookAdapter($kindle);
+
+ $book->open();
+ $book->turnPage();
+
+ $this->assertEquals(2, $book->getPage());
}
}
diff --git a/Structural/Bridge/Assemble.php b/Structural/Bridge/Assemble.php
deleted file mode 100644
index 9b9d114..0000000
--- a/Structural/Bridge/Assemble.php
+++ /dev/null
@@ -1,11 +0,0 @@
-workShop1->work();
- $this->workShop2->work();
- }
-}
diff --git a/Structural/Bridge/FormatterInterface.php b/Structural/Bridge/FormatterInterface.php
new file mode 100644
index 0000000..d59a344
--- /dev/null
+++ b/Structural/Bridge/FormatterInterface.php
@@ -0,0 +1,8 @@
+implementation->format('Hello World');
+ }
+}
diff --git a/Structural/Bridge/HtmlFormatter.php b/Structural/Bridge/HtmlFormatter.php
new file mode 100644
index 0000000..79e49b5
--- /dev/null
+++ b/Structural/Bridge/HtmlFormatter.php
@@ -0,0 +1,11 @@
+%s
', $text);
+ }
+}
diff --git a/Structural/Bridge/Motorcycle.php b/Structural/Bridge/Motorcycle.php
deleted file mode 100644
index f008785..0000000
--- a/Structural/Bridge/Motorcycle.php
+++ /dev/null
@@ -1,21 +0,0 @@
-workShop1->work();
- $this->workShop2->work();
- }
-}
diff --git a/Structural/Bridge/PlainTextFormatter.php b/Structural/Bridge/PlainTextFormatter.php
new file mode 100644
index 0000000..af46283
--- /dev/null
+++ b/Structural/Bridge/PlainTextFormatter.php
@@ -0,0 +1,11 @@
+implementation = $printer;
+ }
+
+ /**
+ * @param FormatterInterface $printer
+ */
+ public function setImplementation(FormatterInterface $printer)
+ {
+ $this->implementation = $printer;
+ }
+
+ abstract public function get();
+}
diff --git a/Structural/Bridge/Tests/BridgeTest.php b/Structural/Bridge/Tests/BridgeTest.php
index 0a89a86..2f70e87 100644
--- a/Structural/Bridge/Tests/BridgeTest.php
+++ b/Structural/Bridge/Tests/BridgeTest.php
@@ -2,24 +2,19 @@
namespace DesignPatterns\Structural\Bridge\Tests;
-use DesignPatterns\Structural\Bridge\Assemble;
-use DesignPatterns\Structural\Bridge\Car;
-use DesignPatterns\Structural\Bridge\Motorcycle;
-use DesignPatterns\Structural\Bridge\Produce;
+use DesignPatterns\Structural\Bridge\HelloWorldService;
+use DesignPatterns\Structural\Bridge\HtmlFormatter;
+use DesignPatterns\Structural\Bridge\PlainTextFormatter;
class BridgeTest extends \PHPUnit_Framework_TestCase
{
- public function testCar()
+ public function testCanPrintUsingThePlainTextPrinter()
{
- $vehicle = new Car(new Produce(), new Assemble());
- $this->expectOutputString('Car Produced Assembled');
- $vehicle->manufacture();
- }
+ $service = new HelloWorldService(new PlainTextFormatter());
+ $this->assertEquals('Hello World', $service->get());
- public function testMotorcycle()
- {
- $vehicle = new Motorcycle(new Produce(), new Assemble());
- $this->expectOutputString('Motorcycle Produced Assembled');
- $vehicle->manufacture();
+ // now change the implemenation and use the HtmlFormatter instead
+ $service->setImplementation(new HtmlFormatter());
+ $this->assertEquals('Hello World
', $service->get());
}
}
diff --git a/Structural/Bridge/Vehicle.php b/Structural/Bridge/Vehicle.php
deleted file mode 100644
index 80311e1..0000000
--- a/Structural/Bridge/Vehicle.php
+++ /dev/null
@@ -1,20 +0,0 @@
-workShop1 = $workShop1;
- $this->workShop2 = $workShop2;
- }
-
- abstract public function manufacture();
-}
diff --git a/Structural/Bridge/Workshop.php b/Structural/Bridge/Workshop.php
deleted file mode 100644
index 9cb26c5..0000000
--- a/Structural/Bridge/Workshop.php
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- PHP
- \DesignPatterns\Structural\Bridge\Vehicle
-
- \DesignPatterns\Structural\Bridge\Workshop
- \DesignPatterns\Structural\Bridge\Car
- \DesignPatterns\Structural\Bridge\Produce
- \DesignPatterns\Structural\Bridge\Motorcycle
- \DesignPatterns\Structural\Bridge\Assemble
- \DesignPatterns\Structural\Bridge\Vehicle
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Structural\Bridge\HtmlFormatter
+
+ \DesignPatterns\Structural\Bridge\PlainTextFormatter
+ \DesignPatterns\Structural\Bridge\FormatterInterface
+ \DesignPatterns\Structural\Bridge\Service
+ \DesignPatterns\Structural\Bridge\HelloWorldService
+ \DesignPatterns\Structural\Bridge\HtmlFormatter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fields
+ Constants
+ Methods
+
+ private
+
+
diff --git a/Structural/Bridge/uml/uml.png b/Structural/Bridge/uml/uml.png
index df6ce3c..6de5b02 100644
Binary files a/Structural/Bridge/uml/uml.png and b/Structural/Bridge/uml/uml.png differ
diff --git a/Structural/Bridge/uml/uml.svg b/Structural/Bridge/uml/uml.svg
index 189a7dc..9483eab 100644
--- a/Structural/Bridge/uml/uml.svg
+++ b/Structural/Bridge/uml/uml.svg
@@ -1,451 +1,661 @@
-
+
diff --git a/Structural/Composite/Form.php b/Structural/Composite/Form.php
index 42f1bc1..6db2d3f 100644
--- a/Structural/Composite/Form.php
+++ b/Structural/Composite/Form.php
@@ -6,12 +6,12 @@ namespace DesignPatterns\Structural\Composite;
* The composite node MUST extend the component contract. This is mandatory for building
* a tree of components.
*/
-class Form extends FormElement
+class Form implements RenderableInterface
{
/**
- * @var array|FormElement[]
+ * @var RenderableInterface[]
*/
- protected $elements;
+ private $elements;
/**
* runs through all elements and calls render() on them, then returns the complete representation
@@ -19,25 +19,25 @@ class Form extends FormElement
*
* from the outside, one will not see this and the form will act like a single object instance
*
- * @param int $indent
- *
* @return string
*/
- public function render($indent = 0)
+ public function render(): string
{
- $formCode = '';
+ $formCode = '';
+
return $formCode;
}
/**
- * @param FormElement $element
+ * @param RenderableInterface $element
*/
- public function addElement(FormElement $element)
+ public function addElement(RenderableInterface $element)
{
$this->elements[] = $element;
}
diff --git a/Structural/Composite/FormElement.php b/Structural/Composite/FormElement.php
deleted file mode 100644
index 0055aee..0000000
--- a/Structural/Composite/FormElement.php
+++ /dev/null
@@ -1,18 +0,0 @@
-';
+ return '';
}
}
diff --git a/Structural/Composite/README.rst b/Structural/Composite/README.rst
index 66d8f16..1e64739 100644
--- a/Structural/Composite/README.rst
+++ b/Structural/Composite/README.rst
@@ -28,9 +28,9 @@ Code
You can also find these code on `GitHub`_
-FormElement.php
+RenderableInterface.php
-.. literalinclude:: FormElement.php
+.. literalinclude:: RenderableInterface.php
:language: php
:linenos:
diff --git a/Structural/Composite/RenderableInterface.php b/Structural/Composite/RenderableInterface.php
new file mode 100644
index 0000000..444f707
--- /dev/null
+++ b/Structural/Composite/RenderableInterface.php
@@ -0,0 +1,8 @@
+addElement(new Composite\TextElement());
+ $form->addElement(new Composite\TextElement('Email:'));
$form->addElement(new Composite\InputElement());
$embed = new Composite\Form();
- $embed->addElement(new Composite\TextElement());
+ $embed->addElement(new Composite\TextElement('Password:'));
$embed->addElement(new Composite\InputElement());
- $form->addElement($embed); // here we have a embedded form (like SF2 does)
+ $form->addElement($embed);
- $this->assertRegExp('#^\s{4}#m', $form->render());
- }
-
- /**
- * The point of this pattern, a Composite must inherit from the node
- * if you want to build trees.
- */
- public function testFormImplementsFormEelement()
- {
- $className = 'DesignPatterns\Structural\Composite\Form';
- $abstractName = 'DesignPatterns\Structural\Composite\FormElement';
- $this->assertTrue(is_subclass_of($className, $abstractName));
+ $this->assertEquals(
+ '',
+ $form->render()
+ );
}
}
diff --git a/Structural/Composite/TextElement.php b/Structural/Composite/TextElement.php
index 48b33ba..55a868b 100644
--- a/Structural/Composite/TextElement.php
+++ b/Structural/Composite/TextElement.php
@@ -2,20 +2,20 @@
namespace DesignPatterns\Structural\Composite;
-/**
- * Class TextElement.
- */
-class TextElement extends FormElement
+class TextElement implements RenderableInterface
{
/**
- * renders the text element.
- *
- * @param int $indent
- *
- * @return mixed|string
+ * @var string
*/
- public function render($indent = 0)
+ private $text;
+
+ public function __construct(string $text)
{
- return str_repeat(' ', $indent).'this is a text element';
+ $this->text = $text;
+ }
+
+ public function render(): string
+ {
+ return $this->text;
}
}
diff --git a/Structural/Composite/uml/Composite.uml b/Structural/Composite/uml/Composite.uml
index 326dfd3..a874b2f 100644
--- a/Structural/Composite/uml/Composite.uml
+++ b/Structural/Composite/uml/Composite.uml
@@ -1,42 +1,39 @@
-
-
- PHP
- \DesignPatterns\Structural\Composite\InputElement
-
- \DesignPatterns\Structural\Composite\TextElement
- \DesignPatterns\Structural\Composite\FormElement
- \DesignPatterns\Structural\Composite\InputElement
- \DesignPatterns\Structural\Composite\Form
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- \DesignPatterns\Structural\Composite\InputElement
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Structural\Composite\Form
+
+ \DesignPatterns\Structural\Composite\RenderableInterface
+ \DesignPatterns\Structural\Composite\InputElement
+ \DesignPatterns\Structural\Composite\TextElement
+ \DesignPatterns\Structural\Composite\Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Methods
+ Constants
+ Fields
+
+ private
+
+
diff --git a/Structural/Composite/uml/uml.png b/Structural/Composite/uml/uml.png
index 5fed73d..1d8ad76 100644
Binary files a/Structural/Composite/uml/uml.png and b/Structural/Composite/uml/uml.png differ
diff --git a/Structural/Composite/uml/uml.svg b/Structural/Composite/uml/uml.svg
index c587818..ac9c76e 100644
--- a/Structural/Composite/uml/uml.svg
+++ b/Structural/Composite/uml/uml.svg
@@ -1,284 +1,606 @@
-
+
diff --git a/Structural/DataMapper/README.rst b/Structural/DataMapper/README.rst
index 5180243..70dd17a 100644
--- a/Structural/DataMapper/README.rst
+++ b/Structural/DataMapper/README.rst
@@ -47,6 +47,12 @@ UserMapper.php
:language: php
:linenos:
+StorageAdapter.php
+
+.. literalinclude:: StorageAdapter.php
+ :language: php
+ :linenos:
+
Test
----
diff --git a/Structural/DataMapper/StorageAdapter.php b/Structural/DataMapper/StorageAdapter.php
new file mode 100644
index 0000000..2c9caef
--- /dev/null
+++ b/Structural/DataMapper/StorageAdapter.php
@@ -0,0 +1,30 @@
+data = $data;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return array|null
+ */
+ public function find(int $id)
+ {
+ if (isset($this->data[$id])) {
+ return $this->data[$id];
+ }
+
+ return null;
+ }
+}
diff --git a/Structural/DataMapper/Tests/DataMapperTest.php b/Structural/DataMapper/Tests/DataMapperTest.php
index 551c11e..402ed3f 100644
--- a/Structural/DataMapper/Tests/DataMapperTest.php
+++ b/Structural/DataMapper/Tests/DataMapperTest.php
@@ -2,109 +2,29 @@
namespace DesignPatterns\Structural\DataMapper\Tests;
-use DesignPatterns\Structural\DataMapper\User;
+use DesignPatterns\Structural\DataMapper\StorageAdapter;
use DesignPatterns\Structural\DataMapper\UserMapper;
-/**
- * UserMapperTest tests the datamapper pattern.
- */
class DataMapperTest extends \PHPUnit_Framework_TestCase
{
- /**
- * @var UserMapper
- */
- protected $mapper;
-
- /**
- * @var DBAL
- */
- protected $dbal;
-
- protected function setUp()
+ public function testCanMapUserFromStorage()
{
- $this->dbal = $this->getMockBuilder('DesignPatterns\Structural\DataMapper\DBAL')
- ->disableAutoload()
- ->setMethods(array('insert', 'update', 'find', 'findAll'))
- ->getMock();
+ $storage = new StorageAdapter([1 => ['username' => 'domnikl', 'email' => 'liebler.dominik@gmail.com']]);
+ $mapper = new UserMapper($storage);
- $this->mapper = new UserMapper($this->dbal);
- }
+ $user = $mapper->findById(1);
- public function getNewUser()
- {
- return array(array(new User(null, 'Odysseus', 'Odysseus@ithaca.gr')));
- }
-
- public function getExistingUser()
- {
- return array(array(new User(1, 'Odysseus', 'Odysseus@ithaca.gr')));
- }
-
- /**
- * @dataProvider getNewUser
- */
- public function testPersistNew(User $user)
- {
- $this->dbal->expects($this->once())
- ->method('insert');
- $this->mapper->save($user);
- }
-
- /**
- * @dataProvider getExistingUser
- */
- public function testPersistExisting(User $user)
- {
- $this->dbal->expects($this->once())
- ->method('update');
- $this->mapper->save($user);
- }
-
- /**
- * @dataProvider getExistingUser
- */
- public function testRestoreOne(User $existing)
- {
- $row = array(
- 'userid' => 1,
- 'username' => 'Odysseus',
- 'email' => 'Odysseus@ithaca.gr',
- );
- $rows = new \ArrayIterator(array($row));
- $this->dbal->expects($this->once())
- ->method('find')
- ->with(1)
- ->will($this->returnValue($rows));
-
- $user = $this->mapper->findById(1);
- $this->assertEquals($existing, $user);
- }
-
- /**
- * @dataProvider getExistingUser
- */
- public function testRestoreMulti(User $existing)
- {
- $rows = array(array('userid' => 1, 'username' => 'Odysseus', 'email' => 'Odysseus@ithaca.gr'));
- $this->dbal->expects($this->once())
- ->method('findAll')
- ->will($this->returnValue($rows));
-
- $user = $this->mapper->findAll();
- $this->assertEquals(array($existing), $user);
+ $this->assertInstanceOf('DesignPatterns\Structural\DataMapper\User', $user);
}
/**
* @expectedException \InvalidArgumentException
- * @expectedExceptionMessage User #404 not found
*/
- public function testNotFound()
+ public function testWillNotMapInvalidData()
{
- $this->dbal->expects($this->once())
- ->method('find')
- ->with(404)
- ->will($this->returnValue(array()));
+ $storage = new StorageAdapter([]);
+ $mapper = new UserMapper($storage);
- $user = $this->mapper->findById(404);
+ $mapper->findById(1);
}
}
diff --git a/Structural/DataMapper/User.php b/Structural/DataMapper/User.php
index f3f7661..8a6fd04 100644
--- a/Structural/DataMapper/User.php
+++ b/Structural/DataMapper/User.php
@@ -2,56 +2,34 @@
namespace DesignPatterns\Structural\DataMapper;
-/**
- * DataMapper pattern.
- *
- * This is our representation of a DataBase record in the memory (Entity)
- *
- * Validation would also go in this object
- */
class User
{
/**
- * @var int
+ * @var string
*/
- protected $userId;
+ private $username;
/**
* @var string
*/
- protected $username;
+ private $email;
- /**
- * @var string
- */
- protected $email;
-
- /**
- * @param null $id
- * @param null $username
- * @param null $email
- */
- public function __construct($id = null, $username = null, $email = null)
+ public static function fromState(array $state): User
{
- $this->setUserID($id);
- $this->setUsername($username);
- $this->setEmail($email);
+ // validate state before accessing keys!
+
+ return new self(
+ $state['username'],
+ $state['email']
+ );
}
- /**
- * @return int
- */
- public function getUserId()
+ public function __construct(string $username, string $email)
{
- return $this->userId;
- }
+ // validate parameters before setting them!
- /**
- * @param int $userId
- */
- public function setUserID($userId)
- {
- $this->userId = $userId;
+ $this->username = $username;
+ $this->email = $email;
}
/**
@@ -62,14 +40,6 @@ class User
return $this->username;
}
- /**
- * @param string $username
- */
- public function setUsername($username)
- {
- $this->username = $username;
- }
-
/**
* @return string
*/
@@ -77,12 +47,4 @@ class User
{
return $this->email;
}
-
- /**
- * @param string $email
- */
- public function setEmail($email)
- {
- $this->email = $email;
- }
}
diff --git a/Structural/DataMapper/UserMapper.php b/Structural/DataMapper/UserMapper.php
index f147063..415246a 100644
--- a/Structural/DataMapper/UserMapper.php
+++ b/Structural/DataMapper/UserMapper.php
@@ -2,107 +2,44 @@
namespace DesignPatterns\Structural\DataMapper;
-/**
- * class UserMapper.
- */
class UserMapper
{
/**
- * @var DBAL
+ * @var StorageAdapter
*/
- protected $adapter;
+ private $adapter;
/**
- * @param DBAL $dbLayer
+ * @param StorageAdapter $storage
*/
- public function __construct(DBAL $dbLayer)
+ public function __construct(StorageAdapter $storage)
{
- $this->adapter = $dbLayer;
+ $this->adapter = $storage;
}
/**
- * saves a user object from memory to Database.
- *
- * @param User $user
- *
- * @return bool
- */
- public function save(User $user)
- {
- /* $data keys should correspond to valid Table columns on the Database */
- $data = array(
- 'userid' => $user->getUserId(),
- 'username' => $user->getUsername(),
- 'email' => $user->getEmail(),
- );
-
- /* if no ID specified create new user else update the one in the Database */
- if (null === ($id = $user->getUserId())) {
- unset($data['userid']);
- $this->adapter->insert($data);
-
- return true;
- } else {
- $this->adapter->update($data, array('userid = ?' => $id));
-
- return true;
- }
- }
-
- /**
- * finds a user from Database based on ID and returns a User object located
- * in memory.
+ * finds a user from storage based on ID and returns a User object located
+ * in memory. Normally this kind of logic will be implemented using the Repository pattern.
+ * However the important part is in mapRowToUser() below, that will create a business object from the
+ * data fetched from storage
*
* @param int $id
*
- * @throws \InvalidArgumentException
- *
* @return User
*/
- public function findById($id)
+ public function findById(int $id): User
{
$result = $this->adapter->find($id);
- if (0 == count($result)) {
+ if ($result === null) {
throw new \InvalidArgumentException("User #$id not found");
}
- $row = $result->current();
- return $this->mapObject($row);
+ return $this->mapRowToUser($result);
}
- /**
- * fetches an array from Database and returns an array of User objects
- * located in memory.
- *
- * @return array
- */
- public function findAll()
+ private function mapRowToUser(array $row): User
{
- $resultSet = $this->adapter->findAll();
- $entries = array();
-
- foreach ($resultSet as $row) {
- $entries[] = $this->mapObject($row);
- }
-
- return $entries;
- }
-
- /**
- * Maps a table row to an object.
- *
- * @param array $row
- *
- * @return User
- */
- protected function mapObject(array $row)
- {
- $entry = new User();
- $entry->setUserID($row['userid']);
- $entry->setUsername($row['username']);
- $entry->setEmail($row['email']);
-
- return $entry;
+ return User::fromState($row);
}
}
diff --git a/Structural/DataMapper/uml/DataMapper.uml b/Structural/DataMapper/uml/DataMapper.uml
index 697e1f9..720faf8 100644
--- a/Structural/DataMapper/uml/DataMapper.uml
+++ b/Structural/DataMapper/uml/DataMapper.uml
@@ -1,23 +1,23 @@
-
-
- PHP
- \DesignPatterns\Structural\DataMapper\User
-
- \DesignPatterns\Structural\DataMapper\User
- \DesignPatterns\Structural\DataMapper\UserMapper
-
-
-
-
-
- \DesignPatterns\Structural\DataMapper\User
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Structural\DataMapper\StorageAdapter
+
+ \DesignPatterns\Structural\DataMapper\UserMapper
+ \DesignPatterns\Structural\DataMapper\StorageAdapter
+ \DesignPatterns\Structural\DataMapper\User
+
+
+
+
+
+ \DesignPatterns\Structural\DataMapper\StorageAdapter
+
+
+ Methods
+ Fields
+ Constants
+
+ private
+
+
diff --git a/Structural/DataMapper/uml/uml.png b/Structural/DataMapper/uml/uml.png
index 97cd26a..d2f59ea 100644
Binary files a/Structural/DataMapper/uml/uml.png and b/Structural/DataMapper/uml/uml.png differ
diff --git a/Structural/DataMapper/uml/uml.svg b/Structural/DataMapper/uml/uml.svg
index d65a9c9..612769f 100644
--- a/Structural/DataMapper/uml/uml.svg
+++ b/Structural/DataMapper/uml/uml.svg
@@ -1,403 +1,680 @@
-
+
diff --git a/Structural/Decorator/Decorator.php b/Structural/Decorator/Decorator.php
deleted file mode 100644
index 0b8fde3..0000000
--- a/Structural/Decorator/Decorator.php
+++ /dev/null
@@ -1,31 +0,0 @@
-wrapped = $wrappable;
- }
-}
diff --git a/Structural/Decorator/JsonRenderer.php b/Structural/Decorator/JsonRenderer.php
new file mode 100644
index 0000000..9cc4066
--- /dev/null
+++ b/Structural/Decorator/JsonRenderer.php
@@ -0,0 +1,11 @@
+wrapped->renderData());
+ }
+}
diff --git a/Structural/Decorator/README.rst b/Structural/Decorator/README.rst
index b57a608..c854787 100644
--- a/Structural/Decorator/README.rst
+++ b/Structural/Decorator/README.rst
@@ -25,9 +25,9 @@ Code
You can also find these code on `GitHub`_
-RendererInterface.php
+RenderableInterface.php
-.. literalinclude:: RendererInterface.php
+.. literalinclude:: RenderableInterface.php
:language: php
:linenos:
@@ -37,21 +37,21 @@ Webservice.php
:language: php
:linenos:
-Decorator.php
+RendererDecorator.php
-.. literalinclude:: Decorator.php
+.. literalinclude:: RendererDecorator.php
:language: php
:linenos:
-RenderInXml.php
+XmlRenderer.php
-.. literalinclude:: RenderInXml.php
+.. literalinclude:: XmlRenderer.php
:language: php
:linenos:
-RenderInJson.php
+JsonRenderer.php
-.. literalinclude:: RenderInJson.php
+.. literalinclude:: JsonRenderer.php
:language: php
:linenos:
diff --git a/Structural/Decorator/RenderInJson.php b/Structural/Decorator/RenderInJson.php
deleted file mode 100644
index fb9a71e..0000000
--- a/Structural/Decorator/RenderInJson.php
+++ /dev/null
@@ -1,19 +0,0 @@
-wrapped->renderData());
- }
-}
diff --git a/Structural/Decorator/RenderInXml.php b/Structural/Decorator/RenderInXml.php
deleted file mode 100644
index f203d53..0000000
--- a/Structural/Decorator/RenderInXml.php
+++ /dev/null
@@ -1,27 +0,0 @@
-wrapped->renderData() as $key => $val) {
- $doc->appendChild($doc->createElement($key, $val));
- }
-
- return $doc->saveXML();
- }
-}
diff --git a/Structural/Decorator/RenderableInterface.php b/Structural/Decorator/RenderableInterface.php
new file mode 100644
index 0000000..07e11d1
--- /dev/null
+++ b/Structural/Decorator/RenderableInterface.php
@@ -0,0 +1,8 @@
+wrapped = $renderer;
+ }
+}
diff --git a/Structural/Decorator/RendererInterface.php b/Structural/Decorator/RendererInterface.php
deleted file mode 100644
index 73152b9..0000000
--- a/Structural/Decorator/RendererInterface.php
+++ /dev/null
@@ -1,16 +0,0 @@
-service = new Decorator\Webservice(array('foo' => 'bar'));
+ $this->service = new Decorator\Webservice('foobar');
}
public function testJsonDecorator()
{
- // Wrap service with a JSON decorator for renderers
- $service = new Decorator\RenderInJson($this->service);
- // Our Renderer will now output JSON instead of an array
- $this->assertEquals('{"foo":"bar"}', $service->renderData());
+ $service = new Decorator\JsonRenderer($this->service);
+
+ $this->assertEquals('"foobar"', $service->renderData());
}
public function testXmlDecorator()
{
- // Wrap service with a XML decorator for renderers
- $service = new Decorator\RenderInXml($this->service);
- // Our Renderer will now output XML instead of an array
- $xml = 'bar';
- $this->assertXmlStringEqualsXmlString($xml, $service->renderData());
- }
+ $service = new Decorator\XmlRenderer($this->service);
- /**
- * The first key-point of this pattern :.
- */
- public function testDecoratorMustImplementsRenderer()
- {
- $className = 'DesignPatterns\Structural\Decorator\Decorator';
- $interfaceName = 'DesignPatterns\Structural\Decorator\RendererInterface';
- $this->assertTrue(is_subclass_of($className, $interfaceName));
- }
-
- /**
- * Second key-point of this pattern : the decorator is type-hinted.
- *
- * @expectedException \PHPUnit_Framework_Error
- */
- public function testDecoratorTypeHinted()
- {
- if (version_compare(PHP_VERSION, '7', '>=')) {
- throw new \PHPUnit_Framework_Error('Skip test for PHP 7', 0, __FILE__, __LINE__);
- }
-
- $this->getMockForAbstractClass('DesignPatterns\Structural\Decorator\Decorator', array(new \stdClass()));
- }
-
- /**
- * Second key-point of this pattern : the decorator is type-hinted.
- *
- * @requires PHP 7
- * @expectedException TypeError
- */
- public function testDecoratorTypeHintedForPhp7()
- {
- $this->getMockForAbstractClass('DesignPatterns\Structural\Decorator\Decorator', array(new \stdClass()));
- }
-
- /**
- * The decorator implements and wraps the same interface.
- */
- public function testDecoratorOnlyAcceptRenderer()
- {
- $mock = $this->getMock('DesignPatterns\Structural\Decorator\RendererInterface');
- $dec = $this->getMockForAbstractClass('DesignPatterns\Structural\Decorator\Decorator', array($mock));
- $this->assertNotNull($dec);
+ $this->assertXmlStringEqualsXmlString('foobar', $service->renderData());
}
}
diff --git a/Structural/Decorator/Webservice.php b/Structural/Decorator/Webservice.php
index e2bcc69..6715a22 100644
--- a/Structural/Decorator/Webservice.php
+++ b/Structural/Decorator/Webservice.php
@@ -2,28 +2,19 @@
namespace DesignPatterns\Structural\Decorator;
-/**
- * Class Webservice.
- */
-class Webservice implements RendererInterface
+class Webservice implements RenderableInterface
{
/**
- * @var mixed
+ * @var string
*/
- protected $data;
+ private $data;
- /**
- * @param mixed $data
- */
- public function __construct($data)
+ public function __construct(string $data)
{
$this->data = $data;
}
- /**
- * @return string
- */
- public function renderData()
+ public function renderData(): string
{
return $this->data;
}
diff --git a/Structural/Decorator/XmlRenderer.php b/Structural/Decorator/XmlRenderer.php
new file mode 100644
index 0000000..012da47
--- /dev/null
+++ b/Structural/Decorator/XmlRenderer.php
@@ -0,0 +1,15 @@
+wrapped->renderData();
+ $doc->appendChild($doc->createElement('content', $data));
+
+ return $doc->saveXML();
+ }
+}
diff --git a/Structural/DependencyInjection/AbstractConfig.php b/Structural/DependencyInjection/AbstractConfig.php
deleted file mode 100644
index f2da4dc..0000000
--- a/Structural/DependencyInjection/AbstractConfig.php
+++ /dev/null
@@ -1,19 +0,0 @@
-storage = $storage;
- }
-}
diff --git a/Structural/DependencyInjection/ArrayConfig.php b/Structural/DependencyInjection/ArrayConfig.php
deleted file mode 100644
index f26c089..0000000
--- a/Structural/DependencyInjection/ArrayConfig.php
+++ /dev/null
@@ -1,39 +0,0 @@
-storage[$key])) {
- return $this->storage[$key];
- }
-
- return $default;
- }
-
- /**
- * Set parameter.
- *
- * @param string|int $key
- * @param mixed $value
- */
- public function set($key, $value)
- {
- $this->storage[$key] = $value;
- }
-}
diff --git a/Structural/DependencyInjection/Connection.php b/Structural/DependencyInjection/Connection.php
deleted file mode 100644
index d389089..0000000
--- a/Structural/DependencyInjection/Connection.php
+++ /dev/null
@@ -1,50 +0,0 @@
-configuration = $config;
- }
-
- /**
- * connection using the injected config.
- */
- public function connect()
- {
- $host = $this->configuration->get('host');
- // connection to host, authentication etc...
-
- //if connected
- $this->host = $host;
- }
-
- /*
- * Get currently connected host
- *
- * @return string
- */
-
- public function getHost()
- {
- return $this->host;
- }
-}
diff --git a/Structural/DependencyInjection/DatabaseConfiguration.php b/Structural/DependencyInjection/DatabaseConfiguration.php
new file mode 100644
index 0000000..51d72d7
--- /dev/null
+++ b/Structural/DependencyInjection/DatabaseConfiguration.php
@@ -0,0 +1,54 @@
+host = $host;
+ $this->port = $port;
+ $this->username = $username;
+ $this->password = $password;
+ }
+
+ public function getHost(): string
+ {
+ return $this->host;
+ }
+
+ public function getPort(): int
+ {
+ return $this->port;
+ }
+
+ public function getUsername(): string
+ {
+ return $this->username;
+ }
+
+ public function getPassword(): string
+ {
+ return $this->password;
+ }
+}
diff --git a/Structural/DependencyInjection/DatabaseConnection.php b/Structural/DependencyInjection/DatabaseConnection.php
new file mode 100644
index 0000000..b5af8ba
--- /dev/null
+++ b/Structural/DependencyInjection/DatabaseConnection.php
@@ -0,0 +1,34 @@
+configuration = $config;
+ }
+
+ public function getDsn(): string
+ {
+ // this is just for the sake of demonstration, not a real DSN
+ // notice that only the injected config is used here, so there is
+ // a real separation of concerns here
+
+ return sprintf(
+ '%s:%s@%s:%d',
+ $this->configuration->getUsername(),
+ $this->configuration->getPassword(),
+ $this->configuration->getHost(),
+ $this->configuration->getPort()
+ );
+ }
+}
diff --git a/Structural/DependencyInjection/Parameters.php b/Structural/DependencyInjection/Parameters.php
deleted file mode 100644
index c49f4c7..0000000
--- a/Structural/DependencyInjection/Parameters.php
+++ /dev/null
@@ -1,26 +0,0 @@
-`__.
+directly in ``DatabaseConnection``, which is not very good for testing and
+extending it.
Examples
--------
@@ -45,27 +38,15 @@ Code
You can also find these code on `GitHub`_
-AbstractConfig.php
+DatabaseConfiguration.php
-.. literalinclude:: AbstractConfig.php
+.. literalinclude:: DatabaseConfiguration.php
:language: php
:linenos:
-Parameters.php
+DatabaseConnection.php
-.. literalinclude:: Parameters.php
- :language: php
- :linenos:
-
-ArrayConfig.php
-
-.. literalinclude:: ArrayConfig.php
- :language: php
- :linenos:
-
-Connection.php
-
-.. literalinclude:: Connection.php
+.. literalinclude:: DatabaseConnection.php
:language: php
:linenos:
@@ -78,11 +59,5 @@ Tests/DependencyInjectionTest.php
:language: php
:linenos:
-Tests/config.php
-
-.. literalinclude:: Tests/config.php
- :language: php
- :linenos:
-
.. _`GitHub`: https://github.com/domnikl/DesignPatternsPHP/tree/master/Structural/DependencyInjection
.. __: http://en.wikipedia.org/wiki/Dependency_injection
diff --git a/Structural/DependencyInjection/Tests/DependencyInjectionTest.php b/Structural/DependencyInjection/Tests/DependencyInjectionTest.php
index 6df82a7..3749d57 100644
--- a/Structural/DependencyInjection/Tests/DependencyInjectionTest.php
+++ b/Structural/DependencyInjection/Tests/DependencyInjectionTest.php
@@ -2,24 +2,16 @@
namespace DesignPatterns\Structural\DependencyInjection\Tests;
-use DesignPatterns\Structural\DependencyInjection\ArrayConfig;
-use DesignPatterns\Structural\DependencyInjection\Connection;
+use DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration;
+use DesignPatterns\Structural\DependencyInjection\DatabaseConnection;
class DependencyInjectionTest extends \PHPUnit_Framework_TestCase
{
- protected $config;
- protected $source;
-
- public function setUp()
- {
- $this->source = include 'config.php';
- $this->config = new ArrayConfig($this->source);
- }
-
public function testDependencyInjection()
{
- $connection = new Connection($this->config);
- $connection->connect();
- $this->assertEquals($this->source['host'], $connection->getHost());
+ $config = new DatabaseConfiguration('localhost', 3306, 'domnikl', '1234');
+ $connection = new DatabaseConnection($config);
+
+ $this->assertEquals('domnikl:1234@localhost:3306', $connection->getDsn());
}
}
diff --git a/Structural/DependencyInjection/Tests/config.php b/Structural/DependencyInjection/Tests/config.php
deleted file mode 100644
index 29d3683..0000000
--- a/Structural/DependencyInjection/Tests/config.php
+++ /dev/null
@@ -1,3 +0,0 @@
- 'github.com');
diff --git a/Structural/DependencyInjection/uml/DependencyInjection.uml b/Structural/DependencyInjection/uml/DependencyInjection.uml
index e058b19..2434332 100644
--- a/Structural/DependencyInjection/uml/DependencyInjection.uml
+++ b/Structural/DependencyInjection/uml/DependencyInjection.uml
@@ -1,38 +1,20 @@
-
-
- PHP
- \DesignPatterns\Structural\DependencyInjection\AbstractConfig
-
- \DesignPatterns\Structural\DependencyInjection\Connection
- \DesignPatterns\Structural\DependencyInjection\ArrayConfig
- \DesignPatterns\Structural\DependencyInjection\Parameters
- \DesignPatterns\Structural\DependencyInjection\AbstractConfig
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- \DesignPatterns\Structural\DependencyInjection\AbstractConfig
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration
+
+ \DesignPatterns\Structural\DependencyInjection\DatabaseConnection
+ \DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration
+
+
+
+
+
+
+ Methods
+ Constants
+ Fields
+
+ private
+
+
diff --git a/Structural/DependencyInjection/uml/uml.png b/Structural/DependencyInjection/uml/uml.png
index 67e6ca1..992d9a1 100644
Binary files a/Structural/DependencyInjection/uml/uml.png and b/Structural/DependencyInjection/uml/uml.png differ
diff --git a/Structural/DependencyInjection/uml/uml.svg b/Structural/DependencyInjection/uml/uml.svg
index 78d603d..1e15816 100644
--- a/Structural/DependencyInjection/uml/uml.svg
+++ b/Structural/DependencyInjection/uml/uml.svg
@@ -1,425 +1,573 @@
-
+
diff --git a/Structural/Facade/BiosInterface.php b/Structural/Facade/BiosInterface.php
index 8f1aa01..ba6bdbe 100644
--- a/Structural/Facade/BiosInterface.php
+++ b/Structural/Facade/BiosInterface.php
@@ -2,30 +2,13 @@
namespace DesignPatterns\Structural\Facade;
-/**
- * Interface BiosInterface.
- */
interface BiosInterface
{
- /**
- * Execute the BIOS.
- */
public function execute();
- /**
- * Wait for halt.
- */
public function waitForKeyPress();
- /**
- * Launches the OS.
- *
- * @param OsInterface $os
- */
public function launch(OsInterface $os);
- /**
- * Power down BIOS.
- */
public function powerDown();
}
diff --git a/Structural/Facade/Facade.php b/Structural/Facade/Facade.php
index 43d1bcb..bda8b46 100644
--- a/Structural/Facade/Facade.php
+++ b/Structural/Facade/Facade.php
@@ -2,25 +2,19 @@
namespace DesignPatterns\Structural\Facade;
-/**
- * Class Facade.
- */
class Facade
{
/**
* @var OsInterface
*/
- protected $os;
+ private $os;
/**
* @var BiosInterface
*/
- protected $bios;
+ private $bios;
/**
- * This is the perfect time to use a dependency injection container
- * to create an instance of this class.
- *
* @param BiosInterface $bios
* @param OsInterface $os
*/
@@ -30,9 +24,6 @@ class Facade
$this->os = $os;
}
- /**
- * Turn on the system.
- */
public function turnOn()
{
$this->bios->execute();
@@ -40,9 +31,6 @@ class Facade
$this->bios->launch($this->os);
}
- /**
- * Turn off the system.
- */
public function turnOff()
{
$this->os->halt();
diff --git a/Structural/Facade/OsInterface.php b/Structural/Facade/OsInterface.php
index d8171e4..1b3a93e 100644
--- a/Structural/Facade/OsInterface.php
+++ b/Structural/Facade/OsInterface.php
@@ -2,13 +2,9 @@
namespace DesignPatterns\Structural\Facade;
-/**
- * Interface OsInterface.
- */
interface OsInterface
{
- /**
- * Halt the OS.
- */
public function halt();
+
+ public function getName(): string;
}
diff --git a/Structural/Facade/Tests/FacadeTest.php b/Structural/Facade/Tests/FacadeTest.php
index 0247aaf..8c6a0a6 100644
--- a/Structural/Facade/Tests/FacadeTest.php
+++ b/Structural/Facade/Tests/FacadeTest.php
@@ -2,47 +2,34 @@
namespace DesignPatterns\Structural\Facade\Tests;
-use DesignPatterns\Structural\Facade\Facade as Computer;
+use DesignPatterns\Structural\Facade\Facade;
use DesignPatterns\Structural\Facade\OsInterface;
-/**
- * FacadeTest shows example of facades.
- */
class FacadeTest extends \PHPUnit_Framework_TestCase
{
- public function getComputer()
+ public function testComputerOn()
{
+ /** @var OsInterface|\PHPUnit_Framework_MockObject_MockObject $os */
+ $os = $this->createMock('DesignPatterns\Structural\Facade\OsInterface');
+
+ $os->method('getName')
+ ->will($this->returnValue('Linux'));
+
$bios = $this->getMockBuilder('DesignPatterns\Structural\Facade\BiosInterface')
- ->setMethods(array('launch', 'execute', 'waitForKeyPress'))
- ->disableAutoload()
- ->getMock();
- $operatingSys = $this->getMockBuilder('DesignPatterns\Structural\Facade\OsInterface')
- ->setMethods(array('getName'))
- ->disableAutoload()
- ->getMock();
+ ->setMethods(['launch', 'execute', 'waitForKeyPress'])
+ ->disableAutoload()
+ ->getMock();
+
$bios->expects($this->once())
- ->method('launch')
- ->with($operatingSys);
- $operatingSys
- ->expects($this->once())
- ->method('getName')
- ->will($this->returnValue('Linux'));
+ ->method('launch')
+ ->with($os);
- $facade = new Computer($bios, $operatingSys);
+ $facade = new Facade($bios, $os);
- return array(array($facade, $operatingSys));
- }
-
- /**
- * @param Computer $facade
- * @param OsInterface $os
- * @dataProvider getComputer
- */
- public function testComputerOn(Computer $facade, OsInterface $os)
- {
- // interface is simpler :
+ // the facade interface is simple
$facade->turnOn();
- // but I can access to lower component
+
+ // but you can also access the underlying components
$this->assertEquals('Linux', $os->getName());
}
}
diff --git a/Structural/Facade/uml/Facade.uml b/Structural/Facade/uml/Facade.uml
index d6bdd03..8835df8 100644
--- a/Structural/Facade/uml/Facade.uml
+++ b/Structural/Facade/uml/Facade.uml
@@ -1,24 +1,23 @@
-
-
- PHP
- \DesignPatterns\Structural\Facade\BiosInterface
-
- \DesignPatterns\Structural\Facade\BiosInterface
- \DesignPatterns\Structural\Facade\OsInterface
- \DesignPatterns\Structural\Facade\Facade
-
-
-
-
-
- \DesignPatterns\Structural\Facade\BiosInterface
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Structural\Facade\BiosInterface
+
+ \DesignPatterns\Structural\Facade\BiosInterface
+ \DesignPatterns\Structural\Facade\Facade
+ \DesignPatterns\Structural\Facade\OsInterface
+
+
+
+
+
+ \DesignPatterns\Structural\Facade\BiosInterface
+
+
+ Methods
+ Constants
+ Fields
+
+ private
+
+
diff --git a/Structural/Facade/uml/uml.png b/Structural/Facade/uml/uml.png
index da5097a..fcea5f2 100644
Binary files a/Structural/Facade/uml/uml.png and b/Structural/Facade/uml/uml.png differ
diff --git a/Structural/Facade/uml/uml.svg b/Structural/Facade/uml/uml.svg
index 2e7b400..a14db50 100644
--- a/Structural/Facade/uml/uml.svg
+++ b/Structural/Facade/uml/uml.svg
@@ -1,324 +1,653 @@
-
+
diff --git a/Structural/FluentInterface/Sql.php b/Structural/FluentInterface/Sql.php
index 58ba491..ed54992 100644
--- a/Structural/FluentInterface/Sql.php
+++ b/Structural/FluentInterface/Sql.php
@@ -2,79 +2,51 @@
namespace DesignPatterns\Structural\FluentInterface;
-/**
- * class SQL.
- */
class Sql
{
/**
* @var array
*/
- protected $fields = array();
+ private $fields = [];
/**
* @var array
*/
- protected $from = array();
+ private $from = [];
/**
* @var array
*/
- protected $where = array();
+ private $where = [];
- /**
- * adds select fields.
- *
- * @param array $fields
- *
- * @return SQL
- */
- public function select(array $fields = array())
+ public function select(array $fields): Sql
{
$this->fields = $fields;
return $this;
}
- /**
- * adds a FROM clause.
- *
- * @param string $table
- * @param string $alias
- *
- * @return SQL
- */
- public function from($table, $alias)
+ public function from(string $table, string $alias): Sql
{
$this->from[] = $table.' AS '.$alias;
return $this;
}
- /**
- * adds a WHERE condition.
- *
- * @param string $condition
- *
- * @return SQL
- */
- public function where($condition)
+ public function where(string $condition): Sql
{
$this->where[] = $condition;
return $this;
}
- /**
- * Gets the query, just an example of building a query,
- * no check on consistency.
- *
- * @return string
- */
- public function getQuery()
+ public function __toString(): string
{
- return 'SELECT '.implode(',', $this->fields)
- .' FROM '.implode(',', $this->from)
- .' WHERE '.implode(' AND ', $this->where);
+ return sprintf(
+ 'SELECT %s FROM %s WHERE %s',
+ join(', ', $this->fields),
+ join(', ', $this->from),
+ join(' AND ', $this->where)
+ );
}
}
diff --git a/Structural/FluentInterface/Tests/FluentInterfaceTest.php b/Structural/FluentInterface/Tests/FluentInterfaceTest.php
index ae4e656..b75e2f9 100644
--- a/Structural/FluentInterface/Tests/FluentInterfaceTest.php
+++ b/Structural/FluentInterface/Tests/FluentInterfaceTest.php
@@ -4,19 +4,15 @@ namespace DesignPatterns\Structural\FluentInterface\Tests;
use DesignPatterns\Structural\FluentInterface\Sql;
-/**
- * FluentInterfaceTest tests the fluent interface SQL.
- */
class FluentInterfaceTest extends \PHPUnit_Framework_TestCase
{
public function testBuildSQL()
{
- $instance = new Sql();
- $query = $instance->select(array('foo', 'bar'))
+ $query = (new Sql())
+ ->select(['foo', 'bar'])
->from('foobar', 'f')
- ->where('f.bar = ?')
- ->getQuery();
+ ->where('f.bar = ?');
- $this->assertEquals('SELECT foo,bar FROM foobar AS f WHERE f.bar = ?', $query);
+ $this->assertEquals('SELECT foo, bar FROM foobar AS f WHERE f.bar = ?', (string) $query);
}
}
diff --git a/Structural/FluentInterface/uml/FluentInterface.uml b/Structural/FluentInterface/uml/FluentInterface.uml
index 8fca787..552b641 100644
--- a/Structural/FluentInterface/uml/FluentInterface.uml
+++ b/Structural/FluentInterface/uml/FluentInterface.uml
@@ -1,22 +1,19 @@
-
-
- PHP
- \DesignPatterns\Structural\FluentInterface\Sql
-
- \DesignPatterns\Structural\FluentInterface\Sql
-
-
-
-
-
- \DesignPatterns\Structural\FluentInterface\Sql
-
-
- Fields
- Constants
- Constructors
- Methods
-
- private
-
-
+
+
+ PHP
+ \DesignPatterns\Structural\FluentInterface\Sql
+
+ \DesignPatterns\Structural\FluentInterface\Sql
+
+
+
+
+
+
+ Fields
+ Constants
+ Methods
+
+ private
+
+
diff --git a/Structural/FluentInterface/uml/uml.png b/Structural/FluentInterface/uml/uml.png
index e49aa57..2f38396 100644
Binary files a/Structural/FluentInterface/uml/uml.png and b/Structural/FluentInterface/uml/uml.png differ
diff --git a/Structural/FluentInterface/uml/uml.svg b/Structural/FluentInterface/uml/uml.svg
index 60a8564..9418314 100644
--- a/Structural/FluentInterface/uml/uml.svg
+++ b/Structural/FluentInterface/uml/uml.svg
@@ -1,191 +1,377 @@
-
+
diff --git a/Structural/Flyweight/CharacterFlyweight.php b/Structural/Flyweight/CharacterFlyweight.php
index 02657c1..d1e7954 100644
--- a/Structural/Flyweight/CharacterFlyweight.php
+++ b/Structural/Flyweight/CharacterFlyweight.php
@@ -16,22 +16,16 @@ class CharacterFlyweight implements FlyweightInterface
*/
private $name;
- /**
- * @param string $name
- */
- public function __construct($name)
+ public function __construct(string $name)
{
$this->name = $name;
}
- /**
- * Clients supply the context-dependent information that the flyweight needs to draw itself
- * For flyweights representing characters, extrinsic state usually contains e.g. the font.
- *
- * @param string $font
- */
- public function draw($font)
+ public function render(string $font): string
{
- print_r("Character {$this->name} printed $font \n");
+ // Clients supply the context-dependent information that the flyweight needs to draw itself
+ // For flyweights representing characters, extrinsic state usually contains e.g. the font.
+
+ return sprintf('Character %s with font %s', $this->name, $font);
}
}
diff --git a/Structural/Flyweight/FlyweightFactory.php b/Structural/Flyweight/FlyweightFactory.php
index 10a0d4d..a295a22 100644
--- a/Structural/Flyweight/FlyweightFactory.php
+++ b/Structural/Flyweight/FlyweightFactory.php
@@ -3,38 +3,26 @@
namespace DesignPatterns\Structural\Flyweight;
/**
- * A factory manages shared flyweights. Clients shouldn't instaniate them directly,
+ * A factory manages shared flyweights. Clients should not instantiate them directly,
* but let the factory take care of returning existing objects or creating new ones.
*/
-class FlyweightFactory
+class FlyweightFactory implements \Countable
{
/**
- * Associative store for flyweight objects.
- *
- * @var array
+ * @var CharacterFlyweight[]
*/
- private $pool = array();
+ private $pool = [];
- /**
- * Magic getter.
- *
- * @param string $name
- *
- * @return Flyweight
- */
- public function __get($name)
+ public function get(string $name): CharacterFlyweight
{
- if (!array_key_exists($name, $this->pool)) {
+ if (!isset($this->pool[$name])) {
$this->pool[$name] = new CharacterFlyweight($name);
}
return $this->pool[$name];
}
- /**
- * @return int
- */
- public function totalNumber()
+ public function count(): int
{
return count($this->pool);
}
diff --git a/Structural/Flyweight/FlyweightInterface.php b/Structural/Flyweight/FlyweightInterface.php
index b12290d..032a1be 100644
--- a/Structural/Flyweight/FlyweightInterface.php
+++ b/Structural/Flyweight/FlyweightInterface.php
@@ -2,13 +2,7 @@
namespace DesignPatterns\Structural\Flyweight;
-/**
- * An interface through which flyweights can receive and act on extrinsic state.
- */
interface FlyweightInterface
{
- /**
- * @param string $extrinsicState
- */
- public function draw($extrinsicState);
+ public function render(string $extrinsicState): string;
}
diff --git a/Structural/Flyweight/Tests/FlyweightTest.php b/Structural/Flyweight/Tests/FlyweightTest.php
index 997c3df..5f63456 100644
--- a/Structural/Flyweight/Tests/FlyweightTest.php
+++ b/Structural/Flyweight/Tests/FlyweightTest.php
@@ -4,33 +4,28 @@ namespace DesignPatterns\Structural\Flyweight\Tests;
use DesignPatterns\Structural\Flyweight\FlyweightFactory;
-/**
- * FlyweightTest demonstrates how a client would use the flyweight structure
- * You don't have to change the code of your client.
- */
class FlyweightTest extends \PHPUnit_Framework_TestCase
{
- private $characters = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
- 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', );
- private $fonts = array('Arial', 'Times New Roman', 'Verdana', 'Helvetica');
-
- // This is about the number of characters in a book of average length
- private $numberOfCharacters = 300000;
+ private $characters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
+ private $fonts = ['Arial', 'Times New Roman', 'Verdana', 'Helvetica'];
public function testFlyweight()
{
$factory = new FlyweightFactory();
- for ($i = 0; $i < $this->numberOfCharacters; $i++) {
- $char = $this->characters[array_rand($this->characters)];
- $font = $this->fonts[array_rand($this->fonts)];
- $flyweight = $factory->$char;
- // External state can be passed in like this:
- // $flyweight->draw($font);
+ foreach ($this->characters as $char) {
+ foreach ($this->fonts as $font) {
+ $flyweight = $factory->get($char);
+ $rendered = $flyweight->render($font);
+
+ $this->assertEquals(sprintf('Character %s with font %s', $char, $font), $rendered);
+ }
}
// Flyweight pattern ensures that instances are shared
// instead of having hundreds of thousands of individual objects
- $this->assertLessThanOrEqual($factory->totalNumber(), count($this->characters));
+ // there must be one instance for every char that has been reused for displaying in different fonts
+ $this->assertCount(count($this->characters), $factory);
}
}
diff --git a/Structural/Flyweight/uml/Flyweight.uml b/Structural/Flyweight/uml/Flyweight.uml
index 5d53b0f..61c78dd 100644
--- a/Structural/Flyweight/uml/Flyweight.uml
+++ b/Structural/Flyweight/uml/Flyweight.uml
@@ -1,25 +1,24 @@
PHP
- \DesignPatterns\Structural\Flyweight\FlyweightFactory
+ \DesignPatterns\Structural\Flyweight\CharacterFlyweight
- \DesignPatterns\Structural\Flyweight\CharacterFlyweight
- \DesignPatterns\Structural\Flyweight\FlyweightFactory
- \DesignPatterns\Structural\Flyweight\FlyweightInterface
+ \DesignPatterns\Structural\Flyweight\CharacterFlyweight
+ \DesignPatterns\Structural\Flyweight\FlyweightFactory
+ \DesignPatterns\Structural\Flyweight\FlyweightInterface
-
-
+
+
-
+
Fields
Constants
- Constructors
Methods
private
diff --git a/Structural/Flyweight/uml/uml.png b/Structural/Flyweight/uml/uml.png
index 1209ab4..26af3e3 100644
Binary files a/Structural/Flyweight/uml/uml.png and b/Structural/Flyweight/uml/uml.png differ
diff --git a/Structural/Flyweight/uml/uml.svg b/Structural/Flyweight/uml/uml.svg
index b46aa2f..fc0d70b 100644
--- a/Structural/Flyweight/uml/uml.svg
+++ b/Structural/Flyweight/uml/uml.svg
@@ -1,466 +1,503 @@
-