十二种常见设计模式代码详解

零:设计模式分类

设计模式有创建型模式、结构型模式与行为型模式

  • 创建型:单例模式、工厂模式(简单工厂,工厂方法,抽象工厂)
  • 结构型:适配器模式、门面模式、装饰器模式、注册树模式、代理模式、管道模式
  • 行为型:策略模式、观察者模式、命令模式、迭代器模式

1. 创建型模式

创建型模式的一个重要思想就是封装,利用封装,把直接获得一个对象改为通过一个接口获得一个对象

  • 单例模式:我们把对象的生成从new改为通过一个静态方法,通过静态方法的控制,使得我们总是返回同一个实例给调用者,确保了系统只有一个实例。
  • 工厂模式:与单例模式相同,生成对象改为接口,还可以通过传参实例化不同的类
    • 简单工厂:为不同的类提供一个生产对象的工厂。要获取类的对象只能通过工厂生产获取
    • 工厂方法模式:为工厂方法提供接口,规范接口方法。每一个工厂针对一个类生产对象
    • 抽象工厂模式:在不指定具体类的情况下创建一系列相关或依赖对象,通常创建的类都实现相同的接口

2. 结构型模式

解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题

种类:

  • 装饰器模式
  • facade门面模式
  • 注册树模式(IOC/DI)
  • 适配器模式
  • Pipeline模式
  • 代理模式

3. 行为型模式

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎么相互协作共同完成单个对象都无法单独完成的任务,它设计算法与对象间职责的分配

种类:

  • 策略模式
  • 观察者模式
  • 命令模式
  • 迭代器模式

一:单例模式

单例模式(singleton),即一种类的设计只会最多产生一个对象的设计思想
在这里插入图片描述

作用:

  • 主要用在数据库应用中,因为一个应用中会存在大量的数据库操作,使用单例模式则可以避免大量的new操作消耗资源。
  • 如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现
  • 在一次页面请求中,便于进行调试,因为所有的代码(例如数据库操作类)会集中在一个类中,我们可以在类中设置钩子,输出日志,从而避免到处var_dump、echo

应用场景:数据库连接、缓存操作、分布式存储

单例模式的要点:

  • 构造函数需要标记为private(访问控制:防止外部代码使用new操作符创建对象),单例类不能在其他类中实例化,只能被其自身实例化
  • 拥有一个保存类的实例的静态成员变量
  • 拥有一个访问这个实例的公共的静态方法(常用getInstance()方法进行实例化单例类,通过instanceof操作符可以检测到类是否已经被实例化)
    • 简称"三私一公":
    • 私有化构造方法:不让外部产生多个对象
    • 私有化克隆方法:不允许对象被克隆对象产生新对象
    • 私有化静态属性:运行进入类内部产生对象
    • 公有化静态方法:保存已经产生的对象
<?php
/*
 * 单例模式
 */

// 数据库操作类
class Dbsql{

    // 私有属性存储对象
    private static $ins = null;

    // 私有化构造方法,避免在类外产生对象
    private function __construct(){}

    // 公有静态方法,创建实例
    public static function createIns()
    {
        // 不存在对象就创建对象
        self::$ins == null ? new self() : self::$ins;
        return self::$ins;
    }

    // 私有化克隆方法,避免在类外克隆对象
    private function __clone(){}

    // 功能方法
    // ...
}

$m1 = Dbsql::createIns();
$m2 = Dbsql::createIns();

var_dump($m1 === $m2);  // bool(true)

二:工厂模式

工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。

优点:如果你想更改所实例化的类名等,则只需更改该工厂方法内容,不需要注意寻找代码中具体实例化的地方(new处)修改。为系统结构提供零花的动态扩展机制,减少了耦合。

分类:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

在这里插入图片描述

1. 简单工厂模式

简单工厂模式又称静态工厂模式,之所以这么说,是因为简单工厂模式是通过一个静态方法来创建对象的。

<?php
/*
 * 简单工厂
 */
class DbMysql{
    public function conn()
    {
        echo "连接Mysql";
    }
}

