如何使用状态设计模式处理订单状态变化

How to handle Order status changes with State Design Pattern

目前,我正在做订单微服务,其中我有两个与订单状态变化相关的方法store(Order order)updateStatus(int orderId, String status),我稍后会解释。

订单有四种状态:

正在等待 -> 已过期

正在等待 -> 已取消

正在等待 -> 已购买

已购买 -> 已取消

我在下面提供了状态流程图,以使其清楚(希望如此)

订单创建后状态为“Waiting”,如果用户已付款则状态为“Purchased”,如果买家或产品所有者取消订单则状态为“Canceled”,如果超过时间然后状态变为“已过期”。

对于我想要处理的每个微服务,如果可能的话,我将实施四人组设计模式,对于订单状态,我决定实施状态设计模式,因为它是相关的,而且我在许多博客,例如文档状态信息(草稿、审阅中等)、音频播放器信息(暂停、播放等)等等。

这是我所做的:

基础状态

public interface OrderStatus {
    void updateStatus(OrderContext orderContext);
}

等待状态

public class WaitingState implements OrderStatus {
    // omited for brevity    
    
    @Override
    public void updateStatus(OrderContext orderContext) {
        orderContext.getOrder().setStatus("Waiting");
    }
}

购买状态

public class PurchasedState implements OrderStatus {
    // omited for brevity

    @Override
    public void updateStatus(OrderContext orderContext) {
        orderContext.getOrder().setStatus("Purchased");
    }
}

其他州

..

上下文:

public class OrderContext {
    private OrderStatus currentStatus;
    private Order order;

    public OrderContext(OrderStatus currentStatus, Order order) {
        this.currentStatus = currentStatus;
        this.order = order;
    }

    public void updateState() {
        currentStatus.updateStatus(this);
    }

    public OrderStatus getCurrentStatus() {
        return currentStatus;
    }

    public void setCurrentStatus(OrderStatus currentStatus) {
        this.currentStatus = currentStatus;
    }

    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }
}

客户端是我从 OrderController 调用的 OrderServiceImpl。

public class OrderServiceImpl implements OrderService {
    // omited for brevity
    
    @Override
    public Order store(Order order) {
        WaitingState state = WaitingState.getInstance();
        OrderContext context = new OrderContext(state, order);
        context.updateState();
    
        // do other stuff
    }

    @Override    
    public void updateStatus(int orderId, String status) {
        Order order = orderRepository.findById(id);
        
        // but how about this?? this still requires me to use if/else or switch
    }
}

如您所见,我可以在 store(Order order) 方法中创建订单时执行此操作,但我不知道在 updateStatus(int orderId, String status) 中执行此操作,因为它仍然需要检查状态值使用正确的状态。

switch (status) {
    case "EXPIRED": {
        ExpiredState state = ExpiredState.getInstance();
        OrderContext context = new OrderContext(state, order);
        context.updateState();

        // do something
        break;
    }
    case "CANCELED": {
        CanceledState state = CanceledState.getInstance();
        OrderContext context = new OrderContext(state, order);
        context.updateState();

        // do something
        break;
    }
    // other case
    default:
        // do something
        break;
}

实现状态设计模式的确切原因是最小化“开关 stuff/hardcoded 检查”和在不破坏当前代码的情况下添加更多状态的灵活性(Open/Close 原则),但也许我错了,也许我知识匮乏,也许我太天真而没有决定使用这种模式。 但是到了最后,我发现我仍然需要使用开关的东西来使用状态模式。

那么,如何处理订单状态变化才是正确的呢?

The exact reason to implement the state design pattern is to minimize "the switch stuff/hardcoded checking" and the flexibility for adding more state without breaking current code (Open/Close principle)

多态性不会取代所有条件逻辑。

but maybe I'm wrong, maybe I'm lack of knowledge, maybe I'm too naive to decide to use this pattern.

考虑响应订单状态变化实际会发生哪些行为变化。如果行为没有改变,就没有理由使用状态模式。

例如,如果订单的行为没有改变,分配一个整数(或枚举)或字符串作为订单状态就可以了:

enum OrderStatus {
    WAITING,
    CANCELLED,
    EXPIRED,
    PURCHASED
}

class Order {
    private OrderStatus status;
    
    public Order() {
        status = OrderStatus.WAITING;
    }
    
    public void setStatus(OrderStatus s) {
        status = s;
    }
    
    public void doOperation1() {
        System.out.println("order status does not affect this method's behavior");
    }
    
    public void doOperation2() {
        System.out.println("order status does not affect this method's behavior");
    }

    public void doOperation3() {
        System.out.println("order status does not affect this method's behavior");
    }
}

如果 doOperation()s 在状态发生变化的情况下保持不变,则此代码可以正常工作。

然而,当 doOperation() 的行为因状态变化而改变时,真正的问题开始出现。您最终会得到如下所示的方法:

...
    public void doOperation3() {
        switch (status) {
        case OrderStatus.WAITING:
            // waiting behavior
            break;
        case OrderStatus.CANCELLED:
            // cancelled behavior
            break;
        case OrderStatus.PURCHASED:
            // etc
            break;
        }
    }
...

对于许多操作来说,这是不可维护的。添加更多 OrderStatus 将变得复杂并影响许多订单操作,违反了 Open/Closed 原则。

状态模式就是专门用来解决这个问题的。一旦你确定了哪些行为发生了变化,你就可以将它们提取到一个界面中。假设 doOperation1() 发生变化:

interface OrderStatus {
  void doOperation1();
}

class WaitingOrderStatus implements OrderStatus {
  public void doOperation1() {
    System.out.println("waiting: doOperation1()");
  }

  public String toString() {
    return "WAITING";
  }
}

class CancelledOrderStatus implements OrderStatus {
  public void doOperation1() {
    System.out.println("cancelled: doOperation1()");
  }

  public String toString() {
    return "CANCELLED";
  }
}

class Order implements OrderStatus {
    private OrderStatus status;
    
    public Order() {
        status = new WaitingOrderStatus();
    }
    
    public void setStatus(OrderStatus s) {
        status = s;
    }
    
    public void doOperation1() {
      status.doOperation1();
    }
    
    public void doOperation2() {
        System.out.println("order status does not affect this method's behavior");
    }

    public void doOperation3() {
        System.out.println("order status does not affect this method's behavior");
    }
}

class Code {
    public static void main(String[ ] args) {
        Order o = new Order();
        
        o.doOperation1();
    }
}

添加新状态很容易,它遵守 Open/Closed 原则。