24种设计模式

设计模式

设计模式基于六大原则

创建型模式

简单工厂模式

缺点

/**
 * 简单工厂模式
 */
abstract class Fruit
{
    abstract function eat();
}

class Apple extends Fruit
{
    public function eat()
    {
        echo '吃红红的苹果' . PHP_EOL;
    }
}

class Pear extends Fruit
{
    public function eat()
    {
        echo '吃长得像灯泡的梨子' . PHP_EOL;
    }
}

// 水果工厂
class FruitFactory
{
    public function create(string $type): Fruit
    {
        switch ($type) {
            case '苹果':
                // 如果生产一个苹果的逻辑很复杂,可以写在这里,但是调用工厂的时候完全不需要考虑这里
                return new Apple();
            case '梨子':
                return new Pear();
            default:
                throw new \Exception("暂时没有这种水果");
        }
    }
}

class User
{
    public function eat()
    {
        $fruitFactory = new FruitFactory();
        $apple = $fruitFactory->create('苹果');
        $pear = $fruitFactory->create('梨子');

        $apple->eat();
        $pear->eat();
    }
}

$user = new User();
$user->eat();

工厂方法模式

解决问题

/**
 * 工厂方法模式
 * 简单工厂模式,1、如果想加新的水果必须要修改工厂,违背了开闭原则(对修改关闭,对扩展开放)、
 *            2、如果想修改苹果的创建过程,必须要修改工厂。修改梨子,也要修改工厂。违背了单一职责
 */
abstract class Fruit
{
    abstract function eat();
}

class Apple extends Fruit
{
    public function eat()
    {
        echo '吃红红的苹果' . PHP_EOL;
    }
}

class Pear extends Fruit
{
    public function eat()
    {
        echo '吃长得像灯泡的梨子' . PHP_EOL;
    }
}
class AppleFactory
{
    public function create(): Fruit
    {
        // 苹果生产的细节全在这里了
        return new Apple();
    }
}

class PearFactory
{
    public function create(): Fruit
    {
        return new Pear();
    }
}

class User1
{
    public function eat()
    {
        $appleFactory = new AppleFactory();
        $apple = $appleFactory->create();

        $pearFactory = new PearFactory();
        $pear = $pearFactory->create();

        $apple->eat();
        $pear->eat();
    }
}

$user = new User1();
$user->eat();

抽象工厂模式

优点

/**
 * 抽象工厂模式
 * 工厂方法模式的进一步优化,看名字就知道,是抽象化
 */
abstract class Fruit
{
    abstract function eat();
}

class Apple extends Fruit
{
    public function eat()
    {
        echo '吃红红的苹果' . PHP_EOL;
    }
}

class Pear extends Fruit
{
    public function eat()
    {
        echo '吃长得像灯泡的梨子' . PHP_EOL;
    }
}
interface IFactory
{
    public function create(): Fruit;
}

class AppleFactory1 implements IFactory
{
    public function create(): Fruit
    {
        return new Apple();
    }
}

class PearFactory1 implements IFactory
{
    public function create(): Fruit
    {
        return new Pear();
    }
}

class User2
{
    public function eat()
    {
        $appleFactory = new AppleFactory1();
        $apple = $appleFactory->create();

        $pearFactory = new PearFactory1();
        $pear = $pearFactory->create();

        $apple->eat();
        $pear->eat();
    }
}

$user = new User2();
$user->eat();

单例模式

/**
 * 单例模式
 * 只会实例化一次,不能被外部new,构造函数需要private
 */
class Singleton
{
    private static $instance;

    private function __construct()
    {
    }

    public static function getInstance()
    {
        if (self::$instance == null) {
            echo '第一次实例化' . PHP_EOL;
            self::$instance = new self();
        } else {
            echo '已经实例化' . PHP_EOL;
        }
        return self::$instance;
    }
}

$instance = Singleton::getInstance();
$instance = Singleton::getInstance();
$instance = Singleton::getInstance();

建造型模式

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

现在建造者模式主要用来通过链式调用生成不同的配置,比如制作不同配置的珍珠奶茶

/**
 * 建造者模式
 * 创建过程稳定,但是配置多变
 */
class Builder
{
    public string $type;
    public string $size = '中杯';
    public bool $pearl = true;
    public bool $ice = false;

    public function __construct(string $type)
    {
        $this->type = $type;
    }

    public function size(string $size): Builder
    {
        $this->size = $size;
        return $this;
    }

    public function pearl(bool $pearl): Builder
    {
        $this->pearl = $pearl;
        return $this;
    }

    public function ice(bool $ice): Builder
    {
        $this->ice = $ice;
        return $this;
    }

    public function build(): MilkTea
    {
        return new MilkTea($this);
    }
}

class MilkTea
{
    private string $type;
    private string $size;
    private bool $pearl;
    private bool $ice;

    public function __construct(Builder $builder)
    {
        $this->type = $builder->type;
        $this->size = $builder->size;
        $this->pearl = $builder->pearl;
        $this->ice = $builder->ice;
    }

    /**
     * @return string
     */
    public function getType(): string
    {
        return $this->type;
    }

    /**
     * @return string
     */
    public function getSize(): string
    {
        return $this->size;
    }

    /**
     * @return bool
     */
    public function isPearl(): bool
    {
        return $this->pearl;
    }

    /**
     * @return bool
     */
    public function isIce(): bool
    {
        return $this->ice;
    }
}

class User3
{
    public function buyMilkTea()
    {
        $milkTea = (new Builder('原味'))->build();
        $this->show($milkTea);

        $milkTea = (new Builder('巧克力味'))->ice(false)->build();
        $this->show($milkTea);

        $milkTea = (new Builder('草莓味'))->size('大杯')->pearl(false)
            ->ice(true)->build();
        $this->show($milkTea);
    }