class DbSqlit
{
    public function conn()
    {
        echo "连接sqlite";
    }
}

class DbFactory
{
    public static function createIns($type)
    {
        // 这么写的话,如果我要连接Oracle,就需要修改代码,不符合开闭原则
        switch ($type)
        {
            case "mysql":
                return new DbMysql();
            case "sqlit":
                return new DbSqlit();
            default:
                throw new ErrorException("error type!");
        }
    }
}

// 客户端
$mysql = DbFactory::createIns("mysql");
$mysql->conn();

$mysql = DbFactory::createIns("sqlit");
$mysql->conn();

2. 工厂方法模式

简单工厂模式实现了产品类的代码跟客户端代码分离,但会有一个问题,优秀的代码是符合开闭原则,如果你要加一个C类产品,就要修改工厂类里面的代码,也就是要增加语句如:switch—case,对于这个问题,工厂方法模式可以解决

工厂方法模式就是为配一个产品提供一个独立的工厂类,通过不同的工厂实例来创建不同的产品实例。

优点:

  • 拥有良好封装性,代码结构清晰。对于每一个对象的创建都是有条件约束的。如:调用一个具体的产品对象,只需要知道这个产品的类名和约束参数就可以了,不用知道创建对象自身的复杂程度,降低模块之间的耦合度。
  • 拥有良好的拓展性,新增一个产品类,只需要适当的增加工厂类或者扩展一个工厂类,如下面这个例子,当需要增加一个数据库Oracle的操作,只需要增加一个Oracle类,厂类不用修改任务就可以完成系统扩展。
  • 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,他只关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。

使用场景:

  • 支付宝、微信、银联的连接方式(connectMode),支付方式(payMode)。使用工厂模式,客户就不需要知道具体的连接方式和支付方式了,只需要调用connectMode和payMode即可
  • MySQL、SQL Server、Oracle等数据库的连接方式(connectMode)、查询方式(selectMode)等操作可以使用工厂模式进行封装
<?php
/*
 * 工厂方法
 */
interface Db
{
    public function conn();
}

class DbMysql implements Db
{
    public function conn()
    {
        echo "连接mysql";
    }
}

class DbSqlit implements Db
{
    public function conn()
    {
        echo "连接Sqlit";
    }
}

interface Factory
{
    public static function createIns();
}

class MysqlFactory implements Factory
{
    public static function createIns()
    {
        return new DbMysql();
    }
}

$mysql = MysqlFactory::createIns();

3. 抽象工厂模式

抽象工厂模式的用意为:给客户提供一个接口,可以创建多个产品族中的产品对象,而且使用抽象工厂模式还要满足条件:

  • 系统中有多个产品族,而系统一次只可能消费其中一族产品
  • 同属于一个产品族的产品可以使用

产品族:位于不同产品等级结构中,功能相关联的产品组成的家族。下面例子中的汽车和空调就是两个产品树,奔驰C200使用了格力某型号空调,那他俩就是一个产品族,同理,奥迪A4和海尔某型号空调也是一个产品族

在这里插入图片描述

<?php
/*
 * 抽象工厂
 */
interface AutoProduct
{
    public function dirve();
}

// 奥迪A4
class AudiA4Product implements AutoProduct
{
    public function dirve()
    {
        echo "奥迪A4";
    }
}

// 奔驰C200
class BenzC200Product implements AutoProduct
{
    public function dirve()
    {
        echo "奔驰C200";
    }
}

interface AirCondition
{
    public function blow();
}

// 格力
class GreeAirCondition implements AirCondition
{
    public function blow()
    {
        echo "格力空调";
    }
}

// 海尔
class HeierAirCondition implements AirCondition
{
    public function blow()
    {
        echo "海尔空调";
    }
}

# 工厂接口
interface Factory
{
    public function getAuto();
    public function getAirCondition();
}

# 工厂A  奔驰C200 + 格力
class AFactory implements Factory
{
    public function getAuto()
    {
        return new BenzC200Product();
    }
    public function getAirCondition()
    {
        return new GreeAirCondition();
    }

    public static function createIns()
    {}
}

