Cuando la orden de Zhuanzhuan Yanxuan se encuentra con una máquina de estado

Introducción a las máquinas de estado

La máquina de estado mencionada aquí, cuyo nombre completo es autómata determinista de estado finito, también se denomina a menudo autómata finito o FSM para abreviar. En el campo del software, es ampliamente utilizado, como la compilación, el reconocimiento de expresiones regulares y el desarrollo de juegos. La máquina de estado mantiene un conjunto de colecciones de estados y colecciones de eventos, que pueden ingresar eventos específicos, hacer que el estado fluya y realizar las acciones correspondientes.

Elementos de la máquina de estado

  • conjunto de estados (estados)
  • Colección de eventos (eventos)
  • Detectores (guardias)
  • transiciones
  • contexto

Alcance del uso del sistema empresarial

En el sistema comercial de Internet, todos los escenarios comerciales que involucran documentos con estados complejos pueden usar máquinas de estado.

aplicación del sistema de negocios

En el sistema de negocios, a través de la configuración del diagrama de salto de estado y la encapsulación de la lógica de negocios del salto de estado, el sistema puede ingresar un evento específico (solicitud de interfaz), realizar un salto de estado y ejecutar la lógica de negocios correspondiente.

Ventajas de las máquinas de estado

En primer lugar, qué tipo de código es un buen código, la sensación más intuitiva es que puedes entenderlo de un vistazo, que es la llamada lógica clara. En otras palabras, es la idea de expresión de código. , que está en línea con la forma en que la mayoría de la gente piensa acerca de los problemas. Las personas están naturalmente disgustadas con las cosas complejas y les gustan las cosas simples, lo que determina que sea difícil para las personas resolver problemas complejos directamente. Los problemas complejos a menudo se pueden ver como una combinación de muchos problemas simples. En la práctica larga, hemos aprendido los conceptos básicos y dominado gradualmente las soluciones a problemas simples. Sin embargo, para problemas complejos, también necesitamos dominar cómo dividir problemas complejos en problemas simples, que es la idea de división. Para la estructura del código, todavía usamos esta idea para pensar. Desde el momento en que la lógica de negocios se vuelve compleja, la gente ha estado pensando en cómo simplificar el problema.Con la idea de los puntos, ha surgido una arquitectura en capas para separar la lógica de negocios, la lógica de control y los datos. Cuando la lógica de negocios se complica aún más, usamos la idea de DDD para dividir la lógica de negocios compleja en los comportamientos de los objetos de dominio y luego los organizamos juntos de una manera fácil de entender a través de los servicios de dominio.

En un sistema empresarial, la aplicación de una máquina de estados no solo significa que se utilizan los componentes de la máquina de estados, sino que también significa la selección de la arquitectura del código, que puede llamarse "arquitectura de la máquina de estados". Porque bien puede dividir nuestra lógica de código y conectar diferentes módulos a través de un mecanismo específico para completar la función del sistema. Este es el papel de la máquina de estado al guiar el diseño del sistema. Permite que nuestro sistema mantenga el principio básico de división lógica en iteraciones a largo plazo.

除了拆分业务逻辑外,它还把控制逻辑和业务逻辑进行了分离。我们上学时都学过这样一个等式,程序=数据结构+算法,后来一位名为Robert Kowalski的大师进一步论证了,算法=逻辑+控制,并提出如果逻辑和控制分离的话,将会让代码更好维护。在业务逻辑复杂的今天,我们本就应该专注于编写业务逻辑,控制逻辑就应该交尽量交给框架去解决。状态机的引入,也就意味着,它帮我们分担了这部分工作。

最后,直观上看,我们的代码基本消除了对状态判断的if...else...代码,这些代码穿插在业务逻辑中,就好比你坐在桌前思考问题时,有一只苍蝇在你眼前晃来晃去。

// 随处可见的状态判断
if (state == EOrderState.WAIT_PAY) {
  ...
} else if (state == EOrderState.PAYED) {
  ...
}
// 下边执行业务逻辑
...

综述,状态机的引入,能够使代码的学习成本降低,也能够使维护成本降低。

状态机在转转严选交易的实践

理解业务

首先,需要绘制状态流转图。要整体理解业务,把单据按业务规则,抽象出一个一个状态,并且明确,什么动作可以促使它从一个状态变成另一个状态。比如,转转严选的订单,用户下单了之后,系统会生成一个订单,此时应该是“待支付”,那么当用户支付后,系统收到了支付消息,也就是收到了“支付”这个事件,就应该从待支付跳转到“已支付”。在这个过程中,我们看到的“状态”,椭圆一个一个画出来,我们提到的“事件”,用文字写在在两个可以流转的“状态”椭圆之间,然后把两个状态按照流转方向用箭头连接。把所有的状态和事件都画好后,状态机的状态跳转图就呈现出来了。

配置状态机

然后,根据把状态流转图,转换为我们的代码,为了便于理解,我们可以把代码设计的更加贴近自然语言。比如从“待支付”到“已支付”的代码可以是这样写的。

