PHPer月工做总结之观察者&装饰器模式

前言

仍是每个月的目标至少写一篇文章,一晃八月份就要过去了,这个月依然没有什么产出,毫无疑问最近的状态就是不停的工做,不停的加班。因此仍是把最近工做进行一个总结,首先来我看看这段时间我作了什么?php

工做内容

此次工做的主要内容就是“取消发货单”功能,这个功能的上下文是这样的:咱们支付成功的订单会在一段时间后被拆成发货单,本次开发任务的目的就是经过客户端对用户开放申请取消发货单的功能。其实这个功能就是发货单服务生成退款单以后回调订单服务的一系列undo操做,其次这些逻辑咱们目前都是同步调用未异步队列化,接着咱们来梳理下大体有哪些undo操做:闭包

undo订单&订单商品信息->undo商品库存->undo各类促销优惠活动的库存->undo钱包余额->log->消息

显而易见这些操做基本和取消订单的逻辑绝大多数一致,加之取消订单的代码已经很老了,并且可维护性,扩展性,可用性都不好,因此我又多了一项任务“重构取消订单”。咱们接着来梳理下取消订单的逻辑:框架

undo订单&订单商品信息->undo商品库存->undo各类促销优惠活动的库存->生成退款单->undo钱包余额->undo赠品->undo红包->log->消息

下图清晰的梳理了二者操做的内容:
图片描述异步

建模

经过上面咱们对业务逻辑的梳理,其实这两个功能绝大多数的逻辑是能够公用的,且这每一个子逻辑均可以独立成为一个个体,这么看来这就是典型的订阅通知模型“观察者模式”应用的场景。咱们能够把“取消发货单”和“取消订单”当作一个被观察或被订阅的类实例的对象,一旦发生取消行为,咱们当即通知各个观察者作出相对应的行为。原本php是提供了观察者的接口SplSuject和SplObserver,咱们只需实现该接口便可,可是SplSuject的attach成员方法不支持闭包(使用闭包可使观察者被通知的时候再实例化,节省了必定的性能和内存空间),因此我本身最后从新实现了该接口。最后咱们的模型以下:函数

图片描述

填充业务逻辑

完成上面的建模,其实咱们的功能其实就算完成一半了,剩下的事情就是在每一个类文件填充对应独立的业务逻辑便可。性能

/**
 * 被观察者接口
 *
 * 因为php原生的被观察者接口SplSubject不支持注册闭包,即本身实现一下这个接口
 */
Interface ObservableInterface
{
    /**
     * 注册观察者对象
     *
     * @param  Closure $closure 闭包形式注册
     * @return void
     */
    public function attach(Closure $closure);

    /**
     * 剔除观察者对象
     *
     * @param  ObserverInterface $observer 观察者对象
     * @return void
     */
    public function detach(ObservableInterface $observer);

    /**
     * 通知观察者对象
     *
     * @return void
     */
    public function notify();
}

/**
 * 观察者接口
 *
 * php原生观察者接口SplObserver
 */
Interface ObserverInterface
{
    /**
     * 观察者操做
     *
     * @param  ObservableInterface $observable 被观察者对象
     * @return void
     */
    public function operate(ObservableInterface $observable);
}
/**
 * 取消订单被订阅实体
 *
 * 被订阅/被观察者实体
 */
class Observable implements ObservableInterface
{
    /**
     * 注册的观察者/订阅对象
     *
     * @var array
     */
    private $observers = [];

    /**
     * 已经被通知的观察者/订阅对象
     *
     * @var array
     */
    private $hadNotify = [];


    /**
     * 构造函数
     *
     * @return void
     */
    public function __construct(params...)
    {
        
    }

    /**
     * 注册观察者/订阅对象
     *
     * @param  Closure $closure 闭包形式注册
     * @return void
     */
    public function attach(Closure $closure)
    {
        $this->observers[] = $closure;
    }

