首页 > 解决方案 > 如何使用状态设计模式处理订单状态更改

问题描述

目前,我正在做订单微服务,其中有两种与订单状态更改相关的方法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)方法中创建 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;
}

实现状态设计模式的确切原因是最大限度地减少“开关内容/硬编码检查”以及在不破坏当前代码的情况下添加更多状态的灵活性(打开/关闭原则),但也许我错了,也许我缺乏知识,也许我太天真决定使用这种模式。但最终,我发现我仍然需要使用 switch 的东西来使用状态模式。

那么,处理订单状态变化的正确方法是什么?

标签: oopdesign-patternssoftware-designclass-design

解决方案


实现状态设计模式的确切原因是最大限度地减少“开关材料/硬编码检查”以及在不破坏当前代码的情况下添加更多状态的灵活性(打开/关闭原则)

多态并不能取代所有的条件逻辑。

但也许我错了,也许我缺乏知识,也许我太天真而无法决定使用这种模式。

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

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

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()s 的行为由于状态变化而发生变化时,就会出现真正的问题。您最终会得到如下所示的方法:

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

对于许多操作,这是不可维护的。添加更多 OrderStatus 将变得复杂并影响许多 Order 操作,违反 Open/Closed Principal。

状态模式旨在专门解决这个问题。一旦您确定了哪些行为发生了变化,您就可以将它们提取到一个界面中。让我们想象一下 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 Principal。


推荐阅读