# 工厂B  奥迪4 + 海尔
class BFactory implements Factory
{
    public function getAuto()
    {
        return new AudiA4Product();
    }
    public function getAirCondition()
    {
        return new HeierAirCondition();
    }

    public static function createIns()
    {}
}

// 工厂运作
$auto_carA = AFactory::getAuto();
$auto_airA = AFactory::getAirCondition();

$auto_carB = BFactory::getAuto();
$auto_airB = BFactory::getAirCondition();

三:适配器模式

概念:将某个类的接口转换成与另一个接口兼容。适配器通过将原始接口进行转换,给用户提供一个兼容接口,使得原来因为接口不同而无法一起使用的类可以得到兼容
在这里插入图片描述
应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库

<?php
/*
 * 适配器模式
 */
interface Weather
{
    public function show();
}

class PHP_Weather implements Weather
{
    public function show()
    {
        // 得到的天气信息
        $weather = [
            "weather"   => "小雨",
            "tep"       => 6,
            "win"       => 3
        ];
        return serialize($weather);
    }
}

$weather = new PHP_Weather();
$info = unserialize($weather->show());
echo "天气:{$info['weather']}<br>";
echo "温度:{$info['tep']}<br>";
echo "风力:{$info['win']}<br>";

// 把上面的类变成兼容,使得py、java能直接使用
interface WeatherAdapter
{
    public function getWeather();
}
// java接入的接口
class Java_Weather implements WeatherAdapter
{
    protected $weather;
    public function __construct(Weather $weather) {
        $this->weather = $weather;
    }
    public function getWeather()
    {
        $info = unserialize($this->weather->show());
        // 即转为json格式
        return json_encode($info);
    }
}

四:装饰器模式

装饰器模式又叫装饰者模式,装饰模式是在不必改变原类文件和使用继承的情况下,动态的拓展一个对象的功能。他是通过创建一个包装对象,也就是装饰来包裹真实的对象。

实例:

  • 组件对象的接口:可以给这些对象动态的添加职责
  • 所有装饰器的父类:需要定义一个与组件接口一致的接口,并持有一个Component对象,该对象其实就是被装饰的对象
  • 具体的装饰器类:实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。

使用场景:

  • 需要动态的给一个对象添加功能,这些功能可以再动态的撤销
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实
  • 当不能采用生成子类的方法进行扩充时.一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸式增长。另一种情况可能是因为类定于被隐藏,或类定义不能用于生成子类
<?php
/*
 * 装饰器模式
 */

// 组件对象接口
interface IComponent
{
    // 动态的给对象添加功能
    public function display();
}

// 待装饰的对象
class Person implements IComponent
{
    protected $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function display()
    {
        echo "装饰的" . $this->name . "<br>";
    }
}

// 所有装饰器的父类
class Clothes implements IComponent
{
    protected $component;
    public function Decorate(IComponent $component)
    {
        $this->component = $component;
    }
    public function display()
    {
        if(!empty($this->component)) {
            $this->component->display();
        }
    }
}

// 具体的装饰器
class Shoes extends Clothes
{
    public function display()
    {
        echo "鞋子";
        parent::display();
    }
}
class Overcoat extends Clothes
{
    public function display()
    {
        echo "外套";
        parent::display();
    }
}

$qd = new Person("乔丹");

$shoes = new Shoes();
$shoes->Decorate($qd);
$shoes->display();

$coat = new Overcoat();
$coat->Decorate($qd);
$coat->display();

五:注册树模式

注册树模式也叫注册模式或注册器模式,顾名思义,注册树就是把对象实例注册到一颗全局的对象树上,需要对象的时候就从书上取下来,就好比书上长的果子,需要的时候就摘一个下来,只是这个对象树的果子是摘不完的

不管前面用单例还是工厂建立的对象,都是一个个游兵散将,没有得到很好的管理,那如果用了注册树模式,就可以把创建出来的对象注册到全局树上,需要的时候取下来用,可以很好的管理创建的对象

<?php
/*
 * 注册树模式
 */
