Design patterns are a way to solve recurring problems. More precisely, this is a guide to solving specific problems. These are not classes, packages or libraries that you can insert into your application and expect magic.
As stated on Wikipedia:
In software engineering, an application design pattern is a reusable solution to a regularly occurring problem within a specific application architecture context. A template is not a complete architectural solution that can be directly converted to source or machine code. This is a description of the approach to solving a problem that can be applied in different situations.
The article contains examples in PHP 7, but don't be confused by the fact that the principles incorporated in the templates are unchanged. In addition, support for other languages ​​is being introduced .
In short
Generating templates describe the creation (instantiate) of an object or a group of related objects.
Wikipedia
In software engineering, generators are templates that use object creation mechanisms to create objects in a way that is appropriate for a given situation. The basic way of creation can lead to problems in architecture or to its complication. Generating patterns attempt to solve these problems by controlling the way objects are created.
Analogy
Let's say you build a house and you need doors. It will be a mess if every time you need a door, you will become armed with tools and do it at the construction site. Instead, you order the doors in the factory.
In short
A simple factory simply generates an instance for a client without providing any instance logic.
Wikipedia
In object-oriented programming, a factory is an object that creates other objects. Formally, a factory is a function or method that returns objects of different prototypes or classes from a call to a method that is considered new.
Example
First we need a door interface and its implementation.
interface Door { public function getWidth(): float; public function getHeight(): float; } class WoodenDoor implements Door { protected $width; protected $height; public function __construct(float $width, float $height) { $this->width = $width; $this->height = $height; } public function getWidth(): float { return $this->width; } public function getHeight(): float { return $this->height; } }
Now we will build a factory of doors that creates and returns doors to us.
class DoorFactory { public static function makeDoor($width, $height): Door { return new WoodenDoor($width, $height); } }
Using:
$door = DoorFactory:makeDoor(100, 200); echo 'Width: ' . $door->getWidth(); echo 'Height: ' . $door->getHeight();
When to use?
When creating an object involves some kind of logic, and not just several assignments, it makes sense to delegate the task to a dedicated factory, rather than repeating the same code everywhere.
Analogy
One cadre is not able to conduct interviews with all candidates for all positions. Depending on the vacancy, she can delegate different stages of interviews to different employees.
In short
This is a way to delegate the logic of creating objects (instantiation logic) to child classes.
Wikipedia
In class-based programming, a factory designation method is a generating method that uses factory methods to solve the problem of creating objects without specifying specific classes for them. Objects are created by calling not the constructor, but the generating method defined in the interface and implemented by the child classes or implemented in the base class and, optionally, overridden by derived classes.
Example
First, create an interface for the interviewer and some implementations for him.
interface Interviewer { public function askQuestions(); } class Developer implements Interviewer { public function askQuestions() { echo 'Asking about design patterns!'; } } class CommunityExecutive implements Interviewer { public function askQuestions() { echo 'Asking about community building'; } }
Now we will create a HiringManager
.
abstract class HiringManager { // abstract public function makeInterviewer(): Interviewer; public function takeInterview() { $interviewer = $this->makeInterviewer(); $interviewer->askQuestions(); } }
Any child class can extend it and provide the right interviewer:
class DevelopmentManager extends HiringManager { public function makeInterviewer(): Interviewer { return new Developer(); } } class MarketingManager extends HiringManager { public function makeInterviewer(): Interviewer { return new CommunityExecutive(); } }
Using:
$devManager = new DevelopmentManager(); $devManager->takeInterview(); // Output: . $marketingManager = new MarketingManager(); $marketingManager->takeInterview(); // Output: .
When to use?
This template is useful for some general class processing, but the required subclasses are dynamically determined during runtime. That is, when the client does not know which subclass he might need.
Analogy
Let's return to the example with the doors from “Simple Factory”. Depending on your needs, you can buy a wooden door in one store, steel - in another, plastic - in the third. For installation, you will need different specialists: a wooden door needs a carpenter, a steel one - a welder, a plastic one - a specialist in PVC profiles.
In short
This is a factory of factories. That is, a factory grouping individual, but interrelated / interdependent factories without specifying specific classes for them.
Wikipedia
The “Abstract Factory” template describes how to encapsulate a group of individual factories united by a certain topic, without specifying specific classes for them.
Example
Create a Door interface and several implementations for it.
interface Door { public function getDescription(); } class WoodenDoor implements Door { public function getDescription() { echo 'I am a wooden door'; } } class IronDoor implements Door { public function getDescription() { echo 'I am an iron door'; } }
Now we need specialists to install each type of door.
interface DoorFittingExpert { public function getDescription(); } class Welder implements DoorFittingExpert { public function getDescription() { echo 'I can only fit iron doors'; } } class Carpenter implements DoorFittingExpert { public function getDescription() { echo 'I can only fit wooden doors'; } }
We have an abstract factory that allows you to create families of objects or interrelated objects. That is, the factory of wooden doors will create a wooden door and a person for its installation, a factory of steel doors - a steel door and an appropriate specialist, etc.
interface DoorFactory { public function makeDoor(): Door; public function makeFittingExpert(): DoorFittingExpert; } // class WoodenDoorFactory implements DoorFactory { public function makeDoor(): Door { return new WoodenDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Carpenter(); } } // class IronDoorFactory implements DoorFactory { public function makeDoor(): Door { return new IronDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Welder(); } }
Using:
$woodenFactory = new WoodenDoorFactory(); $door = $woodenFactory->makeDoor(); $expert = $woodenFactory->makeFittingExpert(); $door->getDescription(); // Output: $expert->getDescription(); // Output: // Same for Iron Factory $ironFactory = new IronDoorFactory(); $door = $ironFactory->makeDoor(); $expert = $ironFactory->makeFittingExpert(); $door->getDescription(); // Output: $expert->getDescription(); // Output:
Here the wooden door factory encapsulated the carpenter
and the wooden door
, the steel door factory was an iron door
and welder
. That is, you can be sure that for each of the created doors, we will get the right specialist.
When to use?
When you have interrelations with not the simplest logic of creation (creation logic).
Analogy
Suppose you came to the diner, ordered a burger of the day, and you were given it without question . This is an example of “Simple Factory.” But sometimes the creation logic consists of more steps. For example, when ordering a burger of the day, there are several options for bread, toppings, sauces, additional ingredients. In such situations helps template "Builder".
In short
The template allows you to create different properties of the object, avoiding constructor pollution (constructor pollution). This is useful when an object can have several properties. Or when the creation of an object consists of a large number of stages.
Wikipedia
The Builder pattern is designed to find a solution to the problem of the Telescoping constructor's anti-pattern.
Let me explain what the anti-pattern Telescoping constructor is. Each of us has ever encountered such a constructor:
public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true) { }
As you can see, the number of parameters can quickly grow, and it will become difficult to understand their structure. In addition, this list of parameters will continue to grow if you want to add new options in the future. This is the anti-pattern telescoping constructor.
Example
A sensible alternative is the builder pattern. First, create a burger:
class Burger { protected $size; protected $cheese = false; protected $pepperoni = false; protected $lettuce = false; protected $tomato = false; public function __construct(BurgerBuilder $builder) { $this->size = $builder->size; $this->cheese = $builder->cheese; $this->pepperoni = $builder->pepperoni; $this->lettuce = $builder->lettuce; $this->tomato = $builder->tomato; } }
And then add the "builder":
class BurgerBuilder { public $size; public $cheese = false; public $pepperoni = false; public $lettuce = false; public $tomato = false; public function __construct(int $size) { $this->size = $size; } public function addPepperoni() { $this->pepperoni = true; return $this; } public function addLettuce() { $this->lettuce = true; return $this; } public function addCheese() { $this->cheese = true; return $this; } public function addTomato() { $this->tomato = true; return $this; } public function build(): Burger { return new Burger($this); } }
Using:
$burger = (new BurgerBuilder(14)) ->addPepperoni() ->addLettuce() ->addTomato() ->build();
When to use?
When an object can have several properties and when it is necessary to avoid the Telescoping constructor. The key difference from the “Simple Factory” template is that it is used in one-step creation, and “Builder” is used in multi-step creation.
Analogy
Remember the cloned sheep Dolly? So, this design pattern is just about cloning.
In short
An object is created by cloning an existing object.
Wikipedia
The prototype template is used when the types of objects created are determined by the prototype instance cloned to create new objects.
That is, the template allows you to duplicate an existing object and modify the copy to suit your needs. No problems with creating an object from scratch and setting it up.
Example
In PHP, this can be done easily with clone
:
class Sheep { protected $name; protected $category; public function __construct(string $name, string $category = 'Mountain Sheep') { $this->name = $name; $this->category = $category; } public function setName(string $name) { $this->name = $name; } public function getName() { return $this->name; } public function setCategory(string $category) { $this->category = $category; } public function getCategory() { return $this->category; } }
Then you can clone like this:
$original = new Sheep('Jolly'); echo $original->getName(); // echo $original->getCategory(); // // , $cloned = clone $original; $cloned->setName('Dolly'); echo $cloned->getName(); // echo $cloned->getCategory(); //
You can also refer to the __clone
magic method to modify the cloning procedure.
When to use?
When the necessary object is similar to an existing one or when creating from scratch is more expensive than cloning.
Analogy
A country can have only one president. He must act when circumstances and debt require it. In this case, the president is a loner.
In short
The template allows you to make sure that the object being created is the only one in its class.
Wikipedia
The “Singleton” template allows you to limit the creation of a class to a single object. This is convenient when coordinating actions within the system requires that the object is the only one in its class.
In fact, the template "Singleton" is considered antipattern, they should not be too carried away. It is not necessarily bad and is sometimes helpful. But use it with caution, because Loner makes the application a global state, so a change in one place can affect all other uses, and debugging this is not an easy task. Other drawbacks to the pattern are: it makes your code tightly coupled, and prototyping (mocking) “Loners” can be difficult.
Example
Make the constructor private, disable the extensions, and create a static variable to hold the instance:
final class President { private static $instance; private function __construct() { // } public static function getInstance(): President { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } private function __clone() { // } private function __wakeup() { // } }
Using:
$president1 = President::getInstance(); $president2 = President::getInstance(); var_dump($president1 === $president2); // true
In short
These templates are mainly devoted to the composition of objects (object composition). That is, how entities can use each other. Another explanation: structural patterns help answer the question “How to build a software component?”
Wikipedia
Structural are templates that facilitate design, defining a simple way to implement the relationship between entities.
Analogy
Suppose you have some pictures on your memory card. They need to be transferred to a computer. You need an adapter that is compatible with the input port of the computer into which you can insert a memory card. In this example, the adapter is a card reader. Another example: an adapter that allows you to use an American power supply with a Russian outlet. The third example: a translator is an adapter that connects two people speaking different languages.
In short
The “Adapter” template allows you to put an incompatible object in a wrapper so that it is compatible with another class.
Wikipedia
The Adapter design pattern allows you to use the interface of an existing class as another interface. This template is often used to ensure that some classes work with others without changing their source code.
Example
Imagine a lion hunter.
Create a Lion
interface that implements all types of lions.
interface Lion { public function roar(); } class AfricanLion implements Lion { public function roar() { } } class AsianLion implements Lion { public function roar() { } }
The hunter must hunt all implementations of the Lion
interface.
class Hunter { public function hunt(Lion $lion) { } }
Add now wild dog WildDog
, which the hunter can also hunt. But we can't do it directly, because the dog has a different interface. To make it compatible with the hunter, you need to create a suitable adapter.
// class WildDog { public function bark() { } } // class WildDogAdapter implements Lion { protected $dog; public function __construct(WildDog $dog) { $this->dog = $dog; } public function roar() { $this->dog->bark(); } }
Now WildDog
can come into play thanks to WildDogAdapter
.
$wildDog = new WildDog(); $wildDogAdapter = new WildDogAdapter($wildDog); $hunter = new Hunter(); $hunter->hunt($wildDogAdapter);
Analogy
Suppose you have a website with several pages. You want to allow users to change the page layout. What would you do? Did you create multiple copies of each page for each topic, or did you simply make separate topics and load them according to the user settings? Template "Bridge" allows you to implement the second approach.
In short
The Bridge template is a preference for the inheritance layout. Implementation details are passed from one hierarchy to another object with a separate hierarchy.
Wikipedia
The "Bridge" template means the separation of abstraction from implementation, so that both of them can be changed independently of each other.
Example
We implement the above example with web pages. Make a hierarchy of WebPage
:
interface WebPage { public function __construct(Theme $theme); public function getContent(); } class About implements WebPage { protected $theme; public function __construct(Theme $theme) { $this->theme = $theme; } public function getContent() { return "About page in " . $this->theme->getColor(); } } class Careers implements WebPage { protected $theme; public function __construct(Theme $theme) { $this->theme = $theme; } public function getContent() { return "Careers page in " . $this->theme->getColor(); } }
Separate the hierarchy of topics:
interface Theme { public function getColor(); } class DarkTheme implements Theme { public function getColor() { return 'Dark Black'; } } class LightTheme implements Theme { public function getColor() { return 'Off white'; } } class AquaTheme implements Theme { public function getColor() { return 'Light blue'; } }
Both hierarchies:
$darkTheme = new DarkTheme(); $about = new About($darkTheme); $careers = new Careers($darkTheme); echo $about->getContent(); // "About page in Dark Black"; echo $careers->getContent(); // "Careers page in Dark Black";
Analogy
Each company consists of employees. Every employee has the same properties: salary, duties, accountability to someone, subordination ...
In short
The “Linker” template allows clients to process individual objects in a single order.
Wikipedia
The “Linker” template describes the general order of processing a group of objects, as if it were a single instance of an object. The essence of the template is the arrangement of objects in a tree structure to represent the hierarchy from the particular to the whole. The template allows clients to access individual objects and groups of objects in the same way.
Example
Here are the different types of employees:
interface Employee { public function __construct(string $name, float $salary); public function getName(): string; public function setSalary(float $salary); public function getSalary(): float; public function getRoles(): array; } class Developer implements Employee { protected $salary; protected $name; public function __construct(string $name, float $salary) { $this->name = $name; $this->salary = $salary; } public function getName(): string { return $this->name; } public function setSalary(float $salary) { $this->salary = $salary; } public function getSalary(): float { return $this->salary; } public function getRoles(): array { return $this->roles; } } class Designer implements Employee { protected $salary; protected $name; public function __construct(string $name, float $salary) { $this->name = $name; $this->salary = $salary; } public function getName(): string { return $this->name; } public function setSalary(float $salary) { $this->salary = $salary; } public function getSalary(): float { return $this->salary; } public function getRoles(): array { return $this->roles; } }
But the company, which consists of employees of different types:
class Organization { protected $employees; public function addEmployee(Employee $employee) { $this->employees[] = $employee; } public function getNetSalaries(): float { $netSalary = 0; foreach ($this->employees as $employee) { $netSalary += $employee->getSalary(); } return $netSalary; } }
Using:
// $john = new Developer('John Doe', 12000); $jane = new Designer('Jane Doe', 15000); // $organization = new Organization(); $organization->addEmployee($john); $organization->addEmployee($jane); echo "Net salaries: " . $organization->getNetSalaries(); // Net Salaries: 27000
Analogy
Suppose you have your own car service, providing various services. How to bill customers? Consistently adding the services and their cost - and in the end you get the total amount to be paid. Here, each type of service is a “decorator”.
In short
The "decorator" template allows you to dynamically change the behavior of an object at runtime, wrapping it into an object of the "decorator" class.
Wikipedia
The "Decorator" template allows you to connect to the object additional behavior (statically or dynamically), without affecting the behavior of other objects of the same class. The template is often used to comply with the Single Responsibility Principle, because it allows the division of functionality between classes to solve specific tasks.
Example
Take coffee as an example. First, just implement the interface:
interface Coffee { public function getCost(); public function getDescription(); } class SimpleCoffee implements Coffee { public function getCost() { return 10; } public function getDescription() { return 'Simple coffee'; } }
You can make the code extensible to make modifications if necessary. Add "decorators":
class MilkCoffee implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 2; } public function getDescription() { return $this->coffee->getDescription() . ', milk'; } } class WhipCoffee implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 5; } public function getDescription() { return $this->coffee->getDescription() . ', whip'; } } class VanillaCoffee implements Coffee { protected $coffee; public function __construct(Coffee $coffee) { $this->coffee = $coffee; } public function getCost() { return $this->coffee->getCost() + 3; } public function getDescription() { return $this->coffee->getDescription() . ', vanilla'; } }
Now make coffee:
$someCoffee = new SimpleCoffee(); echo $someCoffee->getCost(); // 10 echo $someCoffee->getDescription(); // Simple Coffee $someCoffee = new MilkCoffee($someCoffee); echo $someCoffee->getCost(); // 12 echo $someCoffee->getDescription(); // Simple Coffee, milk $someCoffee = new WhipCoffee($someCoffee); echo $someCoffee->getCost(); // 17 echo $someCoffee->getDescription(); // Simple Coffee, milk, whip $someCoffee = new VanillaCoffee($someCoffee); echo $someCoffee->getCost(); // 20 echo $someCoffee->getDescription(); // Simple Coffee, milk, whip, vanilla
Analogy
How to turn on the computer? You will say: “Press the power button”. This is because you are using a simple interface provided by the computer outside. And inside it there are a lot of processes. A simple interface for a complex subsystem is a facade.
In short
The "Facade" template provides a simplified interface for a complex subsystem.
Wikipedia
A “facade” is an object that provides a simplified interface for a larger body of code, such as a class library.
Example
Create a computer class:
class Computer { public function getElectricShock() { echo "Ouch!"; } public function makeSound() { echo "Beep beep!"; } public function showLoadingScreen() { echo "Loading.."; } public function bam() { echo "Ready to be used!"; } public function closeEverything() { echo "Bup bup bup buzzzz!"; } public function sooth() { echo "Zzzzz"; } public function pullCurrent() { echo "Haaah!"; } }
Now the "facade":
class ComputerFacade { protected $computer; public function __construct(Computer $computer) { $this->computer = $computer; } public function turnOn() { $this->computer->getElectricShock(); $this->computer->makeSound(); $this->computer->showLoadingScreen(); $this->computer->bam(); } public function turnOff() { $this->computer->closeEverything(); $this->computer->pullCurrent(); $this->computer->sooth(); } }
Using:
$computer = new ComputerFacade(new Computer()); $computer->turnOn(); // Ouch! Beep beep! Loading.. Ready to be used! $computer->turnOff(); // Bup bup buzzz! Haah! Zzzzz
Analogy
Usually, in catering establishments, tea is not brewed separately for each client, but immediately in some large container. This saves resources: gas / electricity, time, etc. The "Adaptive" template is just about sharing.
In short
The template is used to minimize the use of memory or computational cost due to the total use of as many identical objects as possible.
Wikipedia
“Adaptive” is an object that minimizes the use of memory at the expense of the use of the largest possible amount of data with other similar objects. , .
Example
.
// — , . // — . class KarakTea { } // class TeaMaker { protected $availableTea = []; public function make($preference) { if (empty($this->availableTea[$preference])) { $this->availableTea[$preference] = new KarakTea(); } return $this->availableTea[$preference]; } }
TeaShop
, :
class TeaShop { protected $orders; protected $teaMaker; public function __construct(TeaMaker $teaMaker) { $this->teaMaker = $teaMaker; } public function takeOrder(string $teaType, int $table) { $this->orders[$table] = $this->teaMaker->make($teaType); } public function serve() { foreach ($this->orders as $table => $tea) { echo "Serving tea to table# " . $table; } } }
:
$teaMaker = new TeaMaker(); $shop = new TeaShop($teaMaker); $shop->takeOrder('less sugar', 1); $shop->takeOrder('more milk', 2); $shop->takeOrder('without sugar', 5); $shop->serve(); // Serving tea to table# 1 // Serving tea to table# 2 // Serving tea to table# 5
Analogy
(access card) . — , - — «».
In short
«» .
Wikipedia
«» — , -. -, , «» . «» , : ; (preconditions) .
Example
:
interface Door { public function open(); public function close(); } class LabDoor implements Door { public function open() { echo "Opening lab door"; } public function close() { echo "Closing the lab door"; } }
«», :
class Security { protected $door; public function __construct(Door $door) { $this->door = $door; } public function open($password) { if ($this->authenticate($password)) { $this->door->open(); } else { echo "Big no! It ain't possible."; } } public function authenticate($password) { return $password === '$ecr@t'; } public function close() { $this->door->close(); } }
:
$door = new Security(new LabDoor()); $door->open('invalid'); // Big no! It ain't possible. $door->open('$ecr@t'); // Opening lab door $door->close(); // Closing lab door
(data-mapper). ODM (Object Data Mapper) MongoDB. «» mongo-, __call()
. mongo- «», . find
findOne
, Cursor
.
In short
(responsibilities) . , , , . « ?»
Wikipedia
. .
Analogy
, ( A
, B
C
). : A
— 100 , B
— 300, C
— 1000. : , , . - 210 . « » . — , . — , . .
In short
« » . , .
Wikipedia
« » . , , , .
Example
, , .
abstract class Account { protected $successor; protected $balance; public function setNext(Account $account) { $this->successor = $account; } public function pay(float $amountToPay) { if ($this->canPay($amountToPay)) { echo sprintf('Paid %s using %s' . PHP_EOL, $amountToPay, get_called_class()); } elseif ($this->successor) { echo sprintf('Cannot pay using %s. Proceeding ..' . PHP_EOL, get_called_class()); $this->successor->pay($amountToPay); } else { throw new Exception('None of the accounts have enough balance'); } } public function canPay($amount): bool { return $this->balance >= $amount; } } class Bank extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Paypal extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Bitcoin extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } }
(Bank, Paypal, Bitcoin) :
// // $bank->$paypal->$bitcoin // // // , Paypal // Paypal , Bitcoin $bank = new Bank(100); // 100 $paypal = new Paypal(200); // Paypal 200 $bitcoin = new Bitcoin(300); // Bitcoin 300 $bank->setNext($paypal); $paypal->setNext($bitcoin); // $bank->pay(259); // // ============== // . ... // Paypal. ... // 259 Bitcoin!
Analogy
. ( Client
) ( Invoker
) ( Command
). - ( Receiver
), , . : ( Client
) ( Command
) ( Receiver
) ( Invoker
).
In short
«» . — .
Wikipedia
«» , . ; , ; .
Example
, , .
// Receiver class Bulb { public function turnOn() { echo "Bulb has been lit"; } public function turnOff() { echo "Darkness!"; } }
, . .
interface Command { public function execute(); public function undo(); public function redo(); } // Command class TurnOn implements Command { protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOn(); } public function undo() { $this->bulb->turnOff(); } public function redo() { $this->execute(); } } class TurnOff implements Command { protected $bulb; public function __construct(Bulb $bulb) { $this->bulb = $bulb; } public function execute() { $this->bulb->turnOff(); } public function undo() { $this->bulb->turnOn(); } public function redo() { $this->execute(); } }
Invoker
, .
// Invoker class RemoteControl { public function submit(Command $command) { $command->execute(); } }
, :
$bulb = new Bulb(); $turnOn = new TurnOn($bulb); $turnOff = new TurnOff($bulb); $remote = new RemoteControl(); $remote->submit($turnOn); // ! $remote->submit($turnOff); // !
«» . , . , . undo
.
Analogy
— . - , /. .
In short
— .
Wikipedia
. . - , , .
Example
PHP PHP. RadioStation
.
class RadioStation { protected $frequency; public function __construct(float $frequency) { $this->frequency = $frequency; } public function getFrequency(): float { return $this->frequency; } }
:
use Countable; use Iterator; class StationList implements Countable, Iterator { /** @var RadioStation[] $stations */ protected $stations = []; /** @var int $counter */ protected $counter; public function addStation(RadioStation $station) { $this->stations[] = $station; } public function removeStation(RadioStation $toRemove) { $toRemoveFrequency = $toRemove->getFrequency(); $this->stations = array_filter($this->stations, function (RadioStation $station) use ($toRemoveFrequency) { return $station->getFrequency() !== $toRemoveFrequency; }); } public function count(): int { return count($this->stations); } public function current(): RadioStation { return $this->stations[$this->counter]; } public function key() { return $this->counter; } public function next() { $this->counter++; } public function rewind() { $this->counter = 0; } public function valid(): bool { return isset($this->stations[$this->counter]); } }
:
$stationList = new StationList(); $stationList->addStation(new RadioStation(89)); $stationList->addStation(new RadioStation(101)); $stationList->addStation(new RadioStation(102)); $stationList->addStation(new RadioStation(103.2)); foreach($stationList as $station) { echo $station->getFrequency() . PHP_EOL; } $stationList->removeStation(new RadioStation(89)); // Will remove station 89
Analogy
- , . , . — .
In short
«» («») («»). (coupling) , , .
Wikipedia
, .
Example
: («»), («») .
«»:
interface ChatRoomMediator { public function showMessage(User $user, string $message); } // class ChatRoom implements ChatRoomMediator { public function showMessage(User $user, string $message) { $time = date('M d, y H:i'); $sender = $user->getName(); echo $time . '[' . $sender . ']:' . $message; } }
«»:
class User { protected $name; protected $chatMediator; public function __construct(string $name, ChatRoomMediator $chatMediator) { $this->name = $name; $this->chatMediator = $chatMediator; } public function getName() { return $this->name; } public function send($message) { $this->chatMediator->showMessage($this, $message); } }
:
$mediator = new ChatRoom(); $john = new User('John Doe', $mediator); $jane = new User('Jane Doe', $mediator); $john->send('Hi there!'); $jane->send('Hey!'); // // Feb 14, 10:58 [John]: Hi there! // Feb 14, 10:58 [Jane]: Hey!
Analogy
(«»), («»), - («»).
In short
«» , .
Wikipedia
«» ( — undo via rollback).
, .
Example
, - .
«», .
class EditorMemento { protected $content; public function __construct(string $content) { $this->content = $content; } public function getContent() { return $this->content; } }
(«»), «».
class Editor { protected $content = ''; public function type(string $words) { $this->content = $this->content . ' ' . $words; } public function getContent() { return $this->content; } public function save() { return new EditorMemento($this->content); } public function restore(EditorMemento $memento) { $this->content = $memento->getContent(); } }
:
$editor = new Editor(); // - $editor->type('This is the first sentence.'); $editor->type('This is second.'); // : This is the first sentence. This is second. $saved = $editor->save(); // $editor->type('And this is third.'); // Output: echo $editor->getContent(); // This is the first sentence. This is second. And this is third. // $editor->restore($saved); $editor->getContent(); // This is the first sentence. This is second.
Analogy
: , , , , .
In short
, «» .
Wikipedia
«» («»), «» («») , .
Example
, , .
class JobPost { protected $title; public function __construct(string $title) { $this->title = $title; } public function getTitle() { return $this->title; } } class JobSeeker implements Observer { protected $name; public function __construct(string $name) { $this->name = $name; } public function onJobPosted(JobPost $job) { // Do something with the job posting echo 'Hi ' . $this->name . '! New job posted: '. $job->getTitle(); } }
, .
class JobPostings implements Observable { protected $observers = []; protected function notify(JobPost $jobPosting) { foreach ($this->observers as $observer) { $observer->onJobPosted($jobPosting); } } public function attach(Observer $observer) { $this->observers[] = $observer; } public function addJob(JobPost $jobPosting) { $this->notify($jobPosting); } }
:
// $johnDoe = new JobSeeker('John Doe'); $janeDoe = new JobSeeker('Jane Doe'); // $jobPostings = new JobPostings(); $jobPostings->attach($johnDoe); $jobPostings->attach($janeDoe); // , $jobPostings->addJob(new JobPost('Software Engineer')); // Output // Hi John Doe! New job posted: Software Engineer // Hi Jane Doe! New job posted: Software Engineer
Analogy
. (). , , . - — . «» .
In short
«» .
Wikipedia
«» — , . — . / (open/closed principle).
Example
: , .
// interface Animal { public function accept(AnimalOperation $operation); } // interface AnimalOperation { public function visitMonkey(Monkey $monkey); public function visitLion(Lion $lion); public function visitDolphin(Dolphin $dolphin); }
:
class Monkey implements Animal { public function shout() { echo 'Ooh oo aa aa!'; } public function accept(AnimalOperation $operation) { $operation->visitMonkey($this); } } class Lion implements Animal { public function roar() { echo 'Roaaar!'; } public function accept(AnimalOperation $operation) { $operation->visitLion($this); } } class Dolphin implements Animal { public function speak() { echo 'Tuut tuttu tuutt!'; } public function accept(AnimalOperation $operation) { $operation->visitDolphin($this); } }
:
class Speak implements AnimalOperation { public function visitMonkey(Monkey $monkey) { $monkey->shout(); } public function visitLion(Lion $lion) { $lion->roar(); } public function visitDolphin(Dolphin $dolphin) { $dolphin->speak(); } }
:
$monkey = new Monkey(); $lion = new Lion(); $dolphin = new Dolphin(); $speak = new Speak(); $monkey->accept($speak); // --! $lion->accept($speak); // ! $dolphin->accept($speak); // !
, . . , , :
class Jump implements AnimalOperation { public function visitMonkey(Monkey $monkey) { echo 'Jumped 20 feet high! on to the tree!'; } public function visitLion(Lion $lion) { echo 'Jumped 7 feet! Back on the ground!'; } public function visitDolphin(Dolphin $dolphin) { echo 'Walked on water a little and disappeared'; } }
:
$jump = new Jump(); $monkey->accept($speak); // Ooh oo aa aa! $monkey->accept($jump); // Jumped 20 feet high! on to the tree! $lion->accept($speak); // Roaaar! $lion->accept($jump); // Jumped 7 feet! Back on the ground! $dolphin->accept($speak); // Tuut tutt tuutt! $dolphin->accept($jump); // Walked on water a little and disappeared
Analogy
. , . (Quick sort). , . , , — .
In short
«» .
Wikipedia
«» .
Example
. .
interface SortStrategy { public function sort(array $dataset): array; } class BubbleSortStrategy implements SortStrategy { public function sort(array $dataset): array { echo "Sorting using bubble sort"; // Do sorting return $dataset; } } class QuickSortStrategy implements SortStrategy { public function sort(array $dataset): array { echo "Sorting using quick sort"; // Do sorting return $dataset; } }
, .
class Sorter { protected $sorter; public function __construct(SortStrategy $sorter) { $this->sorter = $sorter; } public function sort(array $dataset): array { return $this->sorter->sort($dataset); } }
:
$dataset = [1, 5, 4, 3, 2, 8]; $sorter = new Sorter(new BubbleSortStrategy()); $sorter->sort($dataset); // Output : $sorter = new Sorter(new QuickSortStrategy()); $sorter->sort($dataset); // Output :
Analogy
, «». : . . .
In short
.
Wikipedia
«» . :
- «»,
- (state transitions) , (superclass).
«» — «», , .
Example
, , . . — . .
:
interface WritingState { public function write(string $words); } class UpperCase implements WritingState { public function write(string $words) { echo strtoupper($words); } } class LowerCase implements WritingState { public function write(string $words) { echo strtolower($words); } } class Default implements WritingState { public function write(string $words) { echo $words; } }
:
class TextEditor { protected $state; public function __construct(WritingState $state) { $this->state = $state; } public function setState(WritingState $state) { $this->state = $state; } public function type(string $words) { $this->state->write($words); } }
:
$editor = new TextEditor(new Default()); $editor->type('First line'); $editor->setState(new UpperCase()); $editor->type('Second line'); $editor->type('Third line'); $editor->setState(new LowerCase()); $editor->type('Fourth line'); $editor->type('Fifth line'); // Output: // First line // SECOND LINE // THIRD LINE // fourth line // fifth line
Analogy
, . :
. — . . : , , , .
In short
« » , .
Wikipedia
« » — , , .
Example
, , , (lint), , ( , . .), .
.
abstract class Builder { // final public function build() { $this->test(); $this->lint(); $this->assemble(); $this->deploy(); } abstract public function test(); abstract public function lint(); abstract public function assemble(); abstract public function deploy(); }
:
class AndroidBuilder extends Builder { public function test() { echo 'Running android tests'; } public function lint() { echo 'Linting the android code'; } public function assemble() { echo 'Assembling the android build'; } public function deploy() { echo 'Deploying android build to server'; } } class IosBuilder extends Builder { public function test() { echo 'Running ios tests'; } public function lint() { echo 'Linting the ios code'; } public function assemble() { echo 'Assembling the ios build'; } public function deploy() { echo 'Deploying ios build to server'; } }
:
$androidBuilder = new AndroidBuilder(); $androidBuilder->build(); // Output: // Android- // Android- // Android- // Android- $iosBuilder = new IosBuilder(); $iosBuilder->build(); // Output: // iOS- // iOS- // iOS- // iOS-
. . .
Source: https://habr.com/ru/post/325492/
All Articles