- 前言
產品的需求千變萬化,有時候需要在原有代碼邏輯上增加需求,或者 刪除,或者修改。
- addMeiqucickOrder 第一次 簡單的新增一個訂單
- addmeiquickorderBeforeValidate 第二次變化, 在新增訂單前驗證一堆判斷,至少20個判斷
- addMeiquickOrderWithNoRepeat 第三次變化,在驗證前,增加一個防止併發的重新新增問題
- 貢獻
如果有更好的辦法解耦,請在評論區貼上博客鏈接哦,互相討論O(∩_∩)O哈哈~!
- 產品需求變化了三次
需求1:商家 --> 訂單修改 --> 保存運單號 --> 修改訂單狀態爲發貨
需求2: 商家 --> 訂單修改 --> 判斷 訂單異常信息(未退款,未取消 等狀態)--> 保存運單號 --> 修改訂單狀態爲發貨
需求3: 商家 --> 訂單修改 --> 判斷 訂單異常信息(未退款,未取消 等狀態)--> 保存運單號 --> 修改訂單狀態爲發貨 --> 記錄訂單狀態修改的日誌
前景分析
從上面的需求可以看出,前後需求三次變化,
需求 1 與 需求 2 對比:在保存運單號前,先判斷訂單異常信息 才能修改
需求2 與 需求3 對比:在修改訂單爲發貨狀態時,增加記錄訂單狀態修改日誌
普遍程序員做法
把 整個流程 從頭到尾 都是 這樣寫
function updateOrderExpressNo(OrderModel $order, $expressNo) {
//先判斷
if else if else if else ....
//修改運單號
$order->express_no = $expressNo;
//增加日誌
Log::info('訂單修改了發貨狀態:'.$order->id);
//修改訂單狀態
$order->status = '1'; //1爲發貨狀態
$order-save();
}
普遍程序員做法分析:
此時 增加了我們調試難度,把所有邏輯都放到一個函數裏面寫,也不確保每個功能是否完整是否能運行正常,隨着以後項目越做越大,這時只能重構。
我的做法,解耦過程如下
我們先分析以下 需求1共有2個步驟
1.保存運單號
2.訂單發貨狀態修改
//以下代碼 基於laravel5框架實現
//定義兩個行爲接口,分別是上面兩個
interface UpdateOrderExpressNoInterface {
//修改訂單運單號接口
public function handle($order, $expressNo);
}
interface UpdateOrderStatusInterface {
//修改訂單狀態接口
public function handle($order, $status);
}
//兩個行爲類
//修改運單號行爲
class UpdateOrderExpressNo implements UpdateOrderExpressNoInterface {
public function handle($order, $expressNo){
$order->express_no = $expressNo;
return $order->save();
}
}
//修改訂單狀態行爲
class UpdateOrderStatus implements UpdateOrderStatusInterface {
public function handle($order, $status){
$order->status = $status;
return $order->save();
}
}
//新建一個AppBehaviorProvider
class AppBehaviorProvider extends ServiceProvider
{
/**行爲類接口
* @var array
*/
protected $behaviorArr = [
//運單號修改, 啥意思呢?當我們要調用app(UpdateOrderExpressNoInterface::class) 就會得到 new UpdateOrderExpressNo 的實例對象
UpdateOrderExpressNoInterface::class => UpdateOrderExpressNo::class,
//修改訂單狀態
UpdateOrderStatusInterface::class => UpdateOrderStatus::class,
];
public function boot()
{
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
foreach ($this->behaviorArr as $interface => $class)
{
$this->app->bind($interface, $class);
}
}
}
//訂單操作控制器
class OrderController {
protected $orderService;
public function __construct(OrderService $orderService){
$this->orderService = $orderService;
}
public function updateExpressNo(int $orderId, Request $request){
$status = $this->orderService->updateExpressNo($orderId, $request->input('express_no', ''));
return $status;
}
}
//訂單服務
class OrderService {
protected $orderModel;
protected $updateOrderExpressNo;
protected $updateOrderStatus;
public function __construct(
OrderModel $orderModel,
UpdateOrderExpressNoInterface $updateOrderExpressNo,
UpdateOrderStatusInterface $updateOrderStatus
){
$this->orderModel = $orderModel;
$this->updateOrderExpressNo = $updateOrderExpressNo;
$this->updateOrderStatus = $updateOrderStatus;
}
public function find($id){
return $this->orderModel->find($id);
}
public function updateExpressNo($orderId, $expressNo)
{
//開啓事務,此處省略
$order = $this->find($orderId);
//執行修改運單號動作
//$result = app(UpdateOrderExpressNoInterface::class)->handle($order, $expressNo);
$result = $this->updateOrderExpressNo->handle($order, $expressNo);
if ($result) {
//執行修改訂單狀態動作
//$result1 = app(UpdateOrderStatus::class)->handle($order, 1); //1爲發貨狀態
$result1 = $this->updateOrderStatus->handle($order, 1); //1爲發貨狀態
}
//失敗保存則回滾,此處省略
}
接下來我們先分析以下 需求2共有三個步驟
1. 判斷訂單狀態
2. 保存運單號
3. 訂單發貨狀態修改
//2,3步驟不變,在原有的保存運單號接口上,增加步驟1, 增加一個判斷訂單狀態的行爲類
class UpdateOrderBeforeValidate implements UpdateOrderExpressNoInterface{
protected $updateOrder;
//這裏laravel自動幫我們new UpdateOrderExpressNo()並傳入到 構造函數了
public function __construct(UpdateOrderExpressNo $updateOrder) {
$this->updateOrder = $updateOrder;
}
public function handle($order, $expressNo){
if ($order->refund != 0) {
return '訂單退款狀態,無法保存訂單運單號';
}else if ($order->cancle != 0){
return '訂單取消狀態,無法保存運單號';
}
return $this->updateOrder->handle($order, $expressNo);
}
}
//增加保存運單號前需要驗證的 行爲類時,需要修改AppBehaviorProvider定義的修改運單號接口對應的類,也就是告訴laravel,當控制器需要 修改運單號時,切換 UpdateOrderBeforeValidate類。
class AppBehaviorProvider extends ServiceProvider
{
/**行爲類接口
* @var array
*/
protected $behaviorArr = [
//從UpdateOrderExpressNo類 切換 UpdateOrderBeforeValidate類
UpdateOrderExpressNoInterface::class => UpdateOrderBeforeValidate::class,
//修改訂單狀態
UpdateOrderStatusInterface::class => UpdateOrderStatus::class,
];
public function boot()
{
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
foreach ($this->behaviorArr as $interface => $class)
{
$this->app->bind($interface, $class);
}
}
}
需求1 -> 需求2分析
需求2,增加了UpdateOrderBeforeValidate類,無需修改 UpdateOrder這個類,也就是對這個類修改的關閉,其他類也無需要改,並且 在該類 通過構造函數實例UpdateOrder類,並且增強 他的handle方法,裝飾者模式。
至於需求三,分析共以下步驟:
1. 訂單狀態判斷
2. 修改訂單運單號
3. 修改訂單狀態爲發貨狀態
4. 記錄日誌
//代碼就不一一列舉了,解題思路:
//增加一個 UpdateOrderStatusAfterLog類,該類用來記錄日誌,通過裝飾者模式,在構造函數傳入pdateOrderStatus類實例.
public handle($order, $status) {
Log::info('訂單狀態修改爲:'.$order->id.':'.$status);
return $this->updateOrderStatus->handle($order, $status);
}
//接着在 AppBehaviorProvider 修改UpdateOrder::class 爲UpdateOrderStatusAfterLog::class
備註:
__construct(OrderService $orderService) 。laravel自動給你 new OrderService() 傳入到構造函數裏面
__construct(UpdateOrderExpressNoInterface $updateOrderExpressNo)
因爲我們在provider裏面註冊了
$this->app->bind(UpdateOrderExpressNoInterface::class, UpdateOrderExpressNo::class);
告訴我們的容器,當需要UpdateOrderExpressNoInterface的接口實現類時,我們就new UpdateOrderExpressNo()傳到構造函數裏面。這是laravel的容器+依賴注入的特性。
當然在provider註冊了,需要在 config/app.php 的providers鍵下增加 AppBehaviorProvider::class, 告訴laravel自動引入這個服務提供者。