京东订单编号查询,京东订单编号查询快递?

大家好,欢迎关注极客架构师,极客架构师,专注架构师成长,我是码农老吴。

本期是我刚刚推出的,《架构师基本功之设计模式》的第5期,我将结合京东订单的状态流转,来分享我们的第三个设计模式,状态模式(State pattern)。

结论先行

基于REIS分析模型,状态模式,包含三种角色,分别是状态服务方角色,状态客户方角色,特定状态服务方角色,状态模式的宗旨是通过特定状态服务方,将原来属于状态服务方的多状态模式,转化为单状态模式,降低了状态服务的逻辑复杂度,提高了状态服务的可扩展性。

基本思路

案例介绍:京东订单状态流转

第一版代码:普通方式

第二版代码:基于状态模式

状态模式定义,通用类图及代码

状态模式冗余代码化解大法:状态模式的二次方(下期内容)

状态模式 VS 策略模式(下期内容)

状态模式的真正舞台(下期内容)

案例介绍:京东订单状态流转模块

京东订单编号查询,京东订单编号查询快递?

京东购物流程图

当我们在电商平台购物时,基本的流程是如下:

  1. 搜索自己需要的商品,
  2. 进入商品列表页面,点击商品,
  3. 进入商详页,了解商品之后,将商品加入购物车,
  4. 进入购物车页面,确认购买的商品之后,点击结算按钮,
  5. 进入结算页面,选择收货地址,配送方式等,点击支付按钮,
  6. 进入支付页面,进行订单支付,支付完成,
  7. 进入自己的订单列表页,可以看到自己刚刚下的订单。

当我们提交一个订单之后,订单在电商系统后台,会经历很多状态流转,直至到订单的完结状态(妥投,取消,删除等)。订单状态的流转,涉及电商系统的交易,仓储,配送等各个环节。当然,不同品类商品的订单,订单状态流转差异也是比较大的,像我以前所在的运营商部门,里面的手机充值业务,因为是虚拟商品,不涉及仓储,配送等环节,状态流转就简化了不少。

而实物类商品,如手机销售业务,状态流转就比较复杂,如果是合约机,或者以贷款的方式分期购买合约机,状态流转的复杂程度,可以用恐怖二字来形容,买个东西,比西天取经还难。一个订单,涉及了实物商品(手机),虚拟商品(合约),金融服务(分期贷款),业务流程复杂到让产品经理焦头烂额,让架构师眉头紧锁,开发这样一个项目,有直接关系的部门一个巴掌都数不过来,还不包括有间接关系的部门,系统上线前,我们常常想,只要有一个用户能下单成功,我们就算没有白忙活。

京东订单编号查询,京东订单编号查询快递?

订单状态流转图

一般的实物商品订单,通常要经历交易,仓储,配送等几个环节,每个环节,订单有不同的状态。比如:

  • 交易环节,就有未支付,已支付,已对账等几个状态。
  • 仓储环节,有打印面单,出库,打包,发货等状态。
  • 配送环节,有分拣,配送,妥投等状态。

对于订单的每个状态,前端支持的用户行为也各不相同,我们以几个常见的用户操作为例,方便我们进行后面的讲解。下面是常见的几个订单状态,支持的用户操作。

  • 未支付:支付,取消,删除。
  • 已支付:催单
  • 已发货:催单
  • 已妥投:点评,删除
  • 已删除:用户就看不到了,所以不支持任何操作。

系统挑战

订单的状态流转,受商品和业务的影响,是复杂多变的,如何简化在订单特定状态下的业务处理逻辑,提高系统对状态的高扩展性,是我们要利用状态模式解决的问题。

功能限制,我们的重点是讲解状态模式,所以对实现的功能模块需要进行一定的简化。我们只关注以上五个订单状态(未支付,已支付,已发货,已妥投,已删除)和三个订单相关的操作(支付,催单,删除)。

下面我们看第一版代码,以普通的方式实现。

第一版代码,普通方式

UML类图

京东订单编号查询,京东订单编号查询快递?

类图中关键接口和类如下:

IOrderService:订单服务接口

OrderServiceV1:订单服务实现类

OrderInfo:订单信息实体类

OrderStateEnum:订单状态枚举类

TestOrderServiceV1:测试类

IOrderService:订单服务接口

package com.geekarchitect.patterns.demo0201;

/**
 * 订单服务接口
 *
 * @author 极客架构师@吴念
 * @createTime 2022/4/15
 */
public interface IOrderService {

    /**
     * @author: 极客架构师@吴念
     * @date: 2022/4/15
     * @param: [orderID]
     * @return: void
     */
    boolean pay(OrderInfo orderInfo);