class DbMysql
{
    public function conn()
    {
        echo "连接Mysql";
    }
}
class DbSqlite
{
    public function conn()
    {
        echo "连接sqlite";
    }
}
class MysqlFactory
{
    public static function getIns()
    {
        return new DbMysql();
    }
}
class SqliteFactory
{
    public static function getIns()
    {
        return new DbSqlite();
    }
}

// 注册数实现存储对象(IOC控制反转思想)
class RegisterTree
{
    protected static $objects;
    // 添加对象到注册树中
    public static function set($alias, $object)
    {
        self::$objects[$alias] = $object;
    }
    // 从注册树中获取对象
    public static function get($alias)
    {
        return self::$objects[$alias];
    }
    // 销毁注册树上的对象
    public function __unset($alias)
    {
        unset(self::$objects[$alias]);
    }
}

// 注册
RegisterTree::set("mysql", MysqlFactory::getIns());
RegisterTree::set("sqlite", SqliteFactory::getIns());

// 客户端
$mysql = RegisterTree::get("mysql");
$mysql->conn();

$sqlite = RegisterTree::get("sqlite");
$sqlite->conn();

六:门面模式

门面模式(Facade)又称外观模式,用于为子系统中的一组接口提供一致的界面。门面模式定义了一个高级接口,这个接口使得子系统更加容易使用:引入门面角色之后,用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦合度

在这里插入图片描述
作用:

  • 为一些复杂的子系统提供一组接口
  • 提高子系统的独立性
  • 在层次化结构中,可以使用门面模式定义系统的每一层接口

优点:

  • 他对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便
  • 实现了子系统与客户之间的松耦合关系
  • 如果应用需要,他不限制他们使用子系统类。因此可以在系统易用性与能用性之间加以选择
<?php
/*
 * 门面模式
 */

class Camera
{
    public function turnOn()
    {
        echo "打开摄像机";
    }
    public function turnOff()
    {
        echo "关闭摄像机";
    }
}

class Light
{
    public function turnOn()
    {
        echo "打开灯";
    }
    public function turnOff()
    {
        echo "关闭灯";
    }
}

// 门面类
class Facade
{
    private $_camera;
    private $_light;
    public function __construct()
    {
        $this->_camera = new Camera();
        $this->_light = new Light();
    }
    // 启动接口
    public function activate()
    {
        $this->_camera->turnOn();
        $this->_light->turnOn();
    }
    // 关闭接口
    public function deactivate()
    {
        $this->_camera->turnOff();
        $this->_light->turnOff();
    }
}

// 客户端
class Client
{
    private static $_securit;
    public static function main()
    {
        self::$_securit = new Facade();
        self::$_securit->activate();
        self::$_securit->deactivate();
    }
}

Client::main();

七:管道模式

概念:管道(Pipeline)设计模式即流水线模式就是会将数据传递到一个任务序列中,管道扮演着流水线的角色,数据在这里被处理然后传递到下一个步骤

释意:

  • 管道,顾名思义,就是一个长长的流水管道,只不过加了许多阀门,所以管道模式大致需要三个角色,管道,阀门和载荷。
  • 目的是在管道中对载荷进行一系列的处理。因为可以对过滤器进行动态的添加,所以对载荷的处理可以变得更加灵活。但同时带来的问题是,在过滤器过多时,很难把握整体的处理逻辑,而且在某一个过滤器对载荷处理后,因为载荷改变,会造成下一个过滤器中的逻辑错误

在这里插入图片描述

<?php
/*
 * 管道模式
 */

// 管道接口
interface PipelineBuilder
{
    public function __construct($payload); // $payload载荷
    public function pipe(StageBuilder $stage);  // 管道与阀门
    public function process();  // 过程
}

// 实现管道接口
class Pipeline implements PipelineBuilder
{
    protected $payload;
    protected $pipes = [];
    public function __construct($payload)
    {
        $this->payload = $payload;
    }

    public function pipe(StageBuilder $stage)
    {
        $this->pipes = $stage;
        return $this;
    }

    public function process()
    {
        foreach ($this->pipes as $pipe) {
            call_user_func([$pipe, "handle"], $this->payload);
        }
    }
}