    private function show(MilkTea $milkTea)
    {
        if ($milkTea->isPearl()) {
            $pearl = '加珍珠';
        } else {
            $pearl = '不加珍珠';
        }
        if ($milkTea->isIce()) {
            $ice = '加冰';
        } else {
            $ice = '不加冰';
        }
        echo "一份" . $milkTea->getSize() . "、" . $pearl . "、" .
            $ice . "的" . $milkTea->getType() . "奶茶\n";
    }
}

$user = new User3();
$user->buyMilkTea();

原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

/**
 * 原型模式
 * 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
 */
class MilkTea1
{
    public string $type;
    public bool $ice;

    public function clone(): MilkTea1
    {
        $milkTea = new MilkTea1();
        $milkTea->type = $this->type;
        $milkTea->ice = $this->ice;
        return $milkTea;
    }
}

class User4
{
    public function order()
    {
        $milkTeaJay = new MilkTea1();
        $milkTeaJay->type = '原味';
        $milkTeaJay->ice = true;

        echo '周杰伦的奶茶:' . $milkTeaJay->type . ' ' . ($milkTeaJay->ice ? '加冰' : '不加冰') . PHP_EOL;
        $yourMilkTea = $milkTeaJay->clone();

        echo '你的奶茶:' . $yourMilkTea->type . ' ' . ($yourMilkTea->ice ? '加冰' : '不加冰') . PHP_EOL;
    }
}

$user = new User4();
$user->order();

结构型模式

结构型模式是用来设计程序的结构的。结构型模式就像搭积木,将不同的类结合在一起形成契合的结构

适配器模式

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

但适配器模式并不推荐多用。因为未雨绸缪好过亡羊补牢,如果事先能预防接口不同的问题,不匹配问题就不会发生,只有遇到源接口无法改变时,才应该考虑使用适配器。

/**
 * 适配器模式
 * 有相关性但不兼容的结构
 * 不推荐使用
 */
class HomeBattery
{
    public function supply(): int
    {
        return 220;
    }
}

class USBLine
{
    public function charge(int $volt)
    {
        if ($volt != 5) {
            throw new Exception('只能接收5V电压');
        }
        echo '正常充电' . PHP_EOL;
    }
}

class Adapter
{
    function convert(int $homeVolt): int
    {
        return $homeVolt - 215;
    }
}

class User5
{
    public function chageForPhone()
    {
        $homeBattery = new HomeBattery();
        $homeVolt = $homeBattery->supply();
        echo '家庭电源提供的电压是 ' . $homeVolt . "V" . PHP_EOL;

        $adapter = new Adapter();
        $chargeVolt = $adapter->convert($homeVolt);

        echo '使用适配器将家庭电压转换成了 ' . $chargeVolt . "V" . PHP_EOL;
        $usbLine = new USBLine();
        $usbLine->charge($chargeVolt);
    }
}

$user = new User5();
$user->chageForPhone();

桥接模式

将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体模式或接口模式。

合成 / 聚合复用原则:优先使用合成 / 聚合,而不是类继承。


/**
 * 桥接模式
 * 将形状和颜色分离,根据需要对形状和颜色进行组合
 *
 * 如果一个对象有两种或者多种分类方式,并且两种分类方式都容易变化,比如本例中的形状和颜色。
 * 这时使用继承很容易造成子类越来越多,所以更好的做法是把这种分类方式分离出来,让他们独立变化,使用时将不同的分类进行组合即可。
 * 它主要用于 两个或多个同等级的接口
 */
interface IShape
{
    public function draw();
}

class Rectangle implements IShape
{
    protected IColor $color;

    public function setColor(IColor $color)
    {
        $this->color = $color;
    }

    public function draw()
    {
        echo "绘制" . $this->color->getColor() . "矩形\n";
    }
}

class Round implements IShape
{
    protected IColor $color;

    public function setColor(IColor $color)
    {
        $this->color = $color;
    }

    public function draw()
    {
        echo "绘制" . $this->color->getColor() . "圆形\n";
    }
}

class Triangle implements IShape
{
    protected IColor $color;

    public function setColor(IColor $color)
    {
        $this->color = $color;
    }

    public function draw()
    {
        echo "绘制" . $this->color->getColor() . "三角形\n";
    }
}

interface IColor
{
    public function getColor(): string;
}

class Red implements IColor
{

    public function getColor(): string
    {
        return "红";
    }
}

class Blue implements IColor
{

    public function getColor(): string
    {
        return "蓝";
    }
}

class Yellow implements IColor
{

    public function getColor(): string
    {
        return "黄";
    }
}

class Green implements IColor
{

    public function getColor(): string
    {
        return "绿";
    }
}

class User6
{
    public function draw()
    {
        $rectangle = new Rectangle();
        $rectangle->setColor(new Red());
        $rectangle->draw();

        $round = new Round();
        $round->setColor(new Yellow());
        $round->draw();

        $triangle = new Triangle();
        $triangle->setColor(new Green());
        $triangle->draw();
    }
}

$user = new User6();
$user->draw();

组合模式(部分整体模式)

组合模式用于 整体与部分的结构,当整体与部分有相似的结构,在操作时可以被一致对待时,就可以使用组合模式。

/**
 * 组合模式(部分整体模式)
 *
 * 用于整体和部分的结构,当整体与部分有相似的结构,在操作时可以被一致对待时,就可以使用组合模式
 */
abstract class Component
{
    private string $position;

    private string $job;

    public function __construct(string $position, string $job)
    {
        $this->position = $position;
        $this->job = $job;
    }

    public function work()
    {
        echo "我是" . $this->position . ",我正在" . $this->job . PHP_EOL;
    }

    abstract function addComponent(Component $component);

    abstract function removeComponent(Component $component);

    abstract function check();
}

class Manager extends Component
{
    //
    private array $components = [];

    public function __construct(string $position, string $job)
    {
        parent::__construct($position, $job);
    }

    function addComponent(Component $component)
    {
        $this->components[] = $component;
    }