    /**
     * 催单
     *
     * @author: 极客架构师@吴念
     * @date: 2022/4/15
     * @param: [orderID, message]
     * @return: void
     */
    boolean reminder(OrderInfo orderInfo);

    /**
     * 删除订单
     *
     * @author: 极客架构师@吴念
     * @date: 2022/4/15
     * @param: [OrderID]
     * @return: boolean
     */
    boolean delete(OrderInfo orderInfo);

    /**
     * 修改订单状态
     * @author: 极客架构师@吴念
     * @date: 2022/4/18
     * @param: [orderInfo]
     * @return: void
     */
    void changeState(OrderInfo orderInfo,OrderStateEnum newState);
}

OrderServiceV1:订单服务实现类

package com.geekarchitect.patterns.demo0201;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 订单服务
 * @author 极客架构师@吴念
 * @createTime 2022/4/15
 */
public class OrderServiceV1 implements IOrderService {
    private static final Logger LOG = LoggerFactory.getLogger(OrderServiceV1.class);
    @Override
    public boolean pay(OrderInfo orderInfo) {
        boolean result=false;
        switch (orderInfo.getOrderStateEnum()){
            case UNPAID:
                //此处省略几百行业务代码
                LOG.info("支付成功");
                changeState(orderInfo,OrderStateEnum.PAID);
                result=true;
                break;
            default:
                LOG.info("无法支付");
        }
        return result;
    }

    @Override
    public boolean reminder(OrderInfo orderInfo) {
        boolean result=false;
        switch (orderInfo.getOrderStateEnum()){
            case PAID:
            case DELIVERED:
                //此处省略几百行业务代码
                LOG.info("催单成功");
                result=true;
                break;
            default:
                LOG.info("无法催单");
        }
        return result;
    }

    @Override
    public boolean delete(OrderInfo orderInfo) {
        boolean result=false;
        switch (orderInfo.getOrderStateEnum()){
            case UNPAID:
            case DONE:
                //此处省略几百行业务代码
                LOG.info("删除成功");
                changeState(orderInfo,OrderStateEnum.DELETED);
                result=true;
                break;
            default:
                LOG.info("无法删除");
        }
        return result;
    }

    @Override
    public void changeState(OrderInfo orderInfo,OrderStateEnum newState) {
        LOG.info("订单状态:由{}转变为{}",orderInfo.getOrderStateEnum().getName(),newState.getName());
        orderInfo.setOrderStateEnum(newState);
    }
}

OrderInfo:订单信息实体类

package com.geekarchitect.patterns.demo0201;

import lombok.Data;

/**
 * 订单信息
 * @author 极客架构师@吴念
 * @createTime 2022/4/16
 */
@Data
public class OrderInfo {
    /**
     * 订单id
     */
    private String ID;
    /**
     * 订单状态
     */
    private OrderStateEnum orderStateEnum;

}

OrderStateEnum:订单状态枚举类

package com.geekarchitect.patterns.demo0201;

public enum OrderStateEnum {
    UNPAID("未支付", 0), PAID("已支付", 1), DELIVERED("已发货", 2), DONE("已妥投", 3), DELETED("已删除", 4);

    private String name;
    private int code;

    OrderStateEnum(String name, int code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

TestOrderServiceV1:测试类

package com.geekarchitect.patterns.demo0201;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 极客架构师@吴念
 * @createTime 2022/4/18
 */
public class TestOrderServiceV1 {
    private static final Logger LOG = LoggerFactory.getLogger(TestOrderServiceV1.class);
    public static void main(String[] args) {
        LOG.info("第一版代码:普通方式");
        TestOrderServiceV1 testOrderService = new TestOrderServiceV1();
        //testOrderService.testPay();
        testOrderService.testReminder();
    }

    public void testPay() {
        LOG.info("测试支付方法pay()");
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setID("1");
        orderInfo.setOrderStateEnum(OrderStateEnum.UNPAID);
        IOrderService orderService = new OrderServiceV1();
        //第一次支付
        orderService.pay(orderInfo);
        //第二次支付
        orderService.pay(orderInfo);
    }

    public void testReminder() {
        LOG.info("测试催单方法reminder()");
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setID("1");
        orderInfo.setOrderStateEnum(OrderStateEnum.UNPAID);
        IOrderService orderService = new OrderServiceV1();
        //第一次催单
        orderService.reminder(orderInfo);
        //支付
        orderService.pay(orderInfo);
        //第二次催单
        orderService.reminder(orderInfo);
    }
}

运行结果

京东订单编号查询,京东订单编号查询快递?

京东订单编号查询,京东订单编号查询快递?

头脑风暴

京东订单编号查询,京东订单编号查询快递?

如上图代码所示,这版代码的特点是,OrderServiceV1(订单服务类)上面的每个方法,都需要考虑订单的各个状态,业务逻辑相对来说复杂一些,如果有一个状态需要调整,上面的方法基本都会受到影响,如果要增加一个新的订单状态,上面的方法都需要进行相应修改,扩展性不高,不符合开闭原则。下面我们基于状态模式进行重构。

第二版代码,基于状态模式

UML类图

京东订单编号查询,京东订单编号查询快递?

新增接口和类

IOrderStateService:订单状态服务接口

AbstractOrderStateService:订单状态服务抽象类

UnpaidOrderStateService:未支付订单状态服务类

PaidOrderStateService:已支付订单状态服务类

DeliveredOrderStateService:已发货订单状态服务类

DeletedOrderStateService:已删除订单状态服务类

受影响的接口和类

OrderServiceV2:订单服务类

TestOrderServiceV2:测试类

UML序列图

京东订单编号查询,京东订单编号查询快递?

IOrderStateService:订单状态服务接口

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.OrderInfo;/** * 订单状态服务接口 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public interface IOrderStateService {    /**     * 支付     * @author: 极客架构师@吴念     * @date: 2022/4/15     * @param: [orderID]     * @return: void     */    boolean pay(OrderInfo orderInfo);    /**     * 催单     *     * @author: 极客架构师@吴念     * @date: 2022/4/15     * @param: [orderID, message]     * @return: void     */    boolean reminder(OrderInfo orderInfo);    /**     * 删除订单     *     * @author: 极客架构师@吴念     * @date: 2022/4/15     * @param: [OrderID]     * @return: boolean     */    boolean delete(OrderInfo orderInfo);}

AbstractOrderStateService:订单状态服务抽象类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import com.geekarchitect.patterns.demo0201.OrderInfo;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 订单状态服务抽象类 * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class AbstractOrderStateService implements IOrderStateService {    private static final Logger LOG = LoggerFactory.getLogger(AbstractOrderStateService.class);    protected IOrderService orderService;    public AbstractOrderStateService(IOrderService orderService) {        this.orderService = orderService;    }    @Override    public boolean pay(OrderInfo orderInfo) {        LOG.info("订单状态:{},无法支付", orderInfo.getOrderStateEnum().getName());        return false;    }    @Override    public boolean reminder(OrderInfo orderInfo) {        LOG.info("订单状态:{},无法催单", orderInfo.getOrderStateEnum().getName());        return false;    }    @Override    public boolean delete(OrderInfo orderInfo) {        LOG.info("订单状态:{},无法删除", orderInfo.getOrderStateEnum().getName());        return false;    }}

UnpaidOrderStateService:未支付订单状态服务类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import com.geekarchitect.patterns.demo0201.OrderInfo;import com.geekarchitect.patterns.demo0201.OrderStateEnum;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 未支付订单状态服务 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class UnpaidOrderStateService extends AbstractOrderStateService {    private static final Logger LOG = LoggerFactory.getLogger(UnpaidOrderStateService.class);    public UnpaidOrderStateService(IOrderService orderService) {        super(orderService);    }    @Override    public boolean pay(OrderInfo orderInfo) {        //此处省略几百行业务代码        LOG.info("支付成功");        this.orderService.changeState(orderInfo, OrderStateEnum.PAID);        return true;    }    @Override    public boolean delete(OrderInfo orderInfo) {        //此处省略几百行业务代码        LOG.info("删除成功");        this.orderService.changeState(orderInfo, OrderStateEnum.DELETED);        return true;    }}

PaidOrderStateService:已支付订单状态服务类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import com.geekarchitect.patterns.demo0201.OrderInfo;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 已支付订单状态服务 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class PaidOrderStateService extends AbstractOrderStateService {    private static final Logger LOG = LoggerFactory.getLogger(PaidOrderStateService.class);    public PaidOrderStateService(IOrderService orderService) {        super(orderService);    }    @Override    public boolean reminder(OrderInfo orderInfo) {        //此处省略几百行业务代码        LOG.info("催单成功");        return true;    }}

DeliveredOrderStateService:已发货订单状态服务类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import com.geekarchitect.patterns.demo0201.OrderInfo;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 已发货订单状态服务 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class DeliveredOrderStateService extends AbstractOrderStateService {    private static final Logger LOG = LoggerFactory.getLogger(DeliveredOrderStateService.class);    public DeliveredOrderStateService(IOrderService orderService) {        super(orderService);    }    @Override    public boolean reminder(OrderInfo orderInfo) {        //此处省略几百行业务代码        LOG.info("催单成功");        return true;    }}

DeletedOrderStateService:已删除订单状态服务类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 已删除订单状态服务 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class DeletedOrderStateService extends AbstractOrderStateService {    private static final Logger LOG = LoggerFactory.getLogger(DeletedOrderStateService.class);    public DeletedOrderStateService(IOrderService orderService) {        super(orderService);    }}

OrderServiceV2:订单服务类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import com.geekarchitect.patterns.demo0201.OrderInfo;import com.geekarchitect.patterns.demo0201.OrderStateEnum;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.HashMap;import java.util.Map;/** * 订单服务类 * @author 极客架构师@吴念 * @createTime 2022/4/15 */public class OrderServiceV2 implements IOrderService {    private static final Logger LOG = LoggerFactory.getLogger(OrderServiceV2.class);    private final Map<Integer, IOrderStateService> orderStateServiceMap = new HashMap<>();    public OrderServiceV2() {        //初始化状态服务对象        orderStateServiceMap.put(OrderStateEnum.UNPAID.getCode(), new UnpaidOrderStateService(this));        orderStateServiceMap.put(OrderStateEnum.PAID.getCode(), new PaidOrderStateService(this));        orderStateServiceMap.put(OrderStateEnum.DELIVERED.getCode(), new DeliveredOrderStateService(this));        orderStateServiceMap.put(OrderStateEnum.DELETED.getCode(), new DeletedOrderStateService(this));    }    private IOrderStateService getOrderStateService(OrderInfo orderInfo) {        IOrderStateService orderStateService = orderStateServiceMap.get(orderInfo.getOrderStateEnum().getCode());        if (orderStateService != null) {            return orderStateService;        }        throw new UnsupportedOperationException("该状态不支持");    }    @Override    public boolean pay(OrderInfo orderInfo) {        IOrderStateService orderStateService = getOrderStateService(orderInfo);        return orderStateService.pay(orderInfo);    }    @Override    public boolean reminder(OrderInfo orderInfo) {        IOrderStateService orderStateService = getOrderStateService(orderInfo);        return orderStateService.reminder(orderInfo);    }    @Override    public boolean delete(OrderInfo orderInfo) {        IOrderStateService orderStateService = getOrderStateService(orderInfo);        return orderStateService.delete(orderInfo);    }    @Override    public void changeState(OrderInfo orderInfo, OrderStateEnum newState) {        LOG.info("订单状态:由{}转变为{}", orderInfo.getOrderStateEnum().getName(), newState.getName());        orderInfo.setOrderStateEnum(newState);    }}

TestOrderServiceV2:测试类

package com.geekarchitect.patterns.demo0202;import com.geekarchitect.patterns.demo0201.IOrderService;import com.geekarchitect.patterns.demo0201.OrderInfo;import com.geekarchitect.patterns.demo0201.OrderStateEnum;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 测试类 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class TestOrderServiceV2 {    private static final Logger LOG = LoggerFactory.getLogger(TestOrderServiceV2.class);    public static void main(String[] args) {        LOG.info("第二版代码:基于状态模式");        TestOrderServiceV2 testOrderService = new TestOrderServiceV2();        testOrderService.testPay();        //testOrderService.testReminder();        //testOrderService.testDelete();    }    public void testPay() {        LOG.info("测试支付方法pay()");        OrderInfo orderInfo = new OrderInfo();        orderInfo.setID("1");        orderInfo.setOrderStateEnum(OrderStateEnum.UNPAID);        IOrderService orderService = new OrderServiceV2();        //第一次支付        orderService.pay(orderInfo);        //第二次支付        orderService.pay(orderInfo);    }    public void testReminder() {        LOG.info("测试催单方法reminder()");        OrderInfo orderInfo = new OrderInfo();        orderInfo.setID("1");        orderInfo.setOrderStateEnum(OrderStateEnum.UNPAID);        IOrderService orderService = new OrderServiceV2();        //第一次催单        orderService.reminder(orderInfo);        //支付        orderService.pay(orderInfo);        //第二次催单        orderService.reminder(orderInfo);    }    public void testDelete() {        LOG.info("测试删除订单方法delete()");        OrderInfo orderInfo = new OrderInfo();        orderInfo.setID("1");        orderInfo.setOrderStateEnum(OrderStateEnum.DONE);        IOrderService orderService = new OrderServiceV2();        orderService.delete(orderInfo);    }}

运行结果

京东订单编号查询,京东订单编号查询快递?

京东订单编号查询,京东订单编号查询快递?

头脑风暴

这一版,我们就实现了基于状态模式的代码。和上一版比较,我们增加了一个接口,一个抽象类和四个实现类

IOrderStateService:订单状态服务接口

AbstractOrderStateService:订单状态服务抽象类

UnpaidOrderStateService:未支付订单状态服务类

PaidOrderStateService:已支付订单状态服务类

DeliveredOrderStateService:已发货订单状态服务类

DeletedOrderStateService:已删除订单状态服务类

看到这里,大家可能都要气得吐血啦,吐血没关系,可以服用东阿阿胶,他们的广告语写的好,东方有佳人,透如琥珀润如玉,百年堂阿胶,程序员的美丽工程师 (这个有点像插入广告哦)。

代码量没有精简,反而暴增,仅仅状态服务类就增加了四个,分别对应四个状态,那要是有几十个状态,就需要增加几十个相应的类,对,是这样的,就是要这么干。

不仅仅是类的增加,方法的实现,代码重复的概率也大大提高了。虽然我们通过抽象父类,提供了各个方法的默认操作,对代码进行了简化,但是还是有可能重复的。因为有不少方法,是支持多个状态的。

能量守恒,物质不灭,不仅仅是物理学定理,也同样适用于我们软件开发行业。这里就是要用空间(更多的类),来换取每个方法的逻辑简化,换取状态的高扩展性。

至于如何简化这些代码,是另外一个问题,不是这个模式所关心的,在后面的状态模式冗余代码化解大法中,我们会聊聊这个问题,下面我们就看看什么是状态模式。

状态模式(State Pattern)定义

Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.

—— Gof《Design Patterns: Elements of Reusable Object-Oriented Software》

中文解释如下:

允许一个对象,在其内部状态改变时,改变它的行为。对象看起来似乎修改了它的类。

这个定义里面我们要注意的关键词只有一个,就是状态

状态

谁的状态?

对象的状态

状态怎么了?

状态发生了变化,被谁改变的,如何改变的,定义里面都没有,说明这些都不重要,重要的是它改变了。

状态改变的后果?

状态改变的后果,就是对象的行为发生了变化,在面向对象的编程思想体系中,行为通常就是类中定义的方法,或者说对象的方法。以前的面向过程语言里面,被称为函数。

一般情况下,类中的方法,在定义类的时候功能已经明确了,固定了,但是这里它的行为却发生了变化,造成的后果就是,给人一种错觉,对象对应的类发生了变化,既然是错觉,就意味着类并没有发生变化。

那么类的方法,如何才能发生变化,是状态模式要解决的重要问题。

在我们上面的案例中。

京东订单编号查询,京东订单编号查询快递?

当订单状态发生变化时,OrderServiceV2订单服务类在接收到客户请求时,会根据订单状态,调用不同的状态服务类的方法,这就是它实现改变对象行为的方式,就是这么简单,直接

总结一下,状态模式,就是当对象的内部状态发生变化时,对象方法的行为,也会随之改变,给人造成一种对象对应的类发生了改变的错觉。

下面我们用我自创的设计模式分析模型REIS模式,对状态模式进行分析,大家就会理解的更透彻一些。

REIS模型分析状态模式

REIS模型是我总结的分析设计模式的一种方法论,主要包括场景(scene),角色(role),交互(interaction),效果(effect)四个要素,后面我会专门分享一下这套方法论。

场景(Scene)

场景,也就是我们在什么情况下,遇到了什么问题,需要使用某个设计模式。

对于状态模式,当出现以下情况时,可能需要使用状态模式。

  1. 从业务角度看:

当一个业务依赖的业务流程,受到对象的状态影响比较大,而且对象的状态需要有一定的扩展性时,可以考虑使用状态模式,比如常见的工作流系统。

  1. 从技术角度看:

当一个类的多个方法,都受到同一个对象状态值影响时,可以考虑使用状态模式。

当一个类的多个方法,里面都充满了分支语句(if,switch),这些分支语句的条件都是某个对象的状态时,可以考虑使用状态模式,这个比较直白一些。

角色(Role)

角色,一般为设计模式出现的类,或者对象。每种角色有自己的职责。

在状态模式中,包含三种角色,状态服务方角色,状态客户方角色,特定状态服务方角色。

状态服务方角色(state server role):状态服务方,就是定义里面所说的那个对象,它的职责如下。

  1. 提供状态:这个状态可以是我们常规上理解的对象的某个属性,这属于物理的,真实存在的状态。也可以是逻辑的,也就是这个状态可能不存在,只是我们头脑中想象的,或者即使存在,但并不在这个对象里面。我们的案例里面,订单状态,就不属于OrderServiceV2类(订单服务类)的属性中,但它确确实实的起到了状态的作用。
  2. 提供状态控方法:什么是状态控方法呢,说到手机控,电脑控,电视控,大家应该能很容易理解它的含义,而状态控方法,也是同样的思路,就是这些方法都受到某个状态的影响,控制,他们的行为,会根据状态而产生变化,所以叫状态控方法,这是我自创的,感觉比较形象些,在原著里面称为“state-specific”。
  3. 提供状态流转的方法:状态流转,也就是从一个状态,切换到另外一个状态,状态服务方,可以提供这个方法,也可以不提供,如果提供了,那么就需要保证后面的特定状态服务方角色,可以访问到这个方法。所以经常见到状态服务方,把自己(this)作为参数,传递给了特定状态服务方角色。我们的通用代码里面,也是这么实现的。

状态客户方角色(state client role):状态客户方,在这个模式中,基本上就是滥竽充数的,起到占位符的作用,定义它只是为了让语境更完整,类图,序列图更好看些,如果非要说它有什么职责的话,那就是调用状态服务方里面的状态控方法,而且调用的时候,它是不用考虑状态问题。

特定状态服务方角色(specific state server role):如果说状态服务方角色是整个状态模式的大哥,那么特定状态服务方角色,就是围绕在大哥身边的一群小弟。前者立志要给状态客户方,提供全状态的服务,口头语是“一切状态,我帮你搞定”,后者则是专注在特定状态的服务上,口头语是“这个状态的事情,我帮大哥搞定”。所有的特定状态服务方,像小弟一样,齐心协力,团结一致,帮助状态服务方,也就是他们的大哥,实现所有的服务,它的职责如下。

  1. 提供特定状态下,所有状态控方法的实现:也就是状态服务方角色里面的状态控方法,在这里都需要实现,只不过,因为状态明确,单一,所以代码实现逻辑难度会大大降低。
  2. 状态流转:特定状态服务方角色,里面实现的状态控方法,可以根据业务需求,进行状态切换。但是要注意,状态一旦切换,控制权就得交还给状态服务方(它们的大哥),由状态服务方,根据新的状态,指派相应的特定状态服务方对象,这一点要特别注意

在上面的第二版代码,基于状态模式实现的订单服务中。

状态服务方角色对应的接口和类如下:

OrderServiceV2:订单服务类

特定状态服务方角色对应的接口和类如下:

IOrderStateService:订单状态服务接口

AbstractOrderStateService:订单状态服务抽象类

UnpaidOrderStateService:未支付订单状态服务类

PaidOrderStateService:已支付订单状态服务类

DeliveredOrderStateService:已发货订单状态服务类

DeletedOrderStateService:已删除订单状态服务类

交互(interaction)

交互,是指设计模式中,各种角色是如何交互的,一般用UML中的序列图,活动图来表示。简单的说就是角色之间是如何配合,完成设计模式的使命的。

状态模式,交互不是特别复杂,如下所示。

京东订单编号查询,京东订单编号查询快递?

基本流程如下:

1,状态客户方角色,发起请求,调用状态服务方里面的状态控方法。

2,状态服务方,根据当前的状态,获取相应的特定状态服务方对象。

3,调用特定状态服务方对象相应的方法。

4,依次返回结果。

综上所述,基于REIS分析模型,状态模式,包含三种角色,分别是状态服务方角色,状态客户方角色,特定状态服务方角色,状态模式的宗旨是通过特定状态服务方,将原来属于状态服务方的多状态模式,转化为单状态模式,降低了状态服务的逻辑复杂度,提高了状态服务的可扩展性。

我们还是以找房子的案例,让大家理解状态模式。

假如你刚刚大学毕业,来北京找工作,人生地不熟,需要先找个地方住。来到一家中介公司。找房整个过程,大致可以分为筛选房源,看房,签合约,付费等几个环节,每个环节,都可以理解为找房事件的一个状态,中介公司都安排专门的人员与你对接,给你提供服务。在这个案例中,你就是状态客户方,中介公司就是状态服务方,每个环节,与你对接的人员就是特定状态服务方。

效果(effect)

效果,使用该设计模式之后,达到了什么效果,有何意义,当然,也可以说说它的缺点,或者风险。

从我们前面的案例可以看出,状态模式达到了以下效果。

积极效果

  1. 大大降低了每个状态控方法的实现难度:以前是一个方法里面,需要考虑对象的所有状态,现在只需要考虑特定的状态。
  2. 提供了状态的扩展性:当一个状态下的行为有变化时,只需要修改相应的特定状态服务方对应的类即可,如果增加了新的状态,只需要增加一个新的特定状态服务方即可。

消极效果

  1. 类的个数增加了:原来一个类干的事情,分散到多个类里面了,而且是有多少个状态,就需要相应的增加多少个类。
  2. 状态控方法代码重复的概率急剧增加:原来只需要写一个方法,现在,需要在每个状态下,都实现该方法。极大的增加了代码重复的概率。如何降低代码重复率,是这个模式需要面临的难题。

状态模式的难点:如何降低特定状态服务方的方法代码重复率

正如上面我们在效果中所说的,使用了状态模式,状态控方法的代码重复率会急剧上升,如何降低代码重复率,可以有以下几个思路,我先简单阐述一下。

1,充分利用特定状态服务方继承的抽象类:这个方法,我们在上面已经使用过了,我们在抽象类中,实现了状态控方法的默认实现(这个其实就是钩子方法),这种方法,基于继承实现代码复用,缺点是会造成抽象类体积急剧膨胀。

2,给状态控方法,建立专用的辅助类:我们可以针对某些复杂的,代码重复率比较高的受控方法,建立专门的辅助类,以组合的方式实现代码复用。

3,状态模式的二次方:假设把所有状态分为几个状态组,某个状态控方法在每个组的代码是重复的,这个时候,我们就可以给这个状态控方法,再使用一次状态模式,也就是状态模式嵌套状态模式,有点看热闹的不怕事大,一个就已经够复杂的了,再嵌入一个,是不是更复杂,实际恰恰相反。

上面这些方案都比较复杂,一两句话说不清楚,我后面专门用一期进行讲解。

状态模式的疑问点:谁可以进行状态切换?

我们现在已经知道,在状态模式中有三个角色,状态服务方角色,状态客户方角色,特定状态服务方角色,那么应该由哪个角色,进行状态的切换。

推荐方案,也是最佳方案,就是在状态服务方角色中,提供状态转换的方法。而特定状态服务方对象,根据自己的需要,调用这个方法就可以。这个方法的好处是,状态切换集中在一个类里面,比较容易管理,一般情况下,状态切换代码量不会太大,但是又比较重要,统一管理,反而容易维护。

也可以分散在各个特定状态服务对象的类里面,缺点是分散,不容易管理,但是如果状态切换比较复杂,分散反而是一种优势。

另外,状态客户方,也可以根据自己的需要,调用服务方提供的状态切换方法,进行状态切换,这也是一种选择,但是一般不提倡。

通用类图和代码

下面我们看看状态模式的通用UML类图和代码。大家注意,我讲的和普通设计模式书籍上的,是不太一样的,看仔细了。

UML类图

京东订单编号查询,京东订单编号查询快递?

接口及类:

StateClientRole:状态客户方

IStateServerRole:状态服务方接口

StateServerRole:状态服务方实现类

ISpecificStateServerRole:特定状态服务方接口

AbstractSpecificStateServerRole:特定状态服务方抽象类

SpecificStateServerRole01:特定状态服务方类1

SpecificStateServerRole02:特定状态服务方类2

StateEnum:状态枚举类

TestStateClientRole:测试类

UML序列图

京东订单编号查询,京东订单编号查询快递?

StateClientRole:状态客户方

package com.geekarchitect.patterns.demo0204;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 状态客户方 * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class StateClientRole {    private static final Logger LOG = LoggerFactory.getLogger(StateClientRole.class);    public void clientMethod() {        LOG.info("状态模式通用代码");        IStateServerRole stateServerRole = new StateServerRole();        stateServerRole.stateServer1();    }}

IStateServerRole:状态服务方接口

package com.geekarchitect.patterns.demo0204;/** * 状态服务方-接口 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public interface IStateServerRole {    /**     * 状态控方法1     */    void stateServer1();    /**     * 状态控方法2     */    void stateServer2();    /**     * 非状态控方法     */    void unStateServer();    /**     * 改变状态     */    void changeState();}

StateServerRole:状态服务方实现类

package com.geekarchitect.patterns.demo0204;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.util.HashMap;import java.util.Map;/** * 状态服务方-实现类 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class StateServerRole implements IStateServerRole {    private static final Logger LOG = LoggerFactory.getLogger(StateServerRole.class);    private final Map<Integer, ISpecificStateServerRole> specificStateServerRoleMap = new HashMap<>();    public StateServerRole() {        LOG.info("状态服务方:StateServerRole,初始化特定状态服务方对象");        specificStateServerRoleMap.put(StateEnum.STATE01.ordinal(), new SpecificStateServerRole01(this));        specificStateServerRoleMap.put(StateEnum.STATE02.ordinal(), new SpecificStateServerRole02(this));    }    private ISpecificStateServerRole getSpecificStateServer() {        LOG.info("状态服务方:StateServerRole,根据状态获取相应的特定状态服务方对象");        //根据实际业务实现该代码        return specificStateServerRoleMap.get(StateEnum.STATE01.ordinal());    }    @Override    public void stateServer1() {        LOG.info("状态服务方:StateServerRole,执行stateServer1");        ISpecificStateServerRole specificStateServerRole = getSpecificStateServer();        specificStateServerRole.stateServer1();    }    @Override    public void stateServer2() {        LOG.info("状态服务方:StateServerRole,执行stateServer2");        ISpecificStateServerRole specificStateServerRole = getSpecificStateServer();        specificStateServerRole.stateServer2();    }    @Override    public void changeState() {        LOG.info("状态服务方:StateServerRole,执行changeState");    }    @Override    public void unStateServer() {    }}

ISpecificStateServerRole:特定状态服务方接口

package com.geekarchitect.patterns.demo0204;/** * 特定状态服务方-接口 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public interface ISpecificStateServerRole {    /**     * 状态控方法1     */    void stateServer1();    /**     * 状态控方法2     */    void stateServer2();}

AbstractSpecificStateServerRole:特定状态服务方抽象类

package com.geekarchitect.patterns.demo0204;/** * 特定状态服务方-抽象类 * * @author 极客架构师@吴念 * @createTime 2022/4/20 */public abstract class AbstractSpecificStateServerRole implements ISpecificStateServerRole {    protected IStateServerRole stateServerRole;    public AbstractSpecificStateServerRole(IStateServerRole stateServerRole) {        this.stateServerRole = stateServerRole;    }    @Override    public void stateServer1() {        //默认实现    }    @Override    public void stateServer2() {        //默认实现    }}

SpecificStateServerRole01:特定状态服务方类1

package com.geekarchitect.patterns.demo0204;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 特定状态服务方实现类 * * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class SpecificStateServerRole01 extends AbstractSpecificStateServerRole {    private static final Logger LOG = LoggerFactory.getLogger(SpecificStateServerRole01.class);    public SpecificStateServerRole01(IStateServerRole stateServerRole) {        super(stateServerRole);    }    @Override    public void stateServer1() {        LOG.info("特定状态服务方:SpecificStateServerRole01,执行stateServer1方法");    }}

SpecificStateServerRole02:特定状态服务方类2

package com.geekarchitect.patterns.demo0204;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @author 极客架构师@吴念 * @createTime 2022/4/18 */public class SpecificStateServerRole02 extends AbstractSpecificStateServerRole {    private static final Logger LOG = LoggerFactory.getLogger(SpecificStateServerRole02.class);    public SpecificStateServerRole02(IStateServerRole stateServerRole) {        super(stateServerRole);    }    @Override    public void stateServer1() {        LOG.info("特定状态服务方:SpecificStateServerRole02,执行stateServer1方法");    }    @Override    public void stateServer2() {        LOG.info("特定状态服务方:SpecificStateServerRole02,执行stateServer2方法");    }}

StateEnum:状态枚举类

package com.geekarchitect.patterns.demo0204;public enum StateEnum {    STATE01, STATE02}

TestStateClientRole:测试类

package com.geekarchitect.patterns.demo0204;/** * 测试类 * * @author 极客架构师@吴念 * @createTime 2022/4/20 */public class TestStateClientRole {    public static void main(String[] args) {        TestStateClientRole testStateClientRole = new TestStateClientRole();        testStateClientRole.testStateServer01();    }    public void testStateServer01() {        StateClientRole stateClientRole = new StateClientRole();        stateClientRole.clientMethod();    }}

运行结果

京东订单编号查询,京东订单编号查询快递?

至此,状态模式讲解完毕。

下期内容预告:

状态模式冗余代码化解大法:状态模式的二次方

状态模式 VS 策略模式

状态模式的真正舞台

预知后事如何,且听下回分解

本期我们就分享到这里,关注我,我将持续分享更多架构师的相关文章和视频,我们下期见。

本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 sumchina520@foxmail.com 举报,一经查实,本站将立刻删除。
如若转载,请注明出处:https://www.sumrw.com/17018.html