// 阀门接口
interface StageBuilder
{
    public function handle($payload);
}

// 实现阀门
class StageOneBuilder implements StageBuilder
{
    public function handle($payload)
    {
        echo $payload . "真是个";
    }
}
class StageTwoBuilder implements StageBuilder
{
    public function handle($payload)
    {
        echo "有趣的东西";
    }
}

// 客户端使用
$pipeline = new Pipeline('joke');
$pipeline->pipe(new StageOneBuilder())->pipe(new StageTwoBuilder())->process();

八:代理模式

概念:代理模式(Proxy Pattern)构建了透明置于两个不同对象之内的一个对象,从而能够截取或代理这两个对象之间的通信或访问

应用场景:

  • 远程代理:就是为了一个对象在不同地址空间提供局部代表。隐藏一个对象存在于不同地址空间的事实
  • 虚拟代理:根据需要来创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象
  • 安全代理:用来控制真实对象的访问对象
  • 智能指引:当调用真实对象的时候,代理处理一些事情

在这里插入图片描述

注意代理模式与装饰器和适配器模式的区分:装饰器一般是对对象进行装饰,其中的方法行为会有所增加,以修饰对象为主,适配器一般会改变方法行为,目的是保持接口的统一但得到不同的实现。

代理模式一般有几种形式:

  • 远程代理,例如第三方sdk
  • 虚代理,例如异步加载图片
  • 保护代理&智能指引,例如权限保护
<?php
/*
 * 代理模式
 */

// 真实接口
interface Subject
{
    public function request();
}
// 真实类
class RealSubject implements Subject
{
    public function request()
    {
        echo "真实的操作";
    }
}


// 代理模式代理真实类
class Proxy implements Subject
{
    protected $realSubject;
    public function __construct()
    {
        $this->realSubject = new RealSubject();
    }
    public function request()
    {
        echo "代理操作";
        $this->realSubject->request();
    }
}

$proxy = new Proxy();
$proxy->request();

九:策略模式

概念:

  • 将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,并让他们可以相互替换,这种模式就是策略模式
  • 简单理解就是由n个做法供你选择,根据你的需要选择某个策略得到结果

优点:

  • 使用设计模式之后,我们的代码冗余和耦合度变低,每个策略模块完成对应的功能
  • 当然缺点就是一个功能我们就要相应制作一个策略类,但是我们统观MVC架构当中(ThinkPHP, Yii2)。一个控制器对应一个视图,其实也是策略设计模式的体现

在这里插入图片描述

<?php
/*
 * 策略模式
 */

// 用户接口
interface UserInterface
{
    /**
     * @purpose 展示广告
     * @return mixed
     */
    public function showAd();

    /**
     * @purpose 展示商品分类
     * @return mixed
     */
    public function showCategory();
}

// 男性用户实现接口
class MaleUser implements UserInterface
{

    public function showAd()
    {
        echo "电子产品";
    }

    public function showCategory()
    {
        echo "手机、电脑、手表";
    }
}

// 女性用户实现接口
class femaleUser implements UserInterface
{

    public function showAd()
    {
        echo "化妆品";
    }

    public function showCategory()
    {
        echo "衣服分类";
    }
}

// 判断用户是男性还是女性,根据用户性别推荐不同内容
if ($_GET('sex') == 'male') {
    $user = new MaleUser();
} else {
    $user = new FemaleUser();
}

$user->showAd();
$user->showCategory();

十:观察者模式

概念:

  • 观察者模式属于行为模式,是定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新
  • 当一个对象状态发生改变后,会影响到其他几个对象的改变,这时候可以用观察者模式
  • 观察者模式符合接口隔离原则,实现了对象之间的松散耦合

角色:

  • 抽象主题(Subject):他把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象
  • 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知
  • 抽象观察者(Observer): 为所有的具体观察者定义一个接口,在得到主题通知时更新自己
  • 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调
<?php
/*
 * 观察者模式
 */

/**
 * Interface Subject
 * 抽象主题
 */

