摘要:我們可以把取消發貨單和取消訂單看成一個被觀察或被訂閱的類實例的對象,一旦發生取消行為,我們立即通知各個觀察者做出相對應的行為。裝飾器模式裝飾器思想,不管以前業務邏輯,甚至不去讀,調用之前的接口裝飾上新的數據,達到自己的目的。
前言
還是每月的目標至少寫一篇文章,一晃八月份就要過去了,這個月依然沒有什么產出,毫無疑問最近的狀態就是不停的工作,不停的加班。所以還是把最近工作進行一個總結,首先來我看看這段時間我做了什么?
工作內容這次工作的主要內容就是“取消發貨單”功能,這個功能的上下文是這樣的:我們支付成功的訂單會在一段時間后被拆成發貨單,本次開發任務的目的就是通過客戶端對用戶開放申請取消發貨單的功能。其實這個功能就是發貨單服務生成退款單之后回調訂單服務的一系列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全??蚣?/pre>掃面下方二維碼關注我的技術公眾號,及時為大家推送我的原創技術分享
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/25693.html
摘要:曾今誰都有過迷茫期,下面是我開始開發中,不斷改變的代碼組織方式。 曾今 誰都有過迷茫期,下面是我開始PHP開發中,不斷改變的代碼組織方式。 初期:所有代碼一股腦控制器controller 曾今只是簡單的理解MVC 中期:業務代碼抽象一部分到模型層model 開始覺得model層是否該做點什么了 后期:業務代碼控制器,模型層只寫db的curd方法 復雜的業務代碼使contro...
摘要:這個月的計劃本來是對基礎的數據結構做一個沉淀,但是,但是,但是這個月的的狀態就是工作工作既然這樣就總結下這個月的工作吧。 前言 目標是每個月寫一篇文章,對從事編程開發的基礎知識做一個學習總結。這個月的計劃本來是對基礎的數據結構做一個沉淀,但是,但是,但是......這個月的的狀態就是工作工作...既然這樣就總結下這個月的工作吧。 工作內容 促銷活動的抽獎工具,具備如下功能: 根據不同...
摘要:我們今天也來做一個萬能遙控器設計模式適配器模式將一個類的接口轉換成客戶希望的另外一個接口。今天要介紹的仍然是創建型設計模式的一種建造者模式。設計模式的理論知識固然重要,但 計算機程序的思維邏輯 (54) - 剖析 Collections - 設計模式 上節我們提到,類 Collections 中大概有兩類功能,第一類是對容器接口對象進行操作,第二類是返回一個容器接口對象,上節我們介紹了...
閱讀 1026·2021-11-22 13:53
閱讀 1578·2021-11-17 09:33
閱讀 2373·2021-10-14 09:43
閱讀 2836·2021-09-01 11:41
閱讀 2263·2021-09-01 10:44
閱讀 2905·2021-08-31 09:39
閱讀 1443·2019-08-30 15:44
閱讀 1853·2019-08-30 13:02