资讯专栏INFORMATION COLUMN

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

MadPecker / 930人阅读

摘要:我们可以把取消发货单和取消订单看成一个被观察或被订阅的类实例的对象,一旦发生取消行为,我们立即通知各个观察者做出相对应的行为。装饰器模式装饰器思想,不管以前业务逻辑,甚至不去读,调用之前的接口装饰上新的数据,达到自己的目的。

前言

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

工作内容

这次工作的主要内容就是“取消发货单”功能,这个功能的上下文是这样的:我们支付成功的订单会在一段时间后被拆成发货单,本次开发任务的目的就是通过客户端对用户开放申请取消发货单的功能。其实这个功能就是发货单服务生成退款单之后回调订单服务的一系列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;
        }
    }
}

最后我们在我们的控制器类中完成调用如下:

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...
        }
    }
}

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

装饰器模式

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

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

扫面下方二维码关注我的技术公众号,及时为大家推送我的原创技术分享

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25693.html

相关文章

  • 这两年多我写PHP业务代码的方式是如何进化的

    摘要:曾今谁都有过迷茫期,下面是我开始开发中,不断改变的代码组织方式。 曾今 谁都有过迷茫期,下面是我开始PHP开发中,不断改变的代码组织方式。 初期:所有代码一股脑控制器controller 曾今只是简单的理解MVC 中期:业务代码抽象一部分到模型层model 开始觉得model层是否该做点什么了 后期:业务代码控制器,模型层只写db的curd方法 复杂的业务代码使contro...

    qqlcbb 评论0 收藏0
  • PHPer工作总结构建抽奖工具

    摘要:这个月的计划本来是对基础的数据结构做一个沉淀,但是,但是,但是这个月的的状态就是工作工作既然这样就总结下这个月的工作吧。 前言 目标是每个月写一篇文章,对从事编程开发的基础知识做一个学习总结。这个月的计划本来是对基础的数据结构做一个沉淀,但是,但是,但是......这个月的的状态就是工作工作...既然这样就总结下这个月的工作吧。 工作内容 促销活动的抽奖工具,具备如下功能: 根据不同...

    W_BinaryTree 评论0 收藏0
  • php设计模式

    摘要:我们今天也来做一个万能遥控器设计模式适配器模式将一个类的接口转换成客户希望的另外一个接口。今天要介绍的仍然是创建型设计模式的一种建造者模式。设计模式的理论知识固然重要,但 计算机程序的思维逻辑 (54) - 剖析 Collections - 设计模式 上节我们提到,类 Collections 中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了...

    Dionysus_go 评论0 收藏0
  • php设计模式

    摘要:我们今天也来做一个万能遥控器设计模式适配器模式将一个类的接口转换成客户希望的另外一个接口。今天要介绍的仍然是创建型设计模式的一种建造者模式。设计模式的理论知识固然重要,但 计算机程序的思维逻辑 (54) - 剖析 Collections - 设计模式 上节我们提到,类 Collections 中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了...

    vspiders 评论0 收藏0
  • 设计模式总结

    摘要:,命令模式,将行为请求者和行为实现者解耦,将行为抽象为对象。解释器模式,迭代器模式,将集合对象的存储数据和遍历数据职责分离。即将遍历的责任交给迭代器返回的迭代器,迭代器。 设计模式总结 创建型:除了直接new来实例化对象外,提供了多种隐藏创建逻辑的生成对象的方法 结构型:通过对象和类的组合,得到新的结构和功能 行为型:解决对象之间的通行和功能职责分配 详细分类 工厂 简单工厂...

    quietin 评论0 收藏0

发表评论

0条评论

MadPecker

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<