如何使用状态设计模式处理订单状态更改

时间:2020-07-08 01:35:35

标签: oop design-patterns software-design class-design

当前,我正在研究Order Microservice,其中有两种与订单状态更改store(Order order)updateStatus(int orderId, String status)相关的方法,我将在后面解释。

订单有四种状态:

等待->已过期

等待->已取消

等待->购买

已购买->已取消

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

Order status diagram flow

创建订单时,状态将为“正在等待”,如果用户已付款,则状态将为“已购买”,如果买方或产品所有者取消了订单,则状态将为“已取消”,并且时间超过,则状态变为“已过期”。

对于每个我想工作的微服务,如果可能的话,我将实施四人行(Gang Of Four)设计模式,对于订单状态,我决定实施状态设计模式,因为它与之相关,并且在许多博客中都提到了这一点,例如文档状态内容(草稿,审阅等),音频播放器内容(已暂停,已播放等)等等。

这就是我所做的:

基本状态

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来使用状态模式。

然后,处理订单状态更改的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

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

多态不能代替所有条件逻辑。

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

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

例如,如果订单的行为没有改变,则将整数(或枚举)或字符串分配为订单状态是可以的:

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()保持不变,则此代码可以正常工作。

但是,当doOperation()的行为由于状态变化而发生变化时,就会出现真正的问题。您最终将得到如下所示的方法:

...
    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();
    }
}

添加新状态非常容易,并且遵循“打开/关闭”主体。