    function removeComponent(Component $component)
    {
        foreach ($this->components as $k => $comp) {
            if ($component == $comp) {
                unset($this->components[$k]);
            }
        }
    }

    public function check()
    {
        $this->work();
        foreach ($this->components as $component) {
            echo '    ';
            $component->check();
        }
    }
}

class Employee extends Component
{
    public function __construct(string $position, string $job)
    {
        parent::__construct($position, $job);
    }

    function addComponent(Component $component)
    {
        echo "职员没有管理权限" . PHP_EOL;
    }

    function removeComponent(Component $component)
    {
        echo "职员没有管理权限" . PHP_EOL;
    }

    function check()
    {
        echo '    ';
        parent::work();
    }
}

class User7
{
    public function main()
    {
        $boss = new Manager('老板', '唱怒放的生命');
        $hr = new Employee('人力资源', '聊微信');
        $pm = new Manager('产品经理', '认真做原型');
        $cfo = new Manager('财务主管', '看韩剧');
        $cto = new Manager('技术主管', '划水');
        $ui = new Employee('设计师', '画画');
        $operator = new Employee('运营人员', '兼职客服');
        $webProgrammer = new Employee('程序员', '学习设计模式');
        $backgroundProgrammer = new Employee('后端程序员', 'CRUD');
        $accountant = new Employee("会计", "背九九乘法表");
        $clerk = new Employee("文员", "给老板递麦克风");

        $boss->addComponent($hr);
        $boss->addComponent($pm);
        $boss->addComponent($cfo);

        $pm->addComponent($ui);
        $pm->addComponent($cto);
        $pm->addComponent($operator);

        $cto->addComponent($webProgrammer);
        $cto->addComponent($backgroundProgrammer);

        $cfo->addComponent($accountant);
        $cfo->addComponent($clerk);

        $boss->check();
    }
}

$user = new User7();
$user->main();

装饰模式

用于增强功能的装饰模式

/**
 * 装饰模式
 * 增强一个类原有的功能,为一个类添加新的功能
 */

// 用于增强功能的装饰模式
// 透明装饰模式可以无限装饰
// 这里的装饰器仅用于增强功能,并不会改变 Me 原有的功能,这种装饰模式称之为 透明装饰模式
interface IBeauty
{
    public function getBeautyValue(): int;
}

class Me implements IBeauty
{
    public function getBeautyValue(): int
    {
        return 100;
    }
}

// 戒指装饰类
class RingDecorator implements IBeauty
{
    protected IBeauty $me;

    public function __construct(IBeauty $me)
    {
        $this->me = $me;
    }

    public function getBeautyValue(): int
    {
        return $this->me->getBeautyValue() + 20;
    }
}

// 耳环装饰类
class EarringDecorator implements IBeauty
{
    protected IBeauty $me;

    public function __construct(IBeauty $me)
    {
        $this->me = $me;
    }

    public function getBeautyValue(): int
    {
        return $this->me->getBeautyValue() + 50;
    }
}

// 项链装饰类
class NecklaceDecorator implements IBeauty
{
    protected IBeauty $me;

    public function __construct(IBeauty $me)
    {
        $this->me = $me;
    }

    public function getBeautyValue(): int
    {
        return $this->me->getBeautyValue() + 80;
    }
}

class User8
{
    public function main()
    {
        $me = new Me();
        echo '我原本的颜值:' . $me->getBeautyValue() . PHP_EOL;

        $meWithNecklace = new NecklaceDecorator($me);
        echo '戴上项链之后,我的颜值:' . $meWithNecklace->getBeautyValue() . PHP_EOL;

        $meWithManyDecorators = new NecklaceDecorator(new RingDecorator(new EarringDecorator($me)));
        echo '戴上耳环,戒指,项链之后,我的颜值:' . $meWithManyDecorators->getBeautyValue() . PHP_EOL;
    }
}

$user = new User8();
$user->main();

用于添加功能的装饰模式

// 用于添加功能的装饰模式
// 也就是说我们并没有修改原有的功能,只是扩展了新的功能,这种模式在装饰模式中称之为 半透明装饰模式
// 半透明模式中无法多次装饰
interface IHouse
{
    public function live();
}

class House implements IHouse
{

    public function live()
    {
        echo '房屋原有的功能:居住功能' . PHP_EOL;
    }
}

// 粘钩装饰器
interface IStickyHookHouse extends IHouse
{
    public function hangThings();
}

class StickyHookDecorator implements IStickyHookHouse
{
    protected IHouse $house;

    public function __construct(IHouse $house)
    {
        $this->house = $house;
    }

    public function live()
    {
        $this->house->live();
    }

    public function hangThings()
    {
        echo '有了粘钩后,新增了挂东西功能' . PHP_EOL;
    }
}

// 镜子装饰器
interface IMirrorHouse extends IHouse
{
    public function lookMirror();
}

class MirrorDecorator implements IMirrorHouse
{
    protected IHouse $house;

    public function __construct(IHouse $house)
    {
        $this->house = $house;
    }

    public function live()
    {
        $this->house->live();
    }

    public function lookMirror()
    {
        echo "有了镜子后,新增了照镜子功能" . PHP_EOL;
    }
}

class User9
{
    public function main()
    {
        $house = new House();
        $house->live();

        $stickyHookHouse = new StickyHookDecorator($house);
        $houseWithStickyHookMirror = new MirrorDecorator($stickyHookHouse);
        $houseWithStickyHookMirror->live();
        $houseWithStickyHookMirror->hangThings();
        $houseWithStickyHookMirror->lookMirror();

    }
}

$user = new User9();
$user->main();

外观模式

封装的思想,将多个子系统封装在一起,提供一个更简洁的接口供外部使用

/**
 * 外观模式(门面模式)
 * 外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,
 * 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式。
 *
 *
 * 外观模式就是这么简单,它使得两种不同的类不用直接交互,而是通过一个中间件——也就是外观类——间接交互。
 * 外观类中只需要暴露简洁的接口,隐藏内部的细节,所以说白了就是封装的思想。
 */
