🎉 设计模式的极简单解释! 🎉
一个很容易让人头脑动摇的话题. 这里,我将试着将设计模式用尽可能简单的方式,灌输到你的脑子里,也可能是我的脑子里。
Did you like this guide and want more of the similar content?
Subscribe for the launch of Hugobots or follow me on twitter!
设计模式是重复出现的问题的解决方法;是解决这些问题的指导方针。设计模式不是简单的类、第三方包或者第三方库,你不能即插即用。相反地,设计模式是告诉你如何在特殊情形下解决特殊问题的方法。
设计模式是重复出现的问题的解决方法;是解决这些问题的指导方针。
维基百科的描述:
在软件工程中,一种软件设计模式是指,给定一个上下文环境的在软件设计中,通常可复用的,对一个常见问题的解决方案。他不是一个可以直接转换为源代码或者机器代码的完整的实现。他是一种问题解决方法的描述或者说模板,并且可以用在很多情形之下。
- 设计模式不是解决你所有问题的万金油。
- 不要尝试硬着头皮强行去用设计模式;这样做会造成很蛋疼的后果。记住,设计模式是解决问题的方法,不是产生问题的方法。所以,不要想太多。
- 如果设计模式以合适的方式用在合适的地方,他们将会是救世主;反之,他会让你的代码陷入恐怖的混乱之中。
值得注意的是,以下的代码例子都使用全世界最好的语言 PHP-7, 然而这不应该阻碍你,因为概念都是一致的,无关语言。正在添加其他语言的支持。
简单来说
创建类型的设计模式专注于怎么实例化一个或者多个相关的对象。
维基百科上写道
在软件工程中,创建类型的设计模式是解决对象创建机制,试图以合适的方式创建对象的设计模式。基本的对象创建形式会导致设计问题或者增加设计的复杂度。创建类型的设计模式通过对对象某些方面的控制,来解决这个问题。
- Simple Factory 简单工厂模式
- Factory Method 工厂模式
- Abstract Factory 抽象工厂模式
- Builder 建造者模式
- Prototype 原型模式
- Singleton 单例模式
真实世界的例子
当你要建造一个房子,然后这时候需要门。假设,如果你每需要一扇门,你都要拿出相应的工具,来制造所需要的门,这将会非常烦。相反,你可以交给生成门的工厂去做这件事情。
简单来说
Simple factory 简单工厂模式生成一个对象实例,但不暴露任何实例化这个对象的逻辑给客户端。
维基百科写道
在面向对象编程,一个工厂(factory)是指那些用来创建对象的对象 —— 在形式上,一个工厂是一个函数或者(对象的)方法,它从某个调用返回不同的原型的对象或类的对象,这个过程被认为是new(创建)
编程示例
首先,我们拥有一个door(门)的接口和实现
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;
}
}
然后我们有一个“生成”门的工厂,并且返回门的实例
class DoorFactory
{
public static function makeDoor($width, $height): Door
{
return new WoodenDoor($width, $height);
}
}
然后,我们可以这样使用
$door = DoorFactory::makeDoor(100, 200);
echo 'Width: ' . $door->getWidth();
echo 'Height: ' . $door->getHeight();
什么时候使用?
当创建一个对象不是简单的几句赋值,并且需要调用一些多级时候,把它放在一个专门的工厂而不是在任何地方重复相同的代码是有意义的。
真实世界的例子
考虑公司的HR(招聘组经理)。 HR一个人不可能为每个职位进行面试。 根据职位,她必须决定并将面试步骤委托给不同的人。
简单来说
工厂模式提供一个方式来代理子类的实例化逻辑。
维基百科写道
在基于类的编程中,工厂模式是一种解决“创建对象且不指定对象的具体类”问题的创建类型设计模式。实现这种方式,是通过调用一个工厂方法来创建对象而不是通过构造函数,这个工厂方法或者是在接口声明然后在子类实现,或者在基类实现然后在子类被重写。
编程示例
按照我们上述的招聘组经理的例子。首先我们要有一个面试官的接口,然后是一些对这个接口的实现。
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';
}
}
现在我们来写 招聘组经理
abstract class HiringManager
{
// Factory method
abstract protected function makeInterviewer(): Interviewer;
public function takeInterview()
{
$interviewer = $this->makeInterviewer();
$interviewer->askQuestions();
}
}
现在,所有的子类能够扩展之,然后提供相应需要的面试官。
class DevelopmentManager extends HiringManager
{
protected function makeInterviewer(): Interviewer
{
return new Developer();
}
}
class MarketingManager extends HiringManager
{
protected function makeInterviewer(): Interviewer
{
return new CommunityExecutive();
}
}
现在你能够这样使用
$devManager = new DevelopmentManager();
$devManager->takeInterview(); // Output: Asking about design patterns
$marketingManager = new MarketingManager();
$marketingManager->takeInterview(); // Output: Asking about community building.
什么时候使用?
当一个类有一些通用的处理,但是需要这些处理的子类是在运行时动态确定的。换种说法,当客户端不知道要使用哪个确切的子类时候,这种模式可能需要使用。
真实世界的例子
扩展上述简单 扩展我们在简单工厂模式讲到的“门”的例子。根据你的需求,你可能需要从“木门”商店购买“木门”,从“铁门”商店购买“钢铁门”,从“PVC门”商店购买“PVC门”。进一步来讲,对于不同类型的“门”,你会需要不同类型的工匠,比如“木门”的“木匠”,“铁门”的“焊接工”等等。因此,现在“门”之间出现了一种依赖关系,“木门”需要“木匠”,“铁门”需要“焊接工”等等。
简单来说
工厂的工厂。一个将单独的的、但是却互相关联或依赖的工厂组合在一起的工厂,并且不需要指定组合的工厂们的具体类型。
维基百科写道:
抽象工厂提供一种封装一组单独的、拥有共同特点的工厂,并且不指定组合的工厂们的具体类型。
Wikipedia says
The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
编程示例
翻译上述的“门”的例子。开始,我们有一个Door
的接口,以及一些这个接口的实现。
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';
}
}
然后我们有适合各种“门”的专业人士。
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';
}
}
现在我们写我们的抽象工厂来负责制造一个系列的相关对象。也就是说:“木门工厂”将会制造“木门”以及“木门”专业人士;“铁门工厂”将会制造“铁门”以及“铁门”专业人士
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();
}
}
然后,就能够这样使用
$woodenFactory = new WoodenDoorFactory();
$door = $woodenFactory->makeDoor();
$expert = $woodenFactory->makeFittingExpert();
$door->getDescription(); // Output: I am a wooden door
$expert->getDescription(); // Output: I can only fit wooden doors
// Same for Iron Factory
$ironFactory = new IronDoorFactory();
$door = $ironFactory->makeDoor();
$expert = $ironFactory->makeFittingExpert();
$door->getDescription(); // Output: I am an iron door
$expert->getDescription(); // Output: I can only fit iron doors
正如你所见,“木门工厂”封装了木匠
和木门
。因此他帮助我们确定对于每个生成出来的们,我们都不会搞错其对应的专业人士。
When to use?
When there are interrelated dependencies with not-that-simple creation logic involved
Real world example
Imagine you are at Hardee's and you order a specific deal, lets say, "Big Hardee" and they hand it over to you without any questions; this is the example of simple factory. But there are cases when the creation logic might involve more steps. For example you want a customized Subway deal, you have several options in how your burger is made e.g what bread do you want? what types of sauces would you like? What cheese would you want? etc. In such cases builder pattern comes to the rescue.
In plain words
Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object.
Wikipedia says
The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern.
Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point or the other we have all seen a constructor like below:
public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true)
{
}
As you can see; the number of constructor parameters can quickly get out of hand and it might become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in future. This is called telescoping constructor anti-pattern.
Programmatic Example
The sane alternative is to use the builder pattern. First of all we have our burger that we want to make
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 we have 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);
}
}
And then it can be used as:
$burger = (new BurgerBuilder(14))
->addPepperoni()
->addLettuce()
->addTomato()
->build();
When to use?
When there could be several flavors of an object and to avoid the constructor telescoping. The key difference from the factory pattern is that; factory pattern is to be used when the creation is a one step process while builder pattern is to be used when the creation is a multi step process.
Real world example
Remember dolly? The sheep that was cloned! Lets not get into the details but the key point here is that it is all about cloning
In plain words
Create object based on an existing object through cloning.
Wikipedia says
The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects.
In short, it allows you to create a copy of an existing object and modify it to your needs, instead of going through the trouble of creating an object from scratch and setting it up.
Programmatic Example
In PHP, it can be easily done using 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 it can be cloned like below
$original = new Sheep('Jolly');
echo $original->getName(); // Jolly
echo $original->getCategory(); // Mountain Sheep
// Clone and modify what is required
$cloned = clone $original;
$cloned->setName('Dolly');
echo $cloned->getName(); // Dolly
echo $cloned->getCategory(); // Mountain sheep
Also you could use the magic method __clone
to modify the cloning behavior.
When to use?
When an object is required that is similar to existing object or when the creation would be expensive as compared to cloning.
Real world example
There can only be one president of a country at a time. The same president has to be brought to action, whenever duty calls. President here is singleton.
In plain words
Ensures that only one object of a particular class is ever created.
Wikipedia says
In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
Singleton pattern is actually considered an anti-pattern and overuse of it should be avoided. It is not necessarily bad and could have some valid use-cases but should be used with caution because it introduces a global state in your application and change to it in one place could affect in the other areas and it could become pretty difficult to debug. The other bad thing about them is it makes your code tightly coupled plus mocking the singleton could be difficult.
Programmatic Example
To create a singleton, make the constructor private, disable cloning, disable extension and create a static variable to house the instance
final class President
{
private static $instance;
private function __construct()
{
// Hide the constructor
}
public static function getInstance(): President
{
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __clone()
{
// Disable cloning
}
private function __wakeup()
{
// Disable unserialize
}
}
Then in order to use
$president1 = President::getInstance();
$president2 = President::getInstance();
var_dump($president1 === $president2); // true
In plain words
Structural patterns are mostly concerned with object composition or in other words how the entities can use each other. Or yet another explanation would be, they help in answering "How to build a software component?"
Wikipedia says
In software engineering, structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.
Real world example
Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a three legged plug can't be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet. Yet another example would be a translator translating words spoken by one person to another
In plain words
Adapter pattern lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class.
Wikipedia says
In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
Programmatic Example
Consider a game where there is a hunter and he hunts lions.
First we have an interface Lion
that all types of lions have to implement
interface Lion
{
public function roar();
}
class AfricanLion implements Lion
{
public function roar()
{
}
}
class AsianLion implements Lion
{
public function roar()
{
}
}
And hunter expects any implementation of Lion
interface to hunt.
class Hunter
{
public function hunt(Lion $lion)
{
}
}
Now let's say we have to add a WildDog
in our game so that hunter can hunt that also. But we can't do that directly because dog has a different interface. To make it compatible for our hunter, we will have to create an adapter that is compatible
// This needs to be added to the game
class WildDog
{
public function bark()
{
}
}
// Adapter around wild dog to make it compatible with our game
class WildDogAdapter implements Lion
{
protected $dog;
public function __construct(WildDog $dog)
{
$this->dog = $dog;
}
public function roar()
{
$this->dog->bark();
}
}
And now the WildDog
can be used in our game using WildDogAdapter
.
$wildDog = new WildDog();
$wildDogAdapter = new WildDogAdapter($wildDog);
$hunter = new Hunter();
$hunter->hunt($wildDogAdapter);
Real world example
Consider you have a website with different pages and you are supposed to allow the user to change the theme. What would you do? Create multiple copies of each of the pages for each of the themes or would you just create separate theme and load them based on the user's preferences? Bridge pattern allows you to do the second i.e.
In Plain Words
Bridge pattern is about preferring composition over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.
Wikipedia says
The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently"
Programmatic Example
Translating our WebPage example from above. Here we have the WebPage
hierarchy
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();
}
}
And the separate theme hierarchy
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';
}
}
And both the 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";
Real world example
Every organization is composed of employees. Each of the employees has the same features i.e. has a salary, has some responsibilities, may or may not report to someone, may or may not have some subordinates etc.
In plain words
Composite pattern lets clients treat the individual objects in a uniform manner.
Wikipedia says
In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
Programmatic Example
Taking our employees example from above. Here we have different employee types
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;
protected $roles;
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;
protected $roles;
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;
}
}
Then we have an organization which consists of several different types of employees
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;
}
}
And then it can be used as
// Prepare the employees
$john = new Developer('John Doe', 12000);
$jane = new Designer('Jane Doe', 15000);
// Add them to organization
$organization = new Organization();
$organization->addEmployee($john);
$organization->addEmployee($jane);
echo "Net salaries: " . $organization->getNetSalaries(); // Net Salaries: 27000
Real world example
Imagine you run a car service shop offering multiple services. Now how do you calculate the bill to be charged? You pick one service and dynamically keep adding to it the prices for the provided services till you get the final cost. Here each type of service is a decorator.
In plain words
Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class.
Wikipedia says
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
Programmatic Example
Lets take coffee for example. First of all we have a simple coffee implementing the coffee interface
interface Coffee
{
public function getCost();
public function getDescription();
}
class SimpleCoffee implements Coffee
{
public function getCost()
{
return 10;
}
public function getDescription()
{
return 'Simple coffee';
}
}
We want to make the code extensible to allow options to modify it if required. Lets make some add-ons (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';
}
}
Lets make a coffee now
$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
Real world example
How do you turn on the computer? "Hit the power button" you say! That is what you believe because you are using a simple interface that computer provides on the outside, internally it has to do a lot of stuff to make it happen. This simple interface to the complex subsystem is a facade.
In plain words
Facade pattern provides a simplified interface to a complex subsystem.
Wikipedia says
A facade is an object that provides a simplified interface to a larger body of code, such as a class library.
Programmatic Example
Taking our computer example from above. Here we have the 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!";
}
}
Here we have 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();
}
}
Now to use the facade
$computer = new ComputerFacade(new Computer());
$computer->turnOn(); // Ouch! Beep beep! Loading.. Ready to be used!
$computer->turnOff(); // Bup bup buzzz! Haah! Zzzzz
Real world example
Did you ever have fresh tea from some stall? They often make more than one cup that you demanded and save the rest for any other customer so to save the resources e.g. gas etc. Flyweight pattern is all about that i.e. sharing.
In plain words
It is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects.
Wikipedia says
In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
Programmatic example
Translating our tea example from above. First of all we have tea types and tea maker
// Anything that will be cached is flyweight.
// Types of tea here will be flyweights.
class KarakTea
{
}
// Acts as a factory and saves the tea
class TeaMaker
{
protected $availableTea = [];
public function make($preference)
{
if (empty($this->availableTea[$preference])) {
$this->availableTea[$preference] = new KarakTea();
}
return $this->availableTea[$preference];
}
}
Then we have the TeaShop
which takes orders and serves them
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;
}
}
}
And it can be used as below
$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
Real world example
Have you ever used an access card to go through a door? There are multiple options to open that door i.e. it can be opened either using access card or by pressing a button that bypasses the security. The door's main functionality is to open but there is a proxy added on top of it to add some functionality. Let me better explain it using the code example below.
In plain words
Using the proxy pattern, a class represents the functionality of another class.
Wikipedia says
A proxy, in its most general form, is a class functioning as an interface to something else. A proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked.
Programmatic Example
Taking our security door example from above. Firstly we have the door interface and an implementation of door
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";
}
}
Then we have a proxy to secure any doors that we want
class SecuredDoor
{
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();
}
}
And here is how it can be used
$door = new SecuredDoor(new LabDoor());
$door->open('invalid'); // Big no! It ain't possible.
$door->open('$ecr@t'); // Opening lab door
$door->close(); // Closing lab door
Yet another example would be some sort of data-mapper implementation. For example, I recently made an ODM (Object Data Mapper) for MongoDB using this pattern where I wrote a proxy around mongo classes while utilizing the magic method __call()
. All the method calls were proxied to the original mongo class and result retrieved was returned as it is but in case of find
or findOne
data was mapped to the required class objects and the object was returned instead of Cursor
.
In plain words
It is concerned with assignment of responsibilities between the objects. What makes them different from structural patterns is they don't just specify the structure but also outline the patterns for message passing/communication between them. Or in other words, they assist in answering "How to run a behavior in software component?"
Wikipedia says
In software engineering, behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.
- Chain of Responsibility
- Command
- Iterator
- Mediator
- Memento
- Observer
- Visitor
- Strategy
- State
- Template Method
Real world example
For example, you have three payment methods (
A
,B
andC
) setup in your account; each having a different amount in it.A
has 100 USD,B
has 300 USD andC
having 1000 USD and the preference for payments is chosen asA
thenB
thenC
. You try to purchase something that is worth 210 USD. Using Chain of Responsibility, first of all accountA
will be checked if it can make the purchase, if yes purchase will be made and the chain will be broken. If not, request will move forward to accountB
checking for amount if yes chain will be broken otherwise the request will keep forwarding till it finds the suitable handler. HereA
,B
andC
are links of the chain and the whole phenomenon is Chain of Responsibility.
In plain words
It helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler.
Wikipedia says
In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.
Programmatic Example
Translating our account example above. First of all we have a base account having the logic for chaining the accounts together and some accounts
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;
}
}
Now let's prepare the chain using the links defined above (i.e. Bank, Paypal, Bitcoin)
// Let's prepare a chain like below
// $bank->$paypal->$bitcoin
//
// First priority bank
// If bank can't pay then paypal
// If paypal can't pay then bit coin
$bank = new Bank(100); // Bank with balance 100
$paypal = new Paypal(200); // Paypal with balance 200
$bitcoin = new Bitcoin(300); // Bitcoin with balance 300
$bank->setNext($paypal);
$paypal->setNext($bitcoin);
// Let's try to pay using the first priority i.e. bank
$bank->pay(259);
// Output will be
// ==============
// Cannot pay using bank. Proceeding ..
// Cannot pay using paypal. Proceeding ..:
// Paid 259 using Bitcoin!
Real world example
A generic example would be you ordering food at a restaurant. You (i.e.
Client
) ask the waiter (i.e.Invoker
) to bring some food (i.e.Command
) and waiter simply forwards the request to Chef (i.e.Receiver
) who has the knowledge of what and how to cook. Another example would be you (i.e.Client
) switching on (i.e.Command
) the television (i.e.Receiver
) using a remote control (Invoker
).
In plain words
Allows you to encapsulate actions in objects. The key idea behind this pattern is to provide the means to decouple client from receiver.
Wikipedia says
In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.
Programmatic Example
First of all we have the receiver that has the implementation of every action that could be performed
// Receiver
class Bulb
{
public function turnOn()
{
echo "Bulb has been lit";
}
public function turnOff()
{
echo "Darkness!";
}
}
then we have an interface that each of the commands are going to implement and then we have a set of commands
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();
}
}
Then we have an Invoker
with whom the client will interact to process any commands
// Invoker
class RemoteControl
{
public function submit(Command $command)
{
$command->execute();
}
}
Finally let's see how we can use it in our client
$bulb = new Bulb();
$turnOn = new TurnOn($bulb);
$turnOff = new TurnOff($bulb);
$remote = new RemoteControl();
$remote->submit($turnOn); // Bulb has been lit!
$remote->submit($turnOff); // Darkness!
Command pattern can also be used to implement a transaction based system. Where you keep maintaining the history of commands as soon as you execute them. If the final command is successfully executed, all good otherwise just iterate through the history and keep executing the undo
on all the executed commands.
Real world example
An old radio set will be a good example of iterator, where user could start at some channel and then use next or previous buttons to go through the respective channels. Or take an example of MP3 player or a TV set where you could press the next and previous buttons to go through the consecutive channels or in other words they all provide an interface to iterate through the respective channels, songs or radio stations.
In plain words
It presents a way to access the elements of an object without exposing the underlying presentation.
Wikipedia says
In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
Programmatic example
In PHP it is quite easy to implement using SPL (Standard PHP Library). Translating our radio stations example from above. First of all we have RadioStation
class RadioStation
{
protected $frequency;
public function __construct(float $frequency)
{
$this->frequency = $frequency;
}
public function getFrequency(): float
{
return $this->frequency;
}
}
Then we have our iterator
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]);
}
}
And then it can be used as
$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
Real world example
A general example would be when you talk to someone on your mobile phone, there is a network provider sitting between you and them and your conversation goes through it instead of being directly sent. In this case network provider is mediator.
In plain words
Mediator pattern adds a third party object (called mediator) to control the interaction between two objects (called colleagues). It helps reduce the coupling between the classes communicating with each other. Because now they don't need to have the knowledge of each other's implementation.
Wikipedia says
In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.
Programmatic Example
Here is the simplest example of a chat room (i.e. mediator) with users (i.e. colleagues) sending messages to each other.
First of all, we have the mediator i.e. the chat room
interface ChatRoomMediator
{
public function showMessage(User $user, string $message);
}
// Mediator
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;
}
}
Then we have our users i.e. colleagues
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);
}
}
And the usage
$mediator = new ChatRoom();
$john = new User('John Doe', $mediator);
$jane = new User('Jane Doe', $mediator);
$john->send('Hi there!');
$jane->send('Hey!');
// Output will be
// Feb 14, 10:58 [John]: Hi there!
// Feb 14, 10:58 [Jane]: Hey!
Real world example
Take the example of calculator (i.e. originator), where whenever you perform some calculation the last calculation is saved in memory (i.e. memento) so that you can get back to it and maybe get it restored using some action buttons (i.e. caretaker).
In plain words
Memento pattern is about capturing and storing the current state of an object in a manner that it can be restored later on in a smooth manner.
Wikipedia says
The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
Usually useful when you need to provide some sort of undo functionality.
Programmatic Example
Lets take an example of text editor which keeps saving the state from time to time and that you can restore if you want.
First of all we have our memento object that will be able to hold the editor state
class EditorMemento
{
protected $content;
public function __construct(string $content)
{
$this->content = $content;
}
public function getContent()
{
return $this->content;
}
}
Then we have our editor i.e. originator that is going to use memento object
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();
}
}
And then it can be used as
$editor = new Editor();
// Type some stuff
$editor->type('This is the first sentence.');
$editor->type('This is second.');
// Save the state to restore to : This is the first sentence. This is second.
$saved = $editor->save();
// Type some more
$editor->type('And this is third.');
// Output: Content before Saving
echo $editor->getContent(); // This is the first sentence. This is second. And this is third.
// Restoring to last saved state
$editor->restore($saved);
$editor->getContent(); // This is the first sentence. This is second.
Real world example
A good example would be the job seekers where they subscribe to some job posting site and they are notified whenever there is a matching job opportunity.
In plain words
Defines a dependency between objects so that whenever an object changes its state, all its dependents are notified.
Wikipedia says
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
Programmatic example
Translating our example from above. First of all we have job seekers that need to be notified for a job posting
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();
}
}
Then we have our job postings to which the job seekers will subscribe
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);
}
}
Then it can be used as
// Create subscribers
$johnDoe = new JobSeeker('John Doe');
$janeDoe = new JobSeeker('Jane Doe');
// Create publisher and attach subscribers
$jobPostings = new JobPostings();
$jobPostings->attach($johnDoe);
$jobPostings->attach($janeDoe);
// Add a new job and see if subscribers get notified
$jobPostings->addJob(new JobPost('Software Engineer'));
// Output
// Hi John Doe! New job posted: Software Engineer
// Hi Jane Doe! New job posted: Software Engineer
Real world example
Consider someone visiting Dubai. They just need a way (i.e. visa) to enter Dubai. After arrival, they can come and visit any place in Dubai on their own without having to ask for permission or to do some leg work in order to visit any place here; just let them know of a place and they can visit it. Visitor pattern lets you do just that, it helps you add places to visit so that they can visit as much as they can without having to do any legwork.
In plain words
Visitor pattern lets you add further operations to objects without having to modify them.
Wikipedia says
In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle.
Programmatic example
Let's take an example of a zoo simulation where we have several different kinds of animals and we have to make them Sound. Let's translate this using visitor pattern
// Visitee
interface Animal
{
public function accept(AnimalOperation $operation);
}
// Visitor
interface AnimalOperation
{
public function visitMonkey(Monkey $monkey);
public function visitLion(Lion $lion);
public function visitDolphin(Dolphin $dolphin);
}
Then we have our implementations for the animals
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);
}
}
Let's implement our visitor
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();
}
}
And then it can be used as
$monkey = new Monkey();
$lion = new Lion();
$dolphin = new Dolphin();
$speak = new Speak();
$monkey->accept($speak); // Ooh oo aa aa!
$lion->accept($speak); // Roaaar!
$dolphin->accept($speak); // Tuut tutt tuutt!
We could have done this simply by having an inheritance hierarchy for the animals but then we would have to modify the animals whenever we would have to add new actions to animals. But now we will not have to change them. For example, let's say we are asked to add the jump behavior to the animals, we can simply add that by creating a new visitor i.e.
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';
}
}
And for the usage
$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
Real world example
Consider the example of sorting, we implemented bubble sort but the data started to grow and bubble sort started getting very slow. In order to tackle this we implemented Quick sort. But now although the quick sort algorithm was doing better for large datasets, it was very slow for smaller datasets. In order to handle this we implemented a strategy where for small datasets, bubble sort will be used and for larger, quick sort.
In plain words
Strategy pattern allows you to switch the algorithm or strategy based upon the situation.
Wikipedia says
In computer programming, the strategy pattern (also known as the policy pattern) is a behavioural software design pattern that enables an algorithm's behavior to be selected at runtime.
Programmatic example
Translating our example from above. First of all we have our strategy interface and different strategy implementations
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;
}
}
And then we have our client that is going to use any strategy
class Sorter
{
protected $sorter;
public function __construct(SortStrategy $sorter)
{
$this->sorter = $sorter;
}
public function sort(array $dataset): array
{
return $this->sorter->sort($dataset);
}
}
And it can be used as
$dataset = [1, 5, 4, 3, 2, 8];
$sorter = new Sorter(new BubbleSortStrategy());
$sorter->sort($dataset); // Output : Sorting using bubble sort
$sorter = new Sorter(new QuickSortStrategy());
$sorter->sort($dataset); // Output : Sorting using quick sort
Real world example
Imagine you are using some drawing application, you choose the paint brush to draw. Now the brush changes its behavior based on the selected color i.e. if you have chosen red color it will draw in red, if blue then it will be in blue etc.
In plain words
It lets you change the behavior of a class when the state changes.
Wikipedia says
The state pattern is a behavioral software design pattern that implements a state machine in an object-oriented way. With the state pattern, a state machine is implemented by implementing each individual state as a derived class of the state pattern interface, and implementing state transitions by invoking methods defined by the pattern's superclass. The state pattern can be interpreted as a strategy pattern which is able to switch the current strategy through invocations of methods defined in the pattern's interface.
Programmatic example
Let's take an example of text editor, it lets you change the state of text that is typed i.e. if you have selected bold, it starts writing in bold, if italic then in italics etc.
First of all we have our state interface and some state implementations
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 DefaultText implements WritingState
{
public function write(string $words)
{
echo $words;
}
}
Then we have our editor
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);
}
}
And then it can be used as
$editor = new TextEditor(new DefaultText());
$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
Real world example
Suppose we are getting some house built. The steps for building might look like
- Prepare the base of house
- Build the walls
- Add roof
- Add other floors
The order of these steps could never be changed i.e. you can't build the roof before building the walls etc but each of the steps could be modified for example walls can be made of wood or polyester or stone.
In plain words
Template method defines the skeleton of how a certain algorithm could be performed, but defers the implementation of those steps to the children classes.
Wikipedia says
In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm's structure.
Programmatic Example
Imagine we have a build tool that helps us test, lint, build, generate build reports (i.e. code coverage reports, linting report etc) and deploy our app on the test server.
First of all we have our base class that specifies the skeleton for the build algorithm
abstract class Builder
{
// Template method
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();
}
Then we can have our implementations
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';
}
}
And then it can be used as
$androidBuilder = new AndroidBuilder();
$androidBuilder->build();
// Output:
// Running android tests
// Linting the android code
// Assembling the android build
// Deploying android build to server
$iosBuilder = new IosBuilder();
$iosBuilder->build();
// Output:
// Running ios tests
// Linting the ios code
// Assembling the ios build
// Deploying ios build to server
And that about wraps it up. I will continue to improve this, so you might want to watch/star this repository to revisit. Also, I have plans on writing the same about the architectural patterns, stay tuned for it.