介绍
设计模式是反复出现问题的解决方案,如何解决某些问题的指导方针。它们并不是可以插入应用程序就可以等待神奇发生的类、包或者库。相反它是如何在某些情况下解决某些问题的指导原则。
设计模式是反复出现问题的解决方案,如何解决某些问题的指导方针。
维基百科将它们描述为:
在软件工程中,软件设计模式是软件设计中给定上下文中常见问题的通用的可重用的解决方案。它不是可以直接转换为源代码或机器代码的完整的设计。它是如何解决可在许多不同情况下使用的问题的描述或模板。
注意
设计模式不是解决所有问题的灵丹妙药。
不要试图强制使用他们,可能会适得其反。
请记住,设计模式是问题的解决方案,而不是解决问题的解决方案,所以不过度使用。
如果以正确的方式在正确的地方使用,就可以证明他们是救世主,否则他们可能会导致代码混乱。
另请注意,下面的代码示例是 PHP7,但是这不应该妨碍你理解,因为概念是相同的。
设计模式的类型
创建型
结构型
行为型
创建型设计模式
通俗解释:
创建模式专注于如何实例化对象或相关对象组。
维基百科:
在软件工程中,创建设计模式是处理对象创建机制的设计模式,试图以适合于该情况的方式创建对象。对象创建的基本形式可能导致设计问题或增加设计的复杂性。创建设计模式通过某种方式控制此对象创建来解决此问题。
简单工厂模式(Simple Factory)
工厂方法模式(Factory Method)
抽象工厂模式(Abstract Factory)
构建器模式
原型模式(Prototype)
单例模式(Singleton)
🏠简单工厂模式(Simple Factory)
现实生活中的例子:
考虑一下,你正在建房子,你需要门。你可以穿上你的木匠衣服,带上一些木头,胶水,钉子和建造门所需的所有工具,然后开始在你的房子里建造它,或者你可以简单地打电话给工厂并把内置的门送到你这里不需要了解关于制门的任何信息或处理制作它所带来的混乱。
通俗解释:
简单工厂只是为客户端生成一个实例,而不会向客户端公开任何实例化逻辑。
维基百科:
在面向对象编程(OOP)中,工厂是用于创建其他对象的对象 - 正式工厂是一种函数或方法,它从一些方法调用返回变化的原型或类的对象,这被假定为“新”。
程序示例:
首先,我们有一个门接口并且实现这个接口。
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);
}
}
然后就可以这样使用。
// Make me a door of 100x200
$door = DoorFactory::makeDoor(100, 200);
echo 'Width: ' . $door->getWidth();
echo 'Height: ' . $door->getHeight();
// Make me a door of 50x100
$door2 = DoorFactory::makeDoor(50, 100);
什么时候使用?
当创建一个对象不仅仅是一些分配而且涉及一些逻辑时,将它放在专用工厂中而不是在任何地方重复相同的代码是有意义的。
🏭工厂方法模式(Factory Method)
现实生活中的例子:
考虑招聘经理的情况。一个人不可能对每个职位进行面试。根据职位空缺,她必须决定并将面试步骤委托给不同的人。
通俗解释:
它提供了一种将实例化逻辑委托给子类的方法。
维基百科:
在基于类的编程中,工厂方法模式是一种创建模式,它使用工厂方法来处理创建对象的问题,而无需指定将要创建的对象的确切类。这是通过调用工厂方法来创建对象来完成的 - 在接口中指定并由子类实现,或者在基类中实现并可选地由派生类覆盖 - 而不是通过调用构造函数。
程序示例:
以上面的招聘经理为例。首先,我们有一个访谈者界面和一些实现
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';
}
}
现在让我们创造我们的 HiringManager
abstract class HiringManager
{
// Factory method
abstract protected function makeInterviewer(): Interviewer;
public function takeInterview()
{
$interviewer = $this->makeInterviewer();
$interviewer->askQuestions();
}
}
现在任何子类都可以继承并提供所需的Interviewer
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.
什么时候使用?
在类中有一些通用处理但在运行时动态决定所需的子类时很有用。换句话说,当客户端不知道它可能需要什么样的子类时。
🔨抽象工厂模式(Abstract Factory)
现实生活中的例子:
从 Simple Factory 扩展我们的门例子。根据您的需求,您可以从木门店获得木门,铁门店获得铁门或相关的商店获得 PVC 门。另外,你可能需要一个有不同技能的伙计来安装门,例如木匠安装木门,焊工安装铁门等等。你可以看到门之间存在依赖关系,木门需要木匠,铁门需要焊工等
通俗解释:
工厂的工厂; 一个将单个但相关/从属的工厂分组在一起而没有指定其具体类别的工厂。
维基百科:
抽象工厂模式提供了一种封装一组具有共同主题但没有指定其具体类的单个工厂的方法
程序示例:
翻译上面的门例子。首先,我们有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;
}
// Wooden factory to return carpenter and wooden door
class WoodenDoorFactory implements DoorFactory
{
public function makeDoor(): Door
{
return new WoodenDoor();
}
public function makeFittingExpert(): DoorFittingExpert
{
return new Carpenter();
}
}
// Iron door factory to get iron door and the relevant fitting expert
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
正如你所看到的,木门工厂封装了carpenter和wooden door并且铁门工厂封装了iron door和welder。因此,它帮助我们确保对于每个创建的门,我们不会得到错误的装配专家。
什么时候使用?
当涉及到不那么简单的,存在相互依赖关系的创建逻辑时使用
👷建造者模式
现实生活中的例子:
想象一下,你在 Hardee's,你下了一个特定的订单,比如说,“Big Hardee”(一种汉堡),他们毫无疑问地把它交给你了; 这是简单工厂的例子。但有些情况下,创建逻辑可能涉及更多步骤。例如,你想要一个定制的 Subway 订单,你有多种选择如何制作你的汉堡,例如你想要什么面包?你想要什么类型的酱汁?你想要什么奶酪?在这种情况下,建造者模式可以解决。
通俗解释:
允许您创建不同风格的对象,同时避免构造函数污染。当一个对象有多种风格时很有用。或者当一个对象创建时涉及很多步骤。
维基百科:
建造者模式是一种创建对象的软件设计模式,其目的是找到伸缩构造器反模式的解决方案。
话虽如此,让我补充说一下伸缩构造函数反模式是什么。某时某刻,我们都见到过如下的构造函数:
public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true)
{
}
如你看到的; 构造函数参数的数量很快就会失控,并且可能难以理解参数的排列。此外,如果您希望将来添加更多选项,此参数列表可能会继续增长。这被称为伸缩构造器反模式。
程序示例:
理智的替代方案是使用建造者模式。首先,我们要制作汉堡
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;
}
}
然后我们有了建设者
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);
}
}
然后就可以这样使用。:
$burger = (new BurgerBuilder(14))
->addPepperoni()
->addLettuce()
->addTomato()
->build();
什么时候使用?
当一个对象可能存在几种类型并避免构造函数伸缩时使用。与工厂模式的主要区别在于:当创建是一步过程时,将使用工厂模式,而当创建是多步骤过程时,将使用建造者模式。
🐑原型模式(Prototype)
现实生活中的例子:
记得多莉?被克隆的羊!让我们不详细介绍,但关键点在于它完全是关于克隆的
通俗解释:
通过克隆现有对象创建对象。
维基百科:
原型模式是软件开发中的一种创建型的设计模式。当要创建的对象类型由原型实例确定时使用它,该实例被克隆以生成新对象。
简而言之,它允许您创建现有对象的副本并根据需要进行修改,而不是从头开始创建对象并进行设置。
程序示例:
在 PHP 中,它可以很容易地使用 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;
}
}
然后它可以像下面一样克隆
$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
您也可以使用魔术方法__clone来修改克隆行为。
什么时候使用?
当需要一个与现有对象类似的对象时,或者与克隆相比,创建的成本会很高。
💍单例模式(Singleton)
现实生活中的例子:
同一时间只能有一个国家的总统。 无论何时打电话,都必须将同一位总统付诸行动。 这里的总统就是单例。
通俗解释:
确保只有特定类的一个对象被创建。
维基百科:
在软件工程中,单例模式是一种软件设计模式,它将类的实例化限制为一个对象。当仅需要一个对象来协调整个系统的操作时,这非常有用。
单例模式实际上被认为是反模式,应该避免过度使用它。它不一定是坏的,可能有一些有效的使用场景,但应谨慎使用,因为它在您的应用程序中引入了一个全局状态,并且在一个地方更改它可能会影响其他地方,并且它可能变得非常难以调试。关于它们的另一个坏处是它使你的代码紧密耦合加上 mock(一种测试方法)单例可能很困难。
程序示例:
要创建单例,请将构造函数设为私有,禁用克隆,禁用扩展并创建静态变量以容纳实例
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
}
}
然后才能使用
$president1 = President::getInstance();
$president2 = President::getInstance();
var_dump($president1 === $president2); // true
结构型设计模式
通俗解释:
结构模式大多关注对象组成,或者换句话说,实体如何相互使用。或者另一种解释是,它们有助于回答“如何构建软件组件?”
维基百科:
在软件工程中,结构设计模式是通过识别实现实体之间关系的简单方法来简化设计的设计模式。
适配器模式(Adapter)
桥梁模式(Bridge)
组合模式(Composite)
装饰模式(Decorator)
门面模式(Facade)
享元模式(Flyweight)
代理模式(Proxy)
🔌适配器模式(Adapter)
现实生活中的例子:
请注意,您的存储卡中有一些照片,需要将它们传输到计算机上。为了传输它们,您需要某种与您的计算机端口兼容的适配器,以便您可以将存储卡连接到您的计算机。在这种情况下,读卡器是适配器。另一个例子是着名的电源适配器; 三脚插头不能连接到双管插座,需要使用电源适配器,使其与双叉插座兼容。另一个例子是翻译人员将一个人所说的话翻译给另一个人
通俗解释:
适配器模式允许您在适配器中包装其他不兼容的对象,使其与另一个类兼容。
维基百科:
在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。它通常用于使现有类与其他类一起工作而无需修改其源代码。
程序示例:
考虑一个有猎人的游戏,他猎杀狮子。
首先,我们有一个Lion所有类型的狮子必须实现的接口
interface Lion
{
public function roar();
}
class AfricanLion implements Lion
{
public function roar()
{
}
}
class AsianLion implements Lion
{
public function roar()
{
}
}
猎人期望狩猎任何Lion接口的实现。
class Hunter
{
public function hunt(Lion $lion)
{
$lion->roar();
}
}
现在,我们需要在游戏中加入一个WildDog,以便猎人也可以狩猎它。但我们不能直接这样做,因为狗有不同的接口。为了使它与我们的猎人兼容,我们将不得不创建一个兼容的适配器
// 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();
}
}
现在在游戏中通过WildDogAdapter可以使用WildDog。
$wildDog = new WildDog();
$wildDogAdapter = new WildDogAdapter($wildDog);
$hunter = new Hunter();
$hunter->hunt($wildDogAdapter);
🚡桥梁模式(Bridge)
现实生活中的例子:
考虑您有一个包含不同页面的网站,您应该允许用户更改主题。你会怎么做?为每个主题创建每个页面的多个副本,或者您只是创建单独的主题并根据用户的首选项加载它们?桥梁模式使你采用第二种方法。即:
用通俗解释:
桥接模式是关于优先于继承的组合。实现细节从层次结构推送到具有单独层次结构的另一个对象。
维基百科:
桥接模式是软件工程中使用的设计模式,旨在“将抽象与其实现分离,以便两者可以独立变化”
程序示例:
从上面翻译我们的 WebPage 示例。这里我们有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();
}
}
和单独的主题层次结构
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';
}
}
以及这两个层次结构
$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";
🌿组合模式(Composite)
现实生活中的例子:
每个组织都由员工组成。每个员工都有相同的特性,即有工资,有一些责任,可能会或可能不会向某人报告,可能会或可能不会有一些下属等。
通俗解释:
组合模式允许客户以统一的方式处理单个对象。
维基百科:
在软件工程中,复合模式是分区设计模式。复合模式描述了一组对象的处理方式与对象的单个实例相同。复合的意图是将对象“组合”成树结构以表示部分整体层次结构。通过实现复合模式,客户可以统一处理单个对象和组合。
程序示例:
以上面的员工为例。这里我们有不同的员工类型
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;
}
}
然后我们有一个由几种不同类型的员工组成的组织
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;
}
}
然后就可以这样使用。
// 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
☕装饰模式(Decorator)
现实生活中的例子:
想象一下,您经营一家提供多种服务的汽车服务店。现在你如何计算收费账单?您选择一项服务并动态地向其添加所提供服务的价格,直到您获得最终成本。这里的每种类型的服务都是一个装饰者。
通俗解释:
装饰器模式允许您通过将对象包装在装饰器类的对象中来动态更改对象在运行时的行为。
维基百科:
在面向对象的编程中,装饰器模式是一种设计模式,它允许将行为静态或动态地添加到单个对象,而不会影响同一类中其他对象的行为。装饰器模式通常用于遵守单一责任原则,因为它允许在具有独特关注区域的类之间划分功能。
程序示例:
让我们以咖啡为例。首先,我们有一个简单的咖啡实现了咖啡接口
interface Coffee
{
public function getCost();
public function getDescription();
}
class SimpleCoffee implements Coffee
{
public function getCost()
{
return 10;
}
public function getDescription()
{
return 'Simple coffee';
}
}
我们想要使代码易于扩展,在需要时允许通过选项修改它。让我们做一些附加组件(装饰器)
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';
}
}
让我们现在喝杯咖啡
$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
📦门面模式(Facade)
现实生活中的例子:
你怎么打开电脑?“按下电源按钮”你说!这就是你所相信的,因为你正在使用计算机在外部提供的简单界面,在内部它必须做很多事情来实现它。这个复杂子系统的简单接口就是一个门面。
通俗解释:
门面模式为复杂的子系统提供了简化的界面。
维基百科:
门面是一个为更大的代码体提供简化的接口的对象,例如一个类库。
程序示例:
以上面我们的计算机为例。这里我们有电脑类
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!";
}
}
在这里,我们有门面
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();
}
}
现在使用立面
$computer = new ComputerFacade(new Computer());
$computer->turnOn(); // Ouch! Beep beep! Loading.. Ready to be used!
$computer->turnOff(); // Bup bup buzzz! Haah! Zzzzz
🍃享元模式(Flyweight)
现实生活中的例子:
你有没有从一些摊位买到新鲜的茶?他们经常制作不止你需要的那一杯茶,并将剩余的留给任何其他客户,以节省资源,例如天然气等。享元模式就是关于共享。
通俗解释:
它用于通过尽可能多地与类似对象共享来最小化内存使用或计算开销。
维基百科:
在计算机编程中,享元是一种软件设计模式。享元是一个通过与其他类似对象共享尽可能多的数据来最小化内存使用的对象; 当一个简单重复的表示会使用不可接受的内存量时,它是一种大量使用对象的方法。
程序化的例子
从上面翻译我们的茶例子。首先,我们有茶类和茶具
// 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];
}
}
然后我们有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
🎱代理模式(Proxy)
现实生活中的例子:
你有没有用过门禁卡进门?打开该门有多种选择,即可以使用门禁卡或按下绕过安检的按钮打开。门的主要功能是打开,但在它上面添加了一个代理来添加一些功能。让我用下面的代码示例更好地解释它。
通俗解释:
使用代理模式,一个类表示另一个类的功能。
维基百科:
代理最一般的形式,是一个充当其他东西的接口的类。代理是一个包装器或代理对象,用来被客户端调用以访问幕后的提供服务的真实对象。使用代理可以简单地转发到真实对象,或者可以提供额外的逻辑。在代理中,可以提供额外的功能,例如当对真实对象的操作是资源密集时进行缓存,或者在调用对象的操作之前检查先决条件。
程序示例:
以上面我们的安全门为例。首先我们有门接口和门的实现
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 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();
}
}
它可以这样被使用
$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
另一个例子是某种数据映射器实现。例如,我最近使用这种模式为 MongoDB 制作了一个 ODM(对象数据映射器),我在使用魔术方法__call()围绕 mongo 类编写了一个代理。所有方法调用被代理到原始 mongo 类,检索结果被原样返回。但在find或findOne的情况下,数据被映射到所需的类对象,返回这个对象来代替Cursor。
行为型设计模式
通俗解释:
它关注对象之间的职责分配。它们与结构型模式的不同之处在于它们不仅指定了结构,还概述了它们之间的消息传递/通信模式。或者换句话说,他们协助回答“如何在软件组件中运行行为?”
维基百科:
在软件工程中,行为设计模式是识别对象之间的共同通信模式并实现这些模式的设计模式。通过这样做,这些模式增加了执行该通信的灵活性。
责任链模式(Chain Of Responsibilities)
命令行模式(Command)
迭代器模式(Iterator)
中介者模式(Mediator)
备忘录模式(Memento)
观察者模式(Observer)
访问者模式(Visitor)
策略模式(Strategy)
状态模式(State)
模板方法模式(Template Method)
🔗责任链模式(Chain Of Responsibilities)
现实生活中的例子:
例如,你的帐户设置有三种付款方式(A,B和C); 每个都有不同的额度。A有 100 美元,B具有 300 美元和C具有 1000 美元,以及支付偏好被选择作为先A再B然后C。你试着购买价值 210 美元的东西。使用责任链,首先A会检查帐户是否可以进行购买,如果是,则进行购买并且责任链破裂。如果不能购买,请求将转发到帐户B来检查金额,如果能购买,责任链破裂,否则请求将继续转发,直到找到合适的处理程序。在这里A,B和C 是链条的链接,整个现象是责任链。
通俗解释:
它有助于构建一系列对象。请求从一端进入并持续从一个对象到另一个对象,直到找到合适的处理程序。
有话要说