StateMachineConf conf = new StateMachineConf();
conf.source(EOrderState.WAIT_PAY)
    .onEvent(EOrderEvent.PAY)
    .target(EOrderState.PAYED)
    .transition(userPayTransition)
    ...

在这一个配置组合的编码之前,我们还需要把每一个状态,每一个事件都放到枚举里定义好。

/**
 * 状态定义
 */
public enum EOrderState implements IFSMState {
    WAIT_PAY(1, "待支付"),
    PAYED(2"已支付"),
    ...
}
/**
 * 事件定义
 */
public enum EOrderEvent implements IFSMEvent {
    PAY(1, "用户支付"),
    APPLY_FOR_REFUND(2"申请退款"),
    ...
}

把所有的状态和事件写好后,我们的配置代码是这样的:

StateMachineConf conf = new StateMachineConf();
conf.source(s1).onEvent(e1).target(s2).transition(t1)
    .and().source(s2).onEvent(e2).target(s3).transition(t2)
    ...
fsm.setName("转转严选订单状态机");
fsm.config(conf);

业务逻辑

状态机的transition(转换器)是用来执行状态跳转时需要做的事情。所以,需要把我们的业务逻辑,写进对应的transition中,如果不需要执行动作,可以定义一个空的transition

/**
 * 编写业务逻辑
 */
public class BuyerPayTransition implements IFSMTransition<OrderFSMContext> {
    @Override
    public boolean onGuard(OrderFSMContext context, IFSMState targetState) {
        // 检测器逻辑,校验条件
    }

    @Override
    public void onTransition(OrderFSMContext context, IFSMState targetState) {
        // 转换器逻辑,业务逻辑在这里
    }
}

下一步,就是状态机的触发了,也就是输入事件。这一部分逻辑,可以放到分层架构的service层,当然也可以放到facade层,这取决于你如何设计的系统的架构。这里做事件触发时,需要传入一个上下文信息,来告知状态机当前的初态和事件,也可以传入一些自定义的内容,以便业务逻辑执行时使用。

@Service
public class OrderService {
    @Resource
    private StateMachine fsm;

    public void userPay(Order order) {
        OrderFSMContext context = new OrderFSMContext();
        context.setSourceState(order.getState());
        context.setEvent(EOrderEvent.PAY);
        context.setOrder(order);
        fsm.fire(context);
    }
}

异常情况

执行以上方法,状态机就会自动帮我们调用BuyerPayTransition中的逻辑。那么如果出现了异常情况会发生什么呢,比如当前订单已经退过款了,但是系统重复收到了一个退款事件,当然不能重复执行一次退款。首先为了防止并发问题,我们修改订单状态时,要使用类似于乐观锁的机制。

update order set state=3 where id=xxx and state=2;

然后,状态机在选择逻辑时,发现初始状态为“已退款”,事件为“申请退款”,没有可以执行的逻辑分支,这个时候我们可以选择让状态机抛出异常,或者我们定义一个回调,来打印一些友好的信息,或做一些记录。

StateMachineConf conf = new StateMachineConf();
conf.source(s1).onEvent(e1).target(s2).transition(t1)
    .and().source(s2).onEvent(e2).target(s3).transition(t2)
    ...
fsm.setName("转转严选订单状态机");
fsm.config(conf);
fsm.setTransBlock(orderTransBlock);  // 这里配置无法跳转时的回调
@Component
@Slf4j
public class OrderFSMTransBlock implements IFSMTransBlock<OrderFSMContext> {

    @Override
    public boolean onTransBlock(OrderFSMContext context) {
        log.info("状态机无法跳转...");
    }
}

可扩展性考虑

如果有一天,转转在售卖严选手机订单的同时,用户只需支付1元钱即可加购一个手机壳,并且在手机退款时,手机壳必须要同时帮用户退款,如何做呢。按照上边的设计思路,应该这样写:

public class ApplyForRefundTransition implements IFSMTransition<OrderFSMContext> {
    @Override
    public void onTransition(OrderFSMContext context, IFSMState targetState) {
        // 处理手机退款逻辑
        ...
        // 处理手机壳退款逻辑
        ...
    }
}

虽然这样写没什么问题,但是这把两个业务流程耦合在一起了,如果明天需要再加个数据线,后天再加个贴膜...代码就会慢慢腐化,逻辑臃肿,架构坍塌。为了解决这个问题,我们可以设计一个注解,来监听严选手机订单的状态机动作。

@Transition(source = EOrderState.PAYED, event = EOrderEvent.APPLY_FOR_REFUND, fsm = "转转严选订单状态机")
public void phoneShellRefund(OrderFSMContext context) {
    // 处理手机壳退款逻辑
}

写在最后

状态机不是什么高级的技术,重点在于让你用另一种思路去理解,去设计系统,以达到我们想要的目的。生活亦是如此,换一种眼观去看待事物,去理解世界,我们能生活的更幸福。(全文完)


> 转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。

> 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

Supongo que te gusta

Origin juejin.im/post/7121678614893953038
Recomendado
Clasificación