class Browser
{
    public static function open()
    {
        echo '打开浏览器' . PHP_EOL;
    }

    public static function close()
    {
        echo '关闭浏览器' . PHP_EOL;
    }
}
class IDE
{
    public static function open()
    {
        echo '打开IDE' . PHP_EOL;
    }

    public static function close()
    {
        echo '关闭IDE' . PHP_EOL;
    }
}
class Wechat
{
    public static function open()
    {
        echo '打开微信' . PHP_EOL;
    }

    public static function close()
    {
        echo '关闭微信' . PHP_EOL;
    }
}
class Facade
{
    public static function open()
    {
        Browser::open();
        IDE::open();
        Wechat::open();
    }

    public static function close()
    {
        Browser::close();
        IDE::close();
        Wechat::close();
    }

}
class User20
{
    public static function main()
    {
        echo '上班' . PHP_EOL;
        Facade::open();

        echo '下班' . PHP_EOL;
        Facade::close();
    }
}

User20::main();

享元模式

/**
 * 享元模式
 * 享元模式体现的是 程序可复用 的特点,为了节约宝贵的内存,程序应该尽可能地复用,就像《极限编程》
 * 作者 Kent 在书里说到的那样:Don't repeat yourself.简单来说 享元模式就是共享对象,提高复用性
 *
 */

代理模式

和装饰模式类似,但是装饰模式是增强或添加功能,代理模式是为了加以控制

静态代理

/**
 * 代理模式
 *
 * 这个代理类看起来和装饰模式的 FilterInputStream 一模一样,但两者的目的不同
 * ,装饰模式是为了 增强功能或添加功能,代理模式主要是为了加以控制。
 */
// 静态代理
interface IHttp
{
    public function request(string $sendData);

    public function onSuccess(string $receiveData);
}

class HttpUtil implements IHttp
{

    public function request(string $sendData)
    {
        echo '网络请求中...' . PHP_EOL;
    }

    public function onSuccess(string $receiveData)
    {
        echo '网络请求完成。' . PHP_EOL;
    }
}

class HttpProxy implements IHttp
{
    protected HttpUtil $httpUtil;

    public function __construct(HttpUtil $httpUtil)
    {
        $this->httpUtil = $httpUtil;
    }

    public function request(string $sendData)
    {
        echo '发送数据:' . $sendData . PHP_EOL;
        $this->httpUtil->request($sendData);
    }

    public function onSuccess(string $receiveData)
    {
        echo '收到数据:' . $receiveData . PHP_EOL;
        $this->httpUtil->onSuccess($receiveData);
    }
}

class User10
{
    public function main()
    {
        $httpUtil = new HttpUtil();
        $proxy = new HttpProxy($httpUtil);
        $proxy->request('request data');
        $proxy->onSuccess('success data');
    }
}

$user = new User10();
$user->main();

动态代理

动态代理本质上与静态代理没有区别,它的好处是节省代码量。利用反射机制实现动态代理

行为型模式

行为型模式重点关注 类与类之间的交互与协作

责任链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止

/**
 * 责任链模式
 */
// bug
class Bug
{
    public int $value;

    public function __construct(int $value)
    {
        $this->value = $value;
    }
}
// 程序员
abstract class Programmer
{
    protected Programmer $next;

  	// 设置责任链
    public function setNext(Programmer $next)
    {
        $this->next = $next;
    }

    abstract public function handle(Bug $bug);
}
// 新手程序员
class NewbieProgrammer extends Programmer
{
    public function handle(Bug $bug)
    {
        if ($bug->value > 0 && $bug->value <= 20) {
            $this->solve($bug);
        } else if ($this->next != null) {
            $this->next->handle($bug);
        }
    }

    public function solve(Bug $bug)
    {
        echo '菜鸟程序员解决了一个难度为 ' . $bug->value . ' 的bug' . PHP_EOL;
    }
}
// 普通程序员
class NormalProgrammer extends Programmer
{
    public function handle(Bug $bug)
    {
        if ($bug->value > 20 && $bug->value <= 50) {
            $this->solve($bug);
        } else if ($this->next != null) {
            $this->next->handle($bug);
        }
    }

    public function solve(Bug $bug)
    {
        echo '普通程序员解决了一个难度为 ' . $bug->value . ' 的bug' . PHP_EOL;
    }
}
// 优秀程序员
class GoodProgrammer extends Programmer
{
    public function handle(Bug $bug)
    {
        if ($bug->value > 50 && $bug->value <= 100) {
            $this->solve($bug);
        } else if ($this->next != null) {
            $this->next->handle($bug);
        }
    }

    public function solve(Bug $bug)
    {
        echo '优秀程序员解决了一个难度为 ' . $bug->value . ' 的bug' . PHP_EOL;
    }
}

class Client
{
    public static function main()
    {
        $newbie = new NewbieProgrammer();
        $normal = new NormalProgrammer();
        $good = new GoodProgrammer();

        $easy = new Bug(20);
        $middle = new Bug(50);
        $hard = new Bug(100);

        // 组成责任链
        $newbie->setNext($normal);
        $normal->setNext($good);

        // 从菜鸟程序开始,沿着责任链传递
        $newbie->handle($easy);
        $newbie->handle($middle);
        $newbie->handle($hard);
    }

    public static function handleBug(Programmer $programmer, Bug $bug): bool
    {
        if ($programmer->type == '菜鸟' && $bug->value > 0 && $bug->value <= 20) {
            $programmer->solve($bug);
            return true;
        } else if ($programmer->type == '普通' && $bug->value > 20 && $bug->value <= 50) {
            $programmer->solve($bug);
            return true;
        } else if ($programmer->type == '优秀' && $bug->value > 50 && $bug->value <= 100) {
            $programmer->solve($bug);
            return true;
        }
        return false;
    }
}

Client::main();

命令模式

命令模式可以说将封装发挥得淋漓尽致。在我们平时的程序设计中,最常用的封装是将拥有一类职责的对象封装成类,而命令对象的唯一职责就是通过 execute 去调用一个方法,也就是说它将 “方法调用” 这个步骤封装起来了,使得我们可以对 “方法调用” 进行排队、撤销等处理。

命令模式的主要优点如下:

它的主要缺点是:

// 命令模式
class Door
{
    public function openDoor()
    {
        echo "门打开了" . PHP_EOL;
    }

    public function closeDoor()
    {
        echo "门关上了" . PHP_EOL;
    }
}

class Light
{
    public function lightOn()
    {
        echo "打开电灯" . PHP_EOL;
    }

    public function lightOff()
    {
        echo "关上电灯" . PHP_EOL;
    }
}

class Tv
{
    public function turnOnTv()
    {
        echo "打开电视机" . PHP_EOL;
    }

    public function turnOffTv()
    {
        echo "关上电视机" . PHP_EOL;
    }
}

class Music
{
    public function play()
    {
        echo "开始播放音乐" . PHP_EOL;
    }

    public function stop()
    {
        echo "停止播放音乐" . PHP_EOL;
    }
}

class MingV1
{
    public static function main()
    {
        $door = new Door();
        $light = new Light();
        $tv = new Tv();
        $music = new Music();

        $doorSwitch = true;
        $lightSwitch = false;
        $tvSwitch = false;
        $musicSwitch = true;

        if ($doorSwitch) {
            $door->openDoor();
        } else {
            $door->closeDoor();
        }
        if ($lightSwitch) {
            $light->lightOn();
        } else {
            $light->lightOff();
        }
        if ($tvSwitch) {
            $tv->turnOnTv();
        } else {
            $tv->turnOffTv();
        }
        if ($musicSwitch) {
            $music->play();
        } else {
            $music->stop();
        }
    }
}

MingV1::main();


class Operation
{
    const DOOR_OPEN = 1;
    const DOOR_CLOSE = 2;
    const LIGHT_ON = 3;
    const LIGHT_OFF = 4;
    const TV_TURN_ON = 5;
    const TV_TURN_OFF = 6;
    const MUSIC_PLAY = 7;
    const MUSIC_STOP = 8;
}

class MingV2
{
    protected static int $operation;
    protected static SplStack $operations;

    protected static Door $door;
    protected static Light $light;
    protected static Tv $tv;
    protected static Music $music;

    protected static bool $doorSwitch = true;
    protected static bool $lightSwitch = false;
    protected static bool $tvSwitch = false;
    protected static bool $musicSwitch = true;


    public static function main()
    {
        self::$operations = new SplStack();

        self::$door = new Door();
        self::$light = new Light();
        self::$tv = new Tv();
        self::$music = new Music();

        self::$doorSwitch = true;
        self::$lightSwitch = false;
        self::$tvSwitch = false;
        self::$musicSwitch = true;

        if (self::$doorSwitch) {
            self::$operations->push(Operation::DOOR_OPEN);
            self::$door->openDoor();
        } else {
            self::$operations->push(Operation::DOOR_CLOSE);
            self::$door->closeDoor();
        }
        if (self::$lightSwitch) {
            self::$operations->push(Operation::LIGHT_ON);
            self::$light->lightOn();
        } else {
            self::$operations->push(Operation::LIGHT_OFF);
            self::$light->lightOff();
        }
        if (self::$tvSwitch) {
            self::$operations->push(Operation::TV_TURN_ON);
            self::$tv->turnOnTv();
        } else {
            self::$operations->push(Operation::TV_TURN_OFF);
            self::$tv->turnOffTv();
        }
        if (self::$musicSwitch) {
            self::$operations->push(Operation::MUSIC_PLAY);
            self::$music->play();
        } else {
            self::$operations->push(Operation::MUSIC_STOP);
            self::$music->stop();
        }
    }


    public static function btnUndo()
    {
        if (self::$operations->isEmpty()) {
            return;
        }
        self::$operation = self::$operations->pop();
        switch (self::$operation) {
            case Operation::DOOR_OPEN:
                self::$door->closeDoor();
                break;
            case Operation::DOOR_CLOSE:
                self::$door->openDoor();
                break;
            case Operation::LIGHT_ON:
                self::$light->lightOff();
                break;
            case Operation::LIGHT_OFF:
                self::$light->lightOn();
                break;
            case Operation::TV_TURN_ON:
                self::$tv->turnOffTv();
                break;
            case Operation::TV_TURN_OFF:
                self::$tv->turnOnTv();
                break;
            case Operation::MUSIC_PLAY:
                self::$music->stop();
                break;
            case Operation::MUSIC_STOP:
                self::$music->play();
                break;
        }
    }
}

MingV2::main();
MingV2::btnUndo();
MingV2::btnUndo();
MingV2::btnUndo();

interface ICommand
{
    public function execute();

    public function undo();
}
class DoorOpenCommand implements ICommand
{
    private Door $door;

    public function setDoor(Door $door)
    {
        $this->door = $door;
    }

    public function execute()
    {
        $this->door->openDoor();
    }

    public function undo()
    {
        $this->door->closeDoor();
    }
}
class DoorCloseCommand implements ICommand
{
    private Door $door;

    public function setDoor(Door $door)
    {
        $this->door = $door;
    }

    public function execute()
    {
        $this->door->closeDoor();
    }

    public function undo()
    {
        $this->door->openDoor();
    }
}
class LightOnCommand implements ICommand
{
    private Light $light;

    public function setDoor(Light $light)
    {
        $this->light = $light;
    }

    public function execute()
    {
        $this->light->lightOn();
    }

    public function undo()
    {
        $this->light->lightOff();
    }
}
class LightOffCommand implements ICommand
{
    private Light $light;

