Last active
February 12, 2022 22:47
-
-
Save Kcko/61d8f6baecc8ccfe68b99dc6a1b1b224 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
interface Command | |
{ | |
public function handle() : void; | |
} | |
final class CreateListing implements Command | |
{ | |
private const MIN_TITLE_LENGTH = 10; | |
private const MIN_CONTENT_LENGTH = 15; | |
private const MIN_AUTHOR_LENGTH = 5; | |
private $repository; | |
private $title; | |
private $content; | |
private $author; | |
public function __construct(ListingRepository $repository, string $title, string $content, string $author) | |
{ | |
$this->repository = $repository; | |
$this->title = $title; | |
$this->content = $content; | |
$this->author = $author; | |
} | |
private function validate(): void | |
{ | |
if (strlen($this->title) < self::MIN_TITLE_LENGTH) { | |
throw new LengthException(sprintf("Title is too short. Must be at least %d characters", | |
self::MIN_TITLE_LENGTH)); | |
} | |
if (strlen($this->content) < self::MIN_CONTENT_LENGTH) { | |
throw new LengthException(sprintf("Content is too short. Must be at least %d characters", | |
self::MIN_CONTENT_LENGTH)); | |
} | |
if (strlen($this->author) < self::MIN_AUTHOR_LENGTH) { | |
throw new LengthException(sprintf("Author name is too short. Must be at least %d characters", | |
self::MIN_AUTHOR_LENGTH)); | |
} | |
} | |
public function handle(): void | |
{ | |
$this->validate(); | |
$this->repository->create($this->title, $this->content, $this->author); | |
} | |
} | |
final class DeleteListing implements Command | |
{ | |
private $repository; | |
private $listingUid; | |
public function __construct(ListingRepository $repository, string $listingUid) | |
{ | |
$this->repository = $repository; | |
$this->listingUid = $listingUid; | |
} | |
public function handle(): void | |
{ | |
$this->repository->delete($this->listingUid); | |
} | |
} | |
final class ListingRepository | |
{ | |
public function create(string $title, string $content, string $author): void | |
{ | |
echo sprintf("Creating new listing by \"%s\" and title \"%s\"", $author, $title).PHP_EOL; | |
echo sprintf("Content: \"%s\"", $content).PHP_EOL; | |
echo sprintf("Generated uid: \"%s\"", uniqid()).PHP_EOL; | |
} | |
public function delete(string $uid): void | |
{ | |
echo sprintf("Removing job listing with uid: \"%s\"", $uid).PHP_EOL; | |
} | |
} | |
final class Client | |
{ | |
private $listingRepository; | |
public function __construct(ListingRepository $repository) | |
{ | |
$this->listingRepository = $repository; | |
} | |
public function createListing(string $title, string $content, string $author): void | |
{ | |
$command = new CreateListing($this->listingRepository, $title, $content, $author); | |
$command->handle(); | |
} | |
public function deleteListing(string $listingUid) : void | |
{ | |
$command = new DeleteListing($this->listingRepository, $listingUid); | |
$command->handle(); | |
} | |
} | |
$client = new Client(new ListingRepository()); | |
$client->createListing( | |
"New job listing", | |
"This is a content of a listing", | |
"Company" | |
); | |
$client->deleteListing("Unique id"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace RefactoringGuru\Command\Conceptual; | |
/** | |
* The Command interface declares a method for executing a command. | |
*/ | |
interface Command | |
{ | |
public function execute(): void; | |
} | |
/** | |
* Some commands can implement simple operations on their own. | |
*/ | |
class SimpleCommand implements Command | |
{ | |
private $payload; | |
public function __construct(string $payload) | |
{ | |
$this->payload = $payload; | |
} | |
public function execute(): void | |
{ | |
echo "SimpleCommand: See, I can do simple things like printing (" . $this->payload . ")\n"; | |
} | |
} | |
/** | |
* However, some commands can delegate more complex operations to other objects, | |
* called "receivers." | |
*/ | |
class ComplexCommand implements Command | |
{ | |
/** | |
* @var Receiver | |
*/ | |
private $receiver; | |
/** | |
* Context data, required for launching the receiver's methods. | |
*/ | |
private $a; | |
private $b; | |
/** | |
* Complex commands can accept one or several receiver objects along with | |
* any context data via the constructor. | |
*/ | |
public function __construct(Receiver $receiver, string $a, string $b) | |
{ | |
$this->receiver = $receiver; | |
$this->a = $a; | |
$this->b = $b; | |
} | |
/** | |
* Commands can delegate to any methods of a receiver. | |
*/ | |
public function execute(): void | |
{ | |
echo "ComplexCommand: Complex stuff should be done by a receiver object.\n"; | |
$this->receiver->doSomething($this->a); | |
$this->receiver->doSomethingElse($this->b); | |
} | |
} | |
/** | |
* The Receiver classes contain some important business logic. They know how to | |
* perform all kinds of operations, associated with carrying out a request. In | |
* fact, any class may serve as a Receiver. | |
*/ | |
class Receiver | |
{ | |
public function doSomething(string $a): void | |
{ | |
echo "Receiver: Working on (" . $a . ".)\n"; | |
} | |
public function doSomethingElse(string $b): void | |
{ | |
echo "Receiver: Also working on (" . $b . ".)\n"; | |
} | |
} | |
/** | |
* The Invoker is associated with one or several commands. It sends a request to | |
* the command. | |
*/ | |
class Invoker | |
{ | |
/** | |
* @var Command | |
*/ | |
private $onStart; | |
/** | |
* @var Command | |
*/ | |
private $onFinish; | |
/** | |
* Initialize commands. | |
*/ | |
public function setOnStart(Command $command): void | |
{ | |
$this->onStart = $command; | |
} | |
public function setOnFinish(Command $command): void | |
{ | |
$this->onFinish = $command; | |
} | |
/** | |
* The Invoker does not depend on concrete command or receiver classes. The | |
* Invoker passes a request to a receiver indirectly, by executing a | |
* command. | |
*/ | |
public function doSomethingImportant(): void | |
{ | |
echo "Invoker: Does anybody want something done before I begin?\n"; | |
if ($this->onStart instanceof Command) { | |
$this->onStart->execute(); | |
} | |
echo "Invoker: ...doing something really important...\n"; | |
echo "Invoker: Does anybody want something done after I finish?\n"; | |
if ($this->onFinish instanceof Command) { | |
$this->onFinish->execute(); | |
} | |
} | |
} | |
/** | |
* The client code can parameterize an invoker with any commands. | |
*/ | |
$invoker = new Invoker; | |
$invoker->setOnStart(new SimpleCommand("Say Hi!")); | |
$receiver = new Receiver; | |
$invoker->setOnFinish(new ComplexCommand($receiver, "Send email", "Save report")); | |
$invoker->doSomethingImportant(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
interface Command | |
{ | |
/** | |
* this is the most important method in the Command pattern, | |
* The Receiver goes in the constructor. | |
*/ | |
public function execute(); | |
} | |
interface UndoableCommand extends Command | |
{ | |
/** | |
* This method is used to undo change made by command execution | |
*/ | |
public function undo(); | |
} | |
class HelloCommand implements Command | |
{ | |
private Receiver $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 | |
*/ | |
public function __construct(Receiver $console) | |
{ | |
$this->output = $console; | |
} | |
/** | |
* execute and output "Hello World". | |
*/ | |
public function execute() | |
{ | |
// sometimes, there is no receiver and this is the command which does all the work | |
$this->output->write('Hello World'); | |
} | |
} | |
class AddMessageDateCommand implements UndoableCommand | |
{ | |
private Receiver $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. | |
*/ | |
public function __construct(Receiver $console) | |
{ | |
$this->output = $console; | |
} | |
/** | |
* Execute and make receiver to enable displaying messages date. | |
*/ | |
public function execute() | |
{ | |
// sometimes, there is no receiver and this is the command which | |
// does all the work | |
$this->output->enableDate(); | |
} | |
/** | |
* Undo the command and make receiver to disable displaying messages date. | |
*/ | |
public function undo() | |
{ | |
// sometimes, there is no receiver and this is the command which | |
// does all the work | |
$this->output->disableDate(); | |
} | |
} | |
/** | |
* Receiver is a specific service with its own contract and can be only concrete. | |
*/ | |
class Receiver | |
{ | |
private bool $enableDate = false; | |
/** | |
* @var string[] | |
*/ | |
private array $output = []; | |
public function write(string $str) | |
{ | |
if ($this->enableDate) { | |
$str .= ' ['.date('Y-m-d').']'; | |
} | |
$this->output[] = $str; | |
} | |
public function getOutput(): string | |
{ | |
return join("\n", $this->output); | |
} | |
/** | |
* Enable receiver to display message date | |
*/ | |
public function enableDate() | |
{ | |
$this->enableDate = true; | |
} | |
/** | |
* Disable receiver to display message date | |
*/ | |
public function disableDate() | |
{ | |
$this->enableDate = false; | |
} | |
} | |
/** | |
* Invoker is using the command given to it. | |
* Example : an Application in SF2. | |
*/ | |
class Invoker | |
{ | |
private Command $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 ... | |
*/ | |
public function setCommand(Command $cmd) | |
{ | |
$this->command = $cmd; | |
} | |
/** | |
* executes the command; the invoker is the same whatever is the command | |
*/ | |
public function run() | |
{ | |
$this->command->execute(); | |
} | |
} | |
// USAGE | |
$invoker = new Invoker(); | |
$receiver = new Receiver(); | |
$invoker->setCommand(new HelloCommand($receiver)); | |
$invoker->run(); | |
$this->assertSame('Hello World', $receiver->getOutput()); | |
// https://designpatternsphp.readthedocs.io/en/latest/Behavioral/Command/README.html |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment