设计模式之状态模式


状态模式(State Pattern):类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

案例示例

最常见地就是订单状态变更;将对应状态变更需要执行的业务逻辑封装到单独的类中,便于代码的维护。

代码示例

下面以商城订单状态变更案例来举例。

定义状态操作接口

public interface MallOrderState {

    /**
     * 订单状态业务操作
     * @param orderNo 订单号
     * @return  返回操作结果
     */
    String changeState(String orderNo);

    // TODO 这里还可以定义更多的方法
}

状态操作接口抽象类

之所以增加这么一个中间的抽象类,是为了实现接口一些默认的实现,避免后面增加新的方法时,每个状态操作类都需要去实现。

public abstract class AbstractMallOrderState implements MallOrderState {

    public final MallOrderDao mallOrderDao;

    public AbstractMallOrderState(MallOrderDao mallOrderDao) {
        this.mallOrderDao = mallOrderDao;
    }

    /**
     * 订单状态业务操作
     * @param orderNo 订单号
     * @return  返回操作结果
     */
    @Override
    public String changeState(String orderNo) { 
        // 这里还可以做些公共的操作
        MallOrder mallOrder = mallOrderDao.findByOrderNo(orderNo);
        return exec(mallOrder);
    }

    /**
     * 进行实际的操作
     * @param mallOrder 订单信息
     * @return  返回操作结果
     */
    public abstract String exec(MallOrder mallOrder);

}

对应订单状态的实际业务操作类

取消订单操作

@Component
public class CancelMallOrderState extends AbstractMallOrderState {

    public CancelMallOrderState(MallOrderDao mallOrderDao) {
        super(mallOrderDao);
    }

    /**
     * 取消操作
     */
    @Override
    public String exec(MallOrder mallOrder)  {
        OrderStatus status = mallOrder.getStatus();

        if(status==OrderStatus.CANCEL){
            return "订单已取消";
        }
        if(status!=OrderStatus.WAIT_PAY){
            return "订单状态已变更,不允许此操作";
        }
        boolean updateOk = this.mallOrderDao.updateStatus(mallOrder.getOrderNo(), OrderStatus.CANCEL);
        if(updateOk){
            return "取消成功";
        }
       return "服务繁忙,请稍后再试";
    }
}

发货操作

@Component
public class ShipMallOrderState extends AbstractMallOrderState {

    public ShippMallOrderState(MallOrderDao mallOrderDao) {
        super(mallOrderDao);
    }

    /**
     * 发货操作
     */
    @Override
    public String exec(MallOrder mallOrder)  {
        OrderStatus status = mallOrder.getStatus();

        if(status!=OrderStatus.WAIT_SHIPPED){
            return "订单状态已变更,不允许此操作";
        }
        boolean updateOk = this.mallOrderDao.updateStatus(mallOrder.getOrderNo(), OrderStatus.SHIPPED);
        if(updateOk){
            return "发货成功";
        }
       return "服务繁忙,请稍后再试";
    }
}

定义状态操作类的工厂方法

@Component
public class MallOrderStateFactory {

    @Autowired
    private CancelMallOrderState cancelMallOrderState;

    @Autowired
    private ShipMallOrderState shipMallOrderState;

    public MallOrderState getOrderState(MallOrderOpera orderOpera){
        // 由于订单状态几乎确定了因此我们这里选择使用switch来处理;如果想更简便一点;可以增加一个获取状态的接口,让每个实现类去实现这个接口,然后这里直接通过遍历接口比较状态来处理。
        switch (orderOpera){
            case SHIPPED:
                return shipMallOrderState;
            case CANCEL:
                return cancelMallOrderState;
            default:
                return null;
        }
    }
}

如果想简化上面这种工厂类的方式,可以在状态操作接口MallOrderState中再定义一个用于区分类功能的方法,让每个类去实现,比如就用订单状态作为返回值;这样就不再需要这个工厂类了;示例使用的示例如下:

// 通过@Autowired直接注入
@Autowired
private List<MallOrderState> mallOrderStates;

// 使用的时候通过刚刚额外增加的方法来获取判断使用那个实现类
public String cancel(params...) {
    Optional<MallOrderState> optional = this.mallOrderStates.stream().filter(p -> p.getStatus()==MallOrderOpera.CANCEL).findFirst();
    Assert.isTrue(optional.isPresent(), "");
    return optional.get().changeState(orderNo);
}

实际业务中调用

@Service
public class MallOrderServiceImpl implements MallOrderService {

    @Autowired
    private MallOrderStateFactory mallOrderStateFactory;

    /**
     * 取消订单
     * @param orderNo 订单号
     * @return 返回结果
     */
    @Override
    public String cancel(String orderNo) {
        return mallOrderStateFactory.getOrderState(MallOrderOpera.CANCEL).changeState(orderNo);
    }

    /**
     * 发货
     * @param orderNo 订单号
     * @return 返回结果
     */
    @Override
    public String ship(String orderNo) {
        return mallOrderStateFactory.getOrderState(MallOrderOpera.SHIPPED).changeState(orderNo);
    }

}

这样进行拆分后会让代码更加简洁,每次去维护时目的会更明确;如果写在一起的话,在进行维护的时候就会是一大坨的代码,如果逻辑复杂的话看起来会很恼火。当进行重构拆分时可能会更加的复杂。


特别提醒:扫码关注微信订阅号'起岸星辰',实时掌握IT业界技术资讯! 转载请保留原文中的链接!
  目录