    public function setDoor(Light $light)
    {
        $this->light = $light;
    }

    public function execute()
    {
        $this->light->lightOff();
    }

    public function undo()
    {
        $this->light->lightOn();
    }
}
class MingV3
{
    protected static SplStack $commands;
    protected static ICommand $lastCommand;
    protected static Door $door;
    protected static Light $light;

    protected static bool $doorSwitch = true;
    protected static bool $lightSwitch = false;


    public static function main()
    {
        self::$commands = new SplStack();
        self::$door = new Door();
        self::$light = new Light();

        $doorOpenCommand = new DoorOpenCommand();
        $doorCloseCommand = new DoorCloseCommand();
        $doorOpenCommand->setDoor(self::$door);
        $doorCloseCommand->setDoor(self::$door);

        $lightOnCommand = new LightOnCommand();
        $lightOffCommand = new LightOffCommand();
        $lightOnCommand->setDoor(self::$light);
        $lightOffCommand->setDoor(self::$light);

        self::$doorSwitch = true;
        self::$lightSwitch = false;

        self::handleCommand(self::$doorSwitch, $doorOpenCommand, $doorCloseCommand);
        self::handleCommand(self::$lightSwitch, $lightOnCommand, $lightOffCommand);
    }

    protected static function handleCommand(bool $isChecked, ICommand $openCommand, ICommand $closeCommand)
    {
        if ($isChecked) {
            self::$commands->push($openCommand);
            $openCommand->execute();
        } else {
            self::$commands->push($closeCommand);
            $closeCommand->execute();
        }
    }


    public static function btnUndo()
    {
        if (self::$commands->isEmpty()) {
            return;
        }
        self::$lastCommand = self::$commands->pop();
        self::$lastCommand->undo();
    }
}

echo "---------------------" . PHP_EOL;
MingV3::main();
MingV3::btnUndo();
MingV3::btnUndo();
MingV3::btnUndo();

解释器模式

解释器模式(Interpreter Pattern):给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

迭代器模式

迭代器模式(Iterator Pattern):提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

那么有什么更好的方式吗?使得外部类只能读取此列表中的数据,无法修改其中的任何数据,保证其安全性。

分析可知,我们可以通过提供两个方法实现此效果:

中介者模式

总而言之,中介者模式就是用于将类与类之间的 多对多关系 简化成 多对一、一对多关系 的设计模式,它的定义如下:

中介者模式(Mediator Pattern):定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。

中介者模式的缺点也很明显:由于它将所有的职责都移到了中介者类中,也就是说中介类需要处理所有类之间的协调工作,这可能会使中介者演变成一个超级类。所以使用中介者模式时需要权衡利弊。

// 中介者模式
class Group
{
    public int $money = 0;
}

class Player
{
    public int $money = 100;

    public Group $group;

    public function __construct(Group $group)
    {
        $this->group = $group;
    }

    public function change(int $money)
    {
        $this->group->money += $money;
        $this->money += $money;
    }
}
class ZhongJie
{
    protected static Iterator $players;
    public static function main()
    {
        $group = new Group();

        $player1 = new Player($group);
        $player2 = new Player($group);
        $player3 = new Player($group);
        $player4 = new Player($group);

        $player1->change(10);
        $player2->change(15);
        $player3->change(-13);
        $player4->change(-19);

        self::$players = new ArrayIterator([$player1, $player2, $player3, $player4]);
    }

    public static function result()
    {
        $result = "四人剩余的钱为:";
        foreach (self::$players as $player) {
            $result .= $player->money . ";";
        }
        echo $result . PHP_EOL;
    }
}

ZhongJie::main();
ZhongJie::result();

备忘录模式

这才是完整的备忘录模式。这个设计模式的定义如下:

备忘录模式:在不破坏封装的条件下,通过备忘录对象存储另外一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。

备忘录模式的优点是:

缺点是:

// 备忘录模式
class Memento
{
    public int $life;
    public int $magic;

    public function __construct(int $life, int $magic)
    {
        $this->life = $life;
        $this->magic = $magic;
    }
}
class PlayerBoy
{
    // 生命值
    private int $life = 300;
    // 魔法值
    private int $magic = 100;

    public function fightBoss() {
        $this->life -= 100;
        $this->magic -= 100;
        if ($this->life <= 0) {
            echo "壮烈牺牲" . PHP_EOL;
        } else {
            echo "正在打boss" . PHP_EOL;
        }
}

    public function saveState(): Memento
    {
        return new Memento($this->life, $this->magic);
    }

    public function restoreState(Memento $memento)
    {
        echo "开始读档" . PHP_EOL;
        $this->life = $memento->life;
        $this->magic = $memento->magic;
    }
}
class Back
{
    public static function main()
    {
        $player = new PlayerBoy();

        // 存档
        $memento = $player->saveState();

        // 打boss
        $player->fightBoss();
        // 打boss
        $player->fightBoss();
        // 打boss
        $player->fightBoss();

        // 读档
        $player->restoreState($memento);

        // 打boss
        $player->fightBoss();
    }
}

Back::main();

观察者模式

// 观察者模式
interface Observer
{
    public function update(string $event);
}
class Observable
{
    protected array $observers = [];

    public function addObserver(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function removeObserver(Observer $observer)
    {
        foreach ($this->observers as $k => $o) {
            if ($observer === $o) {
                unset($this->observers[$k]);
            }
        }
    }

    public function notifyObservers(string $event)
    {
        foreach ($this->observers as $observer) {
            /**
             * @var $observer Observer
             */
            $observer->update($event);
        }
    }
}

class PoliceObserver implements Observer
{

