`
madbluesky
  • 浏览: 84050 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

状态机实现

 
阅读更多
说明:
1、参考了spring state machine,但是个人感觉spring state machine,使用注入的方式太重了,而且附加的各种特性如监听,条件选择等等都不实用,不适合自己的业务场景,个人期望的是,每一个实体类都可以有一个自己的状态机,状态机用来清晰的展示状态的迁移逻辑,核心作用是可读性、内聚性,同时可以限制未定义的状态迁移。
2、状态机跟状态的关系,根据个人的理解做了区隔。状态机是状态迁移的逻辑定义,本身并不代表一个状态,状态机的作用就是对于给定的输入(状态,事件),告诉你下一个状态是什么,是一个辅助工具。
3、对于迁移条件,条件绝大部分时候具有外部性,将外部性的逻辑纳入到状态机内部,只能增加状态机跟外部的耦合性,导致状态机使用复杂,不符合高内聚低耦合、单一职责的开发原则,条件可以通过增加事件类型来处理。
4、对于状态发生变化时,自动触发某个操作,这个操作也是外部性的,如果采用ddd建模,这个触发的操作,应该是聚合的职责,而不是状态机的职责。

总而言之,个人对状态机的理解和需求,是一个简单易用,主要目的是用来明确展示状态迁移规则,增强状态变化业务逻辑内聚性的工具,如下这个实现,很好的满足了这个需求,如果你也在找这样一个状态机,不妨参考下,能给一些建议就更好了。


public class StateMachine<S,E> {
	private Map<S,List<Rule>> ruleMap = new HashMap<>();
	private Set<S> startNodes = new HashSet<>();
	private Set<S> targetNodes = new HashSet<>();
	private final StateMachine<S,E> innerStateMachine = this;
	
	@SuppressWarnings("unchecked")
	public void init(S ...status) {
		if(status !=null && status.length > 0) {
			for(S s : status) {
				startNodes.add(s);
			}
		}
	}
	public When when(S status) {
		if(!startNodes.contains(status) && !targetNodes.contains(status)) {
			throw new RuntimeException("开始状态尚未定义");
		}
		return new When(status);
	}
	
	public class When {
		private S status;
		public When(S status) {
			this.status = status;
		}
		@SuppressWarnings("unchecked")
		public Occur occur(E... event) {
			return  new Occur(status, event);
		}
	}
	public class Occur {
		private E[] eventArr;
		private S statusFrom;
		Occur(S statusFrom, E[] eventArr){
			this.statusFrom = statusFrom;
			this.eventArr = eventArr;
		}
		public StateMachine<S,E> then(S statusTo) {
			Rule rule = new Rule(statusFrom, eventArr, statusTo);
			List<Rule> ruleList;
			if(ruleMap.containsKey(statusFrom)) {
				ruleList = ruleMap.get(statusFrom);
			} else {
				ruleList = new ArrayList<Rule>();
				ruleMap.put(rule.statusFrom, ruleList);
			}
			ruleList.add(rule);
			targetNodes.add(statusTo);
			return innerStateMachine;
		}
	}
	private class Rule {
		private Set<E> eventSet = new HashSet<>();
		private S statusFrom;
		private S statusTo;
		public Rule(S statusFrom, E[] eventArr, S statusTo) {
			for(E e : eventArr) {
				this.eventSet.add(e);
			}
			this.statusFrom=statusFrom;
			this.statusTo = statusTo;
		}
		public boolean isMatch(E event) {
			if(eventSet.contains(event)) {
				return true;
			}
			return false;
		}
	}
	
	public S on(S status,E event) {
		List<Rule> ruleList = this.ruleMap.get(status);
		if(ruleList != null && ruleList.size()>0) {
			for(Rule rule : ruleList) {
				if(rule.isMatch(event)) {
					return rule.statusTo;
				} else {
					continue ;
				}
			}
		}
		throw new RuntimeException("状态"+status+"与事件"+event+"不匹配");
	}
	
	public boolean isValidStartNode(S status) {
		if(startNodes.contains(status)) {
			return true;
		}
		return false;
	}
}


使用实例:

public class Order {
	private String orderId;
	private BigDecimal buyAmount;
	private Integer status;
	public static enum MyStatus {
		WAIT_PAY,
		PAYED,
		SEND_OUT,
		BUY_SUCCESS,
		CLOSE;//订单关闭
		
		public static MyStatus valueOf(Integer v) {
			for(MyStatus status : MyStatus.values()) {
				if(status.ordinal() == v) {
					return status;
				}
			}
			return null;
		}
	}
	public static enum MyEvent {
		PAY_OUTOF_TIME,
		PAY_SUCCESS,
		SEND_OUT_SUCCESS,
		BUY_SUCCESS,
		REFUND, //退货
		REJECTED//拒收
	}
	
	private static StateMachine<MyStatus, MyEvent> stateMachine = new StateMachine<>();
	static {
		stateMachine.when(MyStatus.WAIT_PAY).occur(MyEvent.PAY_SUCCESS).then(MyStatus.PAYED)
		.when(MyStatus.WAIT_PAY).occur(MyEvent.PAY_OUTOF_TIME).then(MyStatus.CLOSE)
		.when(MyStatus.PAYED).occur(MyEvent.PAY_SUCCESS).then(MyStatus.PAYED)
		.when(MyStatus.PAYED).occur(MyEvent.SEND_OUT_SUCCESS).then(MyStatus.SEND_OUT)
		.when(MyStatus.SEND_OUT).occur(MyEvent.REFUND,MyEvent.REJECTED).then(MyStatus.CLOSE);
	}
	
	public Order() {
		this.status = MyStatus.WAIT_PAY.ordinal();
	}
	
	public void paySuccess() {
		this.status = stateMachine.on(MyStatus.valueOf(status), MyEvent.PAY_SUCCESS).ordinal();
	}
	public void sendOut() {
		this.status = stateMachine.on(MyStatus.valueOf(status), MyEvent.SEND_OUT_SUCCESS).ordinal();
	}
	public void refund() {
		this.status = stateMachine.on(MyStatus.valueOf(status), MyEvent.REFUND).ordinal();
	}
	
	public static void main(String[] args) {
		Order order = new Order();
		order.paySuccess();
		order.sendOut();
		order.refund();
		System.out.println(MyStatus.valueOf(order.status));
	}
}

分享到:
评论

相关推荐

    VHDL简单状态机实现交通灯系统

    简单状态机实现交通灯系统

    C# 状态机实现

    在C#中,我们可以利用面向对象的特性来实现状态机,以管理对象在不同状态之间的转换。本篇将深入探讨如何在C#中实现状态机,并结合具体的案例——JustTest,来展示其应用。 首先,理解状态机的基本概念。状态机是一...

    用状态机实现任意编码计数器

    本文将详细讲解如何使用状态机来设计一个任意编码的计数器,特别是实现7进制计数器,其序列是0, 2, 5, 3, 4, 6, 1。 首先,我们需要理解状态机的基本概念。状态机是一种模型,它根据当前状态和输入来决定下一个状态...

    用状态机实现ADC0809的采样控制电路

    在电子设计自动化(EDA)领域,使用状态机来实现ADC(模数转换器)的控制电路是一种常见的设计方法。在本实验中,我们关注的是ADC0809,这是一个8位CMOS A/D转换器,它能处理8路不同的模拟输入,并将其转化为数字...

    linux下状态机实现

    总之,Linux下的状态机实现结合了状态机模型和多线程技术,提供了一种高效且灵活的程序设计方法。通过学习这个项目,你可以提升在系统编程和并发处理方面的能力,这对于任何IT专业人员来说都是非常宝贵的经验。

    STM32F103 HAL状态机实现按键消抖,处理按键单击,双击,三击,长按事件,开启定时器中断处理

    STM32F103 HAL状态机实现按键消抖,处理按键单击,双击,三击,长按事件,开启定时器中断处理

    Qt状态机实现动画

    本教程将深入探讨如何利用Qt的状态机实现动画效果。 首先,让我们了解什么是Qt状态机。Qt状态机是基于Qt的QStateMachine类实现的,它允许开发者通过定义一系列的状态和转换来描述一个对象或系统的生命周期。状态机...

    Monado引擎开发:Monado角色动画与状态机-(10).状态机优化技巧.docxMonado引擎开发:Monado角色动画与状态机-(11).案例分析:游戏中的角色动画与状态机实现.docx

    案例分析:游戏中的角色动画与状态机实现.docx Monado引擎开发:Monado角色动画与状态机_(12).实践项目:创建一个完整的角色动画状态机系统.docx Monado引擎开发:Monado角色动画与状态机_(1).Monado引擎基础...

    verilog实现的“状态机实现AD574数模转换”.zip

    本项目"verilog实现的“状态机实现AD574数模转换”.zip"着重展示了如何用Verilog设计一个状态机来实现AD574数模转换器(DAC)。下面我们将深入探讨Verilog编程、状态机设计以及AD574数模转换器的工作原理。 1. ...

    RS232转串口通信状态机实现

    在RS232转串口通信的状态机实现中,主要关注以下几点: 1. **初始化**:设置初始状态,如关闭接收器,清空接收缓冲区等。 2. **事件处理**:根据接收到的信号(如时钟脉冲、数据线电平变化等)触发状态转移。 3. **...

    简单状态机实现,简单仿真验证

    在本案例中,“简单状态机实现,简单仿真验证”是一个关于如何构建和验证基本状态机的教学实例。我们将探讨状态机的基本概念,以及如何在MATLAB环境中通过function.m文件和untitled.slx模型文件进行实现和仿真。 ...

    实验三:状态机实现序列检测器设计.doc

    三、状态机实现序列检测器设计 在本实验中,我们将使用有限状态机来检测输入的串行数据是否是特定的序列“11100101”。我们将设计一个状态机,通过检测输入的串行数据来确定是否是特定的序列。 四、实验原理 本次...

    电子-实验3状态机实现实验3按键输入修改.rar

    在这个实验中,“电子-实验3状态机实现实验3按键输入修改.rar”是一个针对STM32系列微控制器(包括STM32-F0、F1和F2)进行的实践教学项目,旨在让学生理解并掌握如何用状态机处理按键输入。 状态机,全称为有限状态...

    使用自动转换状态机实现的xml解析程序

    ### 使用自动转换状态机实现的XML解析程序 #### 概述 本文介绍了一种使用状态机来解析XML文件的方法。状态机是一种广泛应用于各种场景下的有限状态自动机,它可以非常高效地处理各种输入序列,特别是在解析语言或者...

    VHDL状态机实现AD采集

    在本课题“VHDL状态机实现AD采集”中,我们将深入探讨如何使用VHDL设计一个状态机来完成ADC(Analog-to-Digital Converter)数据的采集任务。 首先,了解ADC的基本概念是至关重要的。ADC是一种将模拟信号转换为数字...

    单片机键盘扫描程序状态机实现

    总结,通过状态机实现的单片机键盘扫描程序能够有效地管理和控制键盘扫描过程,确保系统的稳定性和响应性。理解并掌握这一实现方式对于单片机编程和嵌入式系统设计至关重要。通过阅读《单片机键盘扫描之状态机实现....

    利用verilog状态机实现按键防抖动.docx

    Verilog状态机实现按键防抖动设计 在数字电路设计中,按键防抖动是指在按键输入信号中去除抖动噪声的过程,以确保按键输入信号的可靠性和稳定性。Verilog是是一种硬件描述语言,常用于数字电路设计、仿真和验证。...

    基于EDA的用状态机实现序列检测器的设计

    ### 基于EDA的用状态机实现序列检测器的设计 #### 实验目的 通过本实验,学习如何使用状态机来实现序列检测器的设计,并掌握对其的仿真与硬件测试方法。 #### 实验仪器 - PC机,操作系统为Windows 2000/XP。 - ...

    I_2C总线接口模块的有限状态机实现

    ### I_2C总线接口模块的有限状态机实现 #### 一、引言 I_2C(Inter-Integrated Circuit)总线是由NXP半导体(原Philips半导体)开发的一种简单、高效的双向二线制串行通信总线。它以其简单易用、支持设备种类多、...

    用状态机实现 ADC 控制电路

    a)以约 100KSPS 的采样率,连续对直流电压进行 AD 转换,将串行结果转换成并行, 显示在数码管上,测量三个以上...c)实现单次 AD 转换:每按一次键,自动产生CS和一组时钟完成一次转换,将转换结 果显示在数码管上。

Global site tag (gtag.js) - Google Analytics