当前位置: 首页 > 后端技术 > PHP

PHPer月度工作总结之Observer&Decorator模式

时间:2023-03-29 21:55:43 PHP

前言至少写一篇文章依旧是每月的目标。八月即将过去,这个月依然没有产出。毫无疑问,最近的状态并不好。停止工作,继续加班。那么我们来对最近的工作做一个总结,首先让我看看这段时间我都做了些什么?工作内容本次工作的主要内容是“取消发票”功能。客户端开放给用户申请取消发票的功能。这个函数其实就是发票服务产生退款订单后回调订单服务的一系列撤销操作。其次,这些逻辑目前都是同步调用的,并没有异步排队。接下来我们来梳理一下大概的undo操作:undoorder&Order商品信息->undo商品库存->undo各种促销活动库存->undo钱包余额->log->message很明显这些操作和取消订单的逻辑,取消订单的代码已经很老了,可维护性、扩展性、可用性都很差,所以我又增加了一个任务“重构取消订单”。我们梳理一下取消订单的逻辑:撤销订单&订单商品信息->撤销商品库存->撤销各种优惠库存->生成退款单->撤销钱包余额->撤销礼物->撤销红包->日志->Message下图清晰的梳理了两个操作的内容:建模通过我们上面对业务逻辑的梳理,其实这两个功能的大部分逻辑是可以共享的,每个子逻辑都可以成为一个个体独立,看来这就是订阅通知模型“观察者模式”的典型应用场景。我们可以将“取消发票”和“取消订单”视为观察类或订阅类实例的对象。一旦发生取消,我们立即通知每个观察者采取相应的行动。原来PHP提供了观察者接口SplSuject和SplObserver,我们只需要实现这个接口即可,但是SplSuject的attach成员方法不支持闭包(使用闭包可以让观察者在收到通知时实例化,节省一定的性能和内存空间),所以我最终自己重新实现了界面。最后我们的模型如下:填写业务逻辑完成上面的建模,其实我们的功能就完成了一半,剩下的就是在各个类文件中填写对应的独立业务逻辑。/***Observable接口**由于php原生的观察者接口SplSubject不支持注册闭包,即自己实现这个接口*/InterfaceObservableInterface{/***注册观察者对象**@paramClosure$closureClosureformregistration*@returnvoid*/publicfunctionattach(Closure$closure);/***移除观察者对象**@paramObserverInterface$observer观察者对象*@returnvoid*/publicfunctiondetach(ObservableInterface$observer);/***通知观察者对象**@returnvoid*/publicfunctionnotify();}/***观察者接口**PHP原生观察者接口SplObserver*/InterfaceObserverInterface{/***观察者操作**@paramObservableInterface$observable可观察对象*@returnvoid*/publicfunctionoperate(ObservableInterface$observable);}/***取消订单订阅实体**订阅/被观察实体*/classObservableimplementsObservableInterface{/***注册观察者/订阅者object**@vararray*/private$observers=[];/***通知的观察者/订阅者对象**@vararray*/private$hadNotify=[];/***构造函数**@returnvoid*/publicfunction__construct(params...){}/***注册观察者/订阅对象**@paramClosure$closure闭包注册*@returnvoid*/publicfunctionattach(闭包$closure){$this->observers[]=$closure;}/***观察者/订阅对象批量注册**@paramarray$closures闭包形式注册*@returnvoid*/publicfunctionmultiAttach($closures=[]){$closures=array_filter($closures,function($var){if($varinstanceofClosure){return$var;}});$this->observers=array_merge($this->observers,$closures);}/***移除观察者/订阅者对象**@paramObserverInterface$observer观察者对象/订阅者对象*@returnvoid*/publicfunctiondetach(ObservableInterface$observer){foreach($this->observersas$k=>$v){if($v()===$observer){取消设置($这个->观察者[$k]);}}}/***通知观察者/订阅对象**@returnvoid*/publicfunctionnotify(){foreach($this->observersas$v){$instance=$v();if(in_array($instance,$this->hadNotify,true)){//不通知重复订阅continue;}$instance->operate($this);$this->hadNotify[]=$实例;}}}最后,我们在我们的控制器类中完成调用,如下所示:可观察的();//注册订单观察者$subject->attach(function(){returnnewOrder();});//注册项目观察者$subject->attach(function(){returnnewGoods();});//注册促销商品观察者$subject->attach(function(){returnnewPr运动商品();});//注册退款订单观察者$subject->attach(function(){returnnewRefundOrder();});//注册钱包观察者$subject->attach(function(){returnnewWallet();});//注册红包观察者$subject->attach(function(){returnnewBonus();});//注册赠品观察者$subject->attach(function(){returnnewGift();});//注册日志观察者$subject->attach(function(){returnnewLog();});//注册消息观察者$subject->attach(function(){returnnewNotice();});/*广播 */$subject->notify();}catch(Exception$e){#code...}}}classDeliveryController{/***取消通知清单*/publicfunctioncancel(){try{/*创建一个取消发票的可观察对象*/$subject=newObservable();//注册一个订单观察者$subject->attach(function(){returnnewOrder();});//注册一个产品观察者$subject->attach(function(){returnnewGoods();});etc.(不要注册红包和赠品观察者).../*broadcast */$subject->notify();}catch(Exception$e){#code...}}}这样我们的业务代码就完全松耦合了,高内聚。如果以后需要增加新的逻辑,只需要注册一个新的观察者,这样重构代码后,只需要在取消订单时,将订单的观察者注册到取消订单的观察者即可未来,我们会将其他观察者注册到一个异步执行的被取消订单的观察实例中,这样可以给用户带来很好的体验。当用户取消订单时,我们只需要通知订单状态变化,其余的观察者会异步通知,保证最终成功。以后实现这个功能的时候,我们的业务代码根本不用动,只需要改变调用方式即可。装饰器模式decorator的思想,不管之前的业务逻辑,连读取都不看,调用之前的接口装饰新的数据,达到自己的目的。我最近遇到的问题是在我们的订单列表中添加一些字段,但是订单列表的代码基本无法阅读和调整。最后想到了装饰器的想法。最后,我们完全不需要关心前面的逻辑。我们只需要调用已有的类方法,然后对我们想要的数据进行装饰,就可以用最简单、最快捷的方式达到我们的目的。EasyPHP:极速轻量级PHP全栈框架扫描下方二维码关注我的技术公众号,及时为大家分享我的原创技术