    public function update(string $event)
    {
        echo "警察收到消息,罪犯在 " . $event . PHP_EOL;
    }
}

class CriminalObservable extends Observable
{
    public function crime(string $event)
    {
        echo "罪犯正在:" . $event . PHP_EOL;
        $this->notifyObservers($event);
    }
}
class Obs
{
    public static function main()
    {
        $zhangSan = new CriminalObservable();
        $police1 = new PoliceObserver();
        $police2 = new PoliceObserver();
        $police3 = new PoliceObserver();
        $zhangSan->addObserver($police1);
        $zhangSan->addObserver($police2);
        $zhangSan->addObserver($police3);
        $zhangSan->crime("放狗咬人");
    }
}

Obs::main();

状态模式

状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

通俗地说,状态模式就是一个关于多态的设计模式。

如果一个对象有多种状态,并且每种状态下的行为不同,一般的做法是在这个对象的各个行为中添加 if-else 或者 switch-case 语句。但更好的做法是为每种状态创建一个状态对象,使用状态对象替换掉这些条件判断语句,使得状态控制更加灵活,扩展性也更好。

// 状态模式
interface IUser
{
    public function mockInterview();
}
interface ISwitchState
{
    public function purchasePlus();

    public function expire();
}
class Normal implements IUser
{

    public function mockInterview()
    {
        echo "模拟面试是 Plus 会员专享功能" . PHP_EOL;
    }
}
class Plus implements IUser
{

    public function mockInterview()
    {
        echo "开始模拟面试" . PHP_EOL;
    }
}
class UserL implements IUser, ISwitchState
{
    protected IUser $state;

    public function __construct(IUser $state)
    {
        $this->state = $state;
    }

    public function mockInterview()
    {
        $this->state->mockInterview();
    }

    public function purchasePlus()
    {
        $this->state = new Plus();
    }

    public function expire()
    {
        $this->state = new Normal();
    }
}

class State
{
    public static function main()
    {
        $state = new Normal();
        $user = new UserL($state);

        $user->mockInterview();

        $user->purchasePlus();
        $user->mockInterview();

        $user->expire();
        $user->mockInterview();
    }
}
State::main();

策略模式

策略模式用一个成语就可以概括 —— 殊途同归。当我们做同一件事有多种方法时,就可以将每种方法封装起来,在不同的场景选择不同的策略,调用不同的方法。

策略模式(Strategy Pattern):定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

通过简单工厂模式与策略模式的结合,我们最大化地减轻了客户端的压力。这是我们第一次用到混合模式,但实际开发中会遇到非常多的混合模式,学习设计模式的过程只能帮助我们各个击破,真正融会贯通还需要在实际开发中多加操练。

需要注意的是,策略模式与状态模式非常类似,甚至他们的 UML 类图都是一模一样的。两者都是采用一个变量来控制程序的行为。策略模式通过不同的策略执行不同的行为,状态模式通过不同的状态值执行不同的行为。两者的代码很类似,他们的区别主要在于程序的目的不同。

// 策略模式
interface ISort
{
    public function sort(array $arr): array;
}

class BubbleSort implements ISort
{
    public function sort(array $arr): array
    {
        for ($i = 0; $i < count($arr) - 1; $i++) {
            // 每次冒泡取最小值放到下面
            for ($j = 0; $j < count($arr) - 1 - $i; $j++) {
                if ($arr[$j] > $arr[$j + 1]) {
                    $tmp = $arr[$j];
                    $arr[$j] = $arr[$j + 1];
                    $arr[$j + 1] = $tmp;
                }
            }
        }
        return $arr;
    }
}

class SelectSort implements ISort
{
    public function sort(array $arr): array
    {
        for ($i = 0; $i < count($arr) - 1; $i++) {
            $minIndex = $i;
            // 取最小值和当前第一个比较,然后交换
            for ($j = $i + 1; $j < count($arr); $j++) {
                if ($arr[$j] < $arr[$minIndex]) {
                    $minIndex = $j;
                }
            }
            $tmp = $arr[$i];
            $arr[$i] = $arr[$minIndex];
            $arr[$minIndex] = $tmp;
        }
        return $arr;
    }
}

class InsertSort implements ISort
{
    public function sort(array $arr): array
    {
        for ($i = 1; $i < count($arr); $i++) {
            $current = $arr[$i];
            $j = $i - 1;

            // 取最小的值放到当前第一个位置,其他位置后移
            while ($j >= 0 && $current < $arr[$j]) {
                $arr[$j + 1] = $arr[$j];
                $j--;
            }

            $arr[$j + 1] = $current;
        }
        return $arr;
    }
}

class Sort implements ISort
{
    protected ISort $sort;

    public function __construct(int $strategy)
    {
        $this->setStrategy($strategy);
    }

    public function sort(array $arr): array
    {
        return $this->sort->sort($arr);
    }

    public function setStrategy(int $strategy)
    {
        switch ($strategy) {
            case SortStrategy::BUBBLE_SORT:
                $this->sort = new BubbleSort();
                break;
            case SortStrategy::SELECTION_SORT:
                $this->sort = new SelectSort();
                break;
            case SortStrategy::INSERT_SORT:
                $this->sort = new InsertSort();
                break;
        }
    }
}

class SortStrategy
{
    const BUBBLE_SORT = 1;
    const SELECTION_SORT = 2;
    const INSERT_SORT = 3;
}

class SortC
{
    public static function main()
    {
        $arr = [1, 5, 4, 3, 7, 9, 8, 2];
        $sort = new Sort(SortStrategy::BUBBLE_SORT);
        $arr = $sort->sort($arr);
        print_r($arr);

        $arr = [12, 15, 11, 1, 5, 4, 3, 7, 9, 8, 2];
        $sort = new Sort(SortStrategy::SELECTION_SORT);
        $arr = $sort->sort($arr);
        print_r($arr);

        $arr = [12, 15, 11, 1, 5, 4, 3, 7, 9, 0, 2];
        $sort = new Sort(SortStrategy::INSERT_SORT);
        $arr = $sort->sort($arr);
        print_r($arr);
    }
}

SortC::main();

模板方法模式

模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

通俗地说,模板方法模式就是一个关于继承的设计模式。

在使用模板方法模式时,我们可以为不同的模板方法设置不同的控制权限:

// 模板模式
abstract class LeaveRequest
{
    public function request()
    {
        echo "本人 ";
        echo $this->name();
        echo " 因 ";
        echo $this->reason();
        echo " 需请假 ";
        echo $this->duration();
        echo " 天,望批准" . PHP_EOL;
    }

