- 前言
产品的需求千变万化,有时候需要在原有代码逻辑上增加需求,或者 删除,或者修改。
- 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自动引入这个服务提供者。