    /**
     * 批量注册观察者/订阅对象
     *
     * @param  array $closures 闭包形式注册
     * @return void
     */
    public function multiAttach($closures = [])
    {
        $closures = array_filter($closures, function ($var) {
            if ($var instanceof Closure) {
                return $var;
            }
        });
        $this->observers = array_merge($this->observers, $closures);
    }

    /**
     * 剔除观察者/订阅对象
     *
     * @param  ObserverInterface $observer 观察者对象/订阅对象
     * @return void
     */
    public function detach(ObservableInterface $observer)
    {
        foreach ($this->observers as $k => $v) {
            if ($v() === $observer) {
                unset($this->observers[$k]);
            }
        }
    }

    /**
     * 通知观察者/订阅对象
     *
     * @return void
     */
    public function notify()
    {
        foreach ($this->observers as $v) {
            $instance = $v();
            if (in_array($instance, $this->hadNotify, true)) {
                // 不通知重复的订阅
                continue;
            }
            $instance->operate($this);
            $this->hadNotify[] = $instance;
        }
    }
}

最后咱们在咱们的控制器类中完成调用以下:this

class OrderController
{
    /**
     * 取消订单
     */
    public function cancel()
    {
        try {
            /* 建立取消订单的被观察者 */
            $subject = new Observable();

            // 注册订单观察者
            $subject->attach(function () {
                return new Order();
            });

            // 注册商品观察者
            $subject->attach(function () {
                return new Goods();
            });

            // 注册促销商品观察者
            $subject->attach(function () {
                return new PromotionGoods();
            });

            // 注册退款单观察者
            $subject->attach(function () {
                return new RefundOrder();
            });

            // 注册钱包观察者
            $subject->attach(function () {
                return new Wallet();
            });

            // 注册红包观察者
            $subject->attach(function () {
                return new Bonus();
            });

            // 注册赠品观察者
            $subject->attach(function () {
                return new Gift();
            });

            // 注册日志观察者
            $subject->attach(function () {
                return new Log();
            });

            // 注册消息观察者
            $subject->attach(function () {
                return new Notice();
            });

            /* 广播 */
            $subject->notify();
        } catch (Exception $e) {
            # code...
        }
    }
}

class DeliveryController
{
    /**
     * 取消发货单
     */
    public function cancel()
    {
        try {
            /* 建立取消发货单的被观察者 */
            $subject = new Observable();

            // 注册订单观察者
            $subject->attach(function () {
                return new Order();
            });

            // 注册商品观察者
            $subject->attach(function () {
                return new Goods();
            });

            等等(不注册红包和赠品观察者)...

            /* 广播 */
            $subject->notify();
        } catch (Exception $e) {
            # code...
        }
    }
}

这样的话咱们彻底高内聚松耦合了咱们的业务代码,若是将来须要增长新的逻辑,咱们只须要注册新的观察者便可。这样重构完成代码后,咱们将来在取消订单的时候只须要注册订单的观察者到取消订单的被观察者便可,其余的观察者咱们再注册到一个异步执行的取消订单的被观察者实例中,经过这样咱们就能给用户带来好的体验,用户取消订单的操做咱们只需通知订单状态变动,其他的观察者咱们异步通知保证最终成功,在将来实现这个功能时咱们的业务代码根本不须要动,只须要改变调用方式。spa

装饰器模式

装饰器思想,无论之前业务逻辑,甚至不去读,调用以前的接口装饰上新的数据,达到本身的目的。最近遇到的问题,在咱们的订单列表加一些字段,可是订单列表的代码基本没法阅读和调整,最后想到了装饰器的思想,最后咱们彻底不须要管以前的逻辑,咱们只需调用现有的类方法,再装饰上咱们想要的数据便可,这样就最简化和快捷的达到了咱们的目的。日志

Easy PHP:一个极速轻量级的PHP全栈框架

扫面下方二维码关注个人技术公众号,及时为你们推送个人原创技术分享code

图片描述

相关文章
相关标签/搜索