    abstract function name(): string;

    abstract function reason(): string;

    abstract function duration(): string;
}

class MyLeaveRequest extends LeaveRequest
{

    function name(): string
    {
        return "刘夫田";
    }

    function reason(): string
    {
        return "家里有事";
    }

    function duration(): string
    {
        return "10";
    }
}
(new MyLeaveRequest())->request();

访问者模式

访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

这就是访问者模式,它的核心思想其实非常简单,就是第一小节中体现的将 数据的结构对数据的操作 分离。之所以说它复杂,主要在于大多数语言都是单分派语言,所以不得不模拟出一个双重分派,也就是用重写方法的动态分派特性将重载方法也模拟成动态分派

但模拟双重分派只是手段,不是目的。有的文章中说模拟双重分派是访问者模式的核心,还有的文章中说双分派语言不需要访问者模式,笔者认为这些说法都有点舍本逐末了。

// 访问者模式
class Restaurant
{
    private string $lobster = 'lobster';
    private string $watermelon = 'watermelon';
    private string $steak = 'steak';
    private string $banana = 'banana';

    public function welcome(IVisitor $visitor)
    {
        $visitor->chooseLobster($this->lobster);
        $visitor->chooseWatermelon($this->watermelon);
        $visitor->chooseSteak($this->steak);
        $visitor->chooseBanana($this->banana);
    }
}
interface IVisitor
{
    public function chooseLobster(string $lobster);

    public function chooseWatermelon(string $watermelon);

    public function chooseSteak(string $steak);

    public function chooseBanana(string $banana);
}
class Aurora implements IVisitor
{

    public function chooseLobster(string $lobster)
    {
        echo "Aurora gets a " . $lobster . PHP_EOL;
    }

    public function chooseWatermelon(string $watermelon)
    {
        echo "Aurora gets a " . $watermelon . PHP_EOL;
    }

    public function chooseSteak(string $steak)
    {
        echo "Aurora doesn't like " . $steak . PHP_EOL;
    }

    public function chooseBanana(string $banana)
    {
        echo "Aurora doesn't like " . $banana . PHP_EOL;
    }
}
class Choose
{
    public static function main()
    {
        $restaurant = new Restaurant();
        $aurora = new Aurora();
        $restaurant->welcome($aurora);
    }
}
Choose::main();

abstract class Food {
    public abstract function name(): string;

    // Food 中添加 accept 方法,接收访问者
    public abstract function accept(IVisitor1 $visitor1);
}
class Lobster extends Food
{

    public function name(): string
    {
        return "lobster";
    }

    public function accept(IVisitor1 $visitor1)
    {
        $visitor1->chooseLobster($this);
    }
}
class Watermelon extends Food
{

    public function name(): string
    {
        return "watermelon";
    }

    public function accept(IVisitor1 $visitor1)
    {
        $visitor1->chooseWatermelon($this);
    }
}
class Steak extends Food
{

    public function name(): string
    {
        return "steak";
    }

    public function accept(IVisitor1 $visitor1)
    {
        $visitor1->chooseSteak($this);
    }
}
class Banana extends Food
{

    public function name(): string
    {
        return "banana";
    }

    public function accept(IVisitor1 $visitor1)
    {
        $visitor1->chooseBanana($this);
    }
}

interface IVisitor1
{
    public function chooseLobster(Lobster $lobster);
    public function chooseWatermelon(Watermelon $watermelon);
    public function chooseSteak(Steak $steak);
    public function chooseBanana(Banana $banana);
}

class Aurora1 implements IVisitor1
{
    public function chooseLobster(Lobster $lobster)
    {
        echo "Aurora gets a " . $lobster->name() . PHP_EOL;
    }

    public function chooseWatermelon(Watermelon $watermelon)
    {
        echo "Aurora gets a " . $watermelon->name() . PHP_EOL;
    }

    public function chooseSteak(Steak $steak)
    {
        echo "Aurora doesn't like " . $steak->name() . PHP_EOL;
    }

    public function chooseBanana(Banana $banana)
    {
        echo "Aurora doesn't like " . $banana->name() . PHP_EOL;
    }
}

class Aurora2 implements IVisitor1
{

    public function chooseLobster(Lobster $lobster)
    {
        echo "Aurora2 gets a " . $lobster->name() . PHP_EOL;
    }

    public function chooseWatermelon(Watermelon $watermelon)
    {
        echo "Aurora2 gets a " . $watermelon->name() . PHP_EOL;
    }

    public function chooseSteak(Steak $steak)
    {
        echo "Aurora2 doesn't like " . $steak->name() . PHP_EOL;
    }

    public function chooseBanana(Banana $banana)
    {
        echo "Aurora2 doesn't like " . $banana->name() . PHP_EOL;
    }
}

class Restaurant1
{
    private function prepareFoods(): array
    {
        $foods = [];
        for ($i=0;$i < 10;$i++) {
            $foods[] = new Lobster();
            $foods[] = new Watermelon();
            $foods[] = new Steak();
            $foods[] = new Banana();
        }
        return $foods;
    }

    public function welcome(IVisitor1 $visitor1)
    {
        $foods = $this->prepareFoods();

        /**
         * @var Food $food
         */
        foreach ($foods as $food) {
            $food->accept($visitor1);
        }
    }
}
class Choose1
{
    public static function main()
    {
        $restaurant =  new Restaurant1();
        $aurora = new Aurora1();
        $aurora2 = new Aurora2();
        $restaurant->welcome($aurora);
        $restaurant->welcome($aurora2);
    }
}

Choose1::main();