interface Subject
{
    /**
     * @purpose 新增观察者
     * @param Observer $observer
     * @return mixed
     */
    public function attach(Observer $observer);

    /**
     * @purpose 移除观察者
     * @param Observer $observer
     * @return mixed
     */
    public function detach(Observer $observer);

    /**
     * @purpose 通知观察者
     * @return mixed
     */
    public function notifyObserver();
}

/**
 * @purpose 具体主题
 * Class ConcreteSubjects
 */
class ConcreteSubject implements Subject
{
    // 存储观察者
    private $observes;

    public function __construct()
    {
        $this->observes = [];
    }

    public function attach(Observer $observer)
    {
        return array_push($this->observes, $observer);
    }

    public function detach(Observer $observer)
    {
        $index = array_search($observer, $this->observes);
        if ($index === false || array_key_exists($index, $this->observes)) return false;
        unset($this->observes[$index]);
        return true;
    }

    public function notifyObserver()
    {
        if(!is_array($this->observes)) return false;
        foreach ($this->observes as $observe) {
            // 通知改变
            $observe->update();
        }
        return true;
    }
}

/**
 * @purpose 抽象观察者
 * Interface Observer
 */
interface Observer
{
    /**
     * @purpose 观察者状态改变
     * @return mixed
     */
    public function update();
}

/**
 * @purpose 具体观察者的
 * Class ConcreteObserver
 */
class ConcreteObserver implements Observer
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function update()
    {
        echo "观察者" . $this->name . "已经收到通知<br>";
    }
}

// 注册观察者,监听
$subject = new ConcreteSubject();

// 增加观察者
$subject->attach(new ConcreteObserver("joke"));
$subject->attach(new ConcreteObserver("lisa"));
$subject->attach(new ConcreteObserver("lucy"));

$subject->notifyObserver();

十一:命令模式

概念:

  • 将一个请求封装(命令的封装)为一个对象,从而使用你可用不同的请求对客户进行参数化。对请求排队或记录请求日志,以及支持可撤销的操作
  • 命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分隔开,委派给不同的对象
  • 请求的一方发出请求要求执行一个操作,接收的一方收到请求,并执行操作,命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收的,以及操作是否被执行、何时被执行,以及是怎么被执行的

在这里插入图片描述

角色:

  • 命令(Command)角色:声明一个给所有具体命令类的抽象接口,这是一个抽象角色
  • 具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合,实现Execute()方法,负责调用接收的相应操作,Execute()方法通常叫做执行方法
  • 客户(Client)角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者
  • 请求者(Invoker)角色:负责调用命令对象执行请求,相关方法叫做行动方法
  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法

优点

  • 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分离开
  • 命令类与其他任何别的类一样,可以修改和推广
  • 可以把命令对象聚合在一起,合成为合成命令
  • 可以很容易的加入新的命令类

缺点:可能会导致某些系统有过多的具体命令

应用场景:

  • 抽象出待执行的动作以参数化对象。Command模式是回调机制的一个面向对象的替代品
  • 在不同的时刻指定、排列和执行请求
  • 支持取消操作
  • 支持修改日志
  • 用构建在原语操作上的高层操作构建一个系统。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务,同时使用该模式也易于添加新事务以扩展系统
<?php
/*
 * 命令模式
 *
 * 实例:复制粘贴功能
 */

/**
 * @purpose 抽象命令接口
 * Interface Command
 */
interface Command
{
    /**
     * @purpose 命令执行
     * @return mixed
     */
    public function execute();
}

/**
 * @purpose 宏命令接口
 * Interface MacroCommand
 */
interface MacroCommand extends Command
{
    /**
     * @purpose 宏命令聚集管理方法,可以删除一个命令
     * @param Command $command
     * @return mixed
     */
    public function remove(Command $command);

    /**
     * @purpose 宏命令聚集管理方法,可以添加一个命令
     * @param Command $command
     * @return mixed
     */
    public function add(Command $command);
}

// 复制命令
class CopyCommand implements Command
{
    private $receiver;
    public function __construct(Receiver $receiver)
    {
        $this->receiver = $receiver;
    }
    public function execute()
    {
        $this->receiver->copy();
    }
}

// 粘贴命令
class PasteCommand implements Command
{
    private $receiver;
    public function __construct(Receiver $receiver)
    {
        $this->receiver = $receiver;
    }
    public function execute()
    {
        $this->receiver->paste();
    }
}

// 命令接收者执行命令
class Receiver
{
    private $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function copy()
    {
        echo "{$this->name}执行copy方法<br>";
    }
    public function paste()
    {
        echo "{$this->name}执行paste方法<br>";
    }
}

// 命令请求者,用于发送命令
class Invoker
{
    private $command;
    public function __construct(Command $command)
    {
        $this->command = $command;
    }
    public function action()
    {
        $this->command->execute();
    }
}

// 实现宏命令
class DemoMacroCommand implements MacroCommand
{
    private $commands;
    public function __construct()
    {
        $this->commands = [];
    }

    public function add(Command $command)
    {
        return array_push($this->commands, $command);
    }

    public function remove(Command $command)
    {
        $index = array_search($command, $this->commands);
        if ($index === false || array_key_exists($index, $this->commands)) return false;
        unset($this->commands[$index]);
        return true;
    }

    public function execute()
    {
        if(!is_array($this->commands)) return false;
        foreach ($this->commands as $command) {
            // 通知改变
            $command->execute();
        }
        return true;
    }

}

// 客户端
$receiver = new Receiver("joke");
$copyCommand = new CopyCommand($receiver);
$pasteCommand = new PasteCommand($receiver);

// 添加命令到宏命令中
$macroCommand = new DemoMacroCommand();
$macroCommand->add($copyCommand);
$macroCommand->add($pasteCommand);

$invoker = new Invoker($macroCommand);
$invoker->action();

十二:迭代器模式

概念:迭代器模式是遍历集合的成熟模式,迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,他在工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构

说明

  • 迭代器模式(Iterator),又叫做游标(Cursor)模式,提供一种方法访问一个容器(Container)对象中各个元素,而又不需要暴露该对象的内部细节
  • 当你需要访问一个聚合对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑使用迭代器模式。另外,当需要对聚集有多种方式遍历时,可以考虑去使用迭代器模式。迭代器模式为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口

作用:

  • 我们想要向遍历数组那样,遍历对象,或是遍历一个容器
  • 迭代器模式可以隐藏遍历元素所需的操作

应用场景:

  • 访问一个聚合对象的内容而无需暴露他的内部表示
  • 支持对聚合对象的多种遍历
  • 为遍历不同的聚合结构提供一个统一的接口

角色:

  • Iterator(迭代器):迭代器定义访问和遍历元素的接口
  • ConcreteIterator(具体迭代器): 具体迭代器实现迭代器接口,对该聚合遍历时跟踪当前位置
  • Aggregate(聚合):聚合定义创建相应迭代器对象的接口
  • ConcreteIterator(具体聚合):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例
<?php
/*
 * 迭代器模式
 */

/**
 * 自定义迭代器实现系统定义迭代器接口
 * Class MyIterator
 */
class MyIterator implements Iterator
{
    protected $data = [];
    protected $index;
    public function __construct($data)
    {
        $this->data = $data;
        $this->index = 0;
    }

    /**
     * 返回当前元素
     * @return mixed|void
     */
    public function current()
    {
        return $this->data[$this->index];
    }

    /**
     * 返回下一个
     */
    public function next()
    {
        $this->index++;
    }

    /**
     * 返回key值
     * @return bool|float|int|string|void|null
     */
    public function key()
    {
        return $this->index;
    }

    /**
     * 验证是否继续
     * @return bool|void
     */
    public function valid()
    {
        return $this->index < count($this->data);
    }

    /**
     * 返回第一个元素
     */
    public function rewind()
    {
        $this->index = 0;
    }
}

$arr = ['张三', '李四', '王二', '麻子'];
$iterate = new MyIterator($arr);
foreach($iterate as $dynasty)
{
    echo "the ogj == {$dynasty}<br>";
}

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔的个人空间

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值