状态模式 处理订单状态_将状态机模式实现为流处理器

状态模式 处理订单状态

在我的上一个博客中,我说过我真的以为某些“四人行”(GOF)模式已经过时了,如果不是过时的话肯定不受欢迎。 特别是我说过StateMachine没什么用,因为您通常会想到另一种更简单的方式来执行您正在执行的事情,而不是使用它。 为了进行修正,无论是为了宣讲过时的内容还是我在上一个博客末尾附加的丑陋的“ C”代码,我都认为我将演示使用StateMachine将Twitter推文转换为HTML。

这个场景只是一次,不是虚构的或牵强的,而是前几天要做的事情。 在这种情况下,我有一个应用程序刚刚为经过身份验证的Twitter用户下载了一系列时间轴推文。 解析了XML(或JSON)并掌握了我需要格式化以进行显示的推文。 问题在于它们是纯文本格式,我需要将它们转换为HTML,并添加锚标记以生成类似于twitter在Twitter主页上格式化相同内容时所执行的操作的方式。

仅供参考,可以使用Twitter API通过以下URL检索用户Tweets:

<a href="https://api.twitter.com/1/statuses/user_timeline.xml?include_entities=true&include_rts=true&screen_name=BentleyMotors&count=2" target="new">https://api.twitter.com/1/statuses/user_timeline.xml?include_entities=true&include_rts=true&screen_name=BentleyMotors&count=2</a>

…在这种情况下,用户名是“ BentleyMotors”。 如果您在URL中指定XML格式,则会在文本标签中返回一条tweet,其外观如下所示:

Deputy PM Nick Clegg visits #Bentley today to tour Manufacturing facilities. #RegionalGrowthFund http://t.co/kX81aZmY http://t.co/Eet31cCA

……这需要转换成如下形式:

Deputy PM Nick Clegg visits <a href=\"https://twitter.com/#!/search/%23Bentley\">#Bentley</a> today to tour Manufacturing facilities. 
<a href=\"https://twitter.com/#!/search/%23RegionalGrowthFund\">#RegionalGrowthFund</a> 
<a href=\"http://t.co/kX81aZmY\">t.co/kX81aZmY</a> 
<a href=\"http://t.co/Eet31cCA\">t.co/Eet31cCA</a>

解决此问题1的一个好主意是使用状态机 ,该状态机一次读取一个输入流,以查找主题标签,用户名和URL,并将其转换为HTML锚标签。 例如,从#Bentley上方的完整推文中

变成

<a href=\"https://twitter.com/#!/search/%23Bentley\"> #Bentley </a>

http://t.co/Eet31cCA

变成

<a href=\"http://t.co/Eet31cCA\"> t.co/Eet31cCA </a>

这意味着代码必须找到每个以“#”或“ @”开头的单词或以“ http://”开头的URL。

该状态机的URL图如下所示:

此实现与以下GOF图表的不同之处在于,对于该应用程序,我已将状态与事件/操作分开。 这具有改善去耦的好处,并且动作可以与多个状态相关联。

聚集你的状态

构建任何状态机时,要做的第一件事就是将您的状态收集在一起。 在最初的GOF模式中,状态是抽象类。 但是,为了简化起见,我更喜欢使用更多的现代枚举。 该状态机的状态为:

public enum TweetState {OFF("Off - not yet running"), //RUNNING("Running - happily processing any old byte bytes"), //READY("Ready - found a space, so there's maybe soemthing to do, but that depends upon the next byte"), //HASHTAG("#HashTag has been found - process it"), //NAMETAG("@Name has been found - process it"), //HTTPCHECK("Checking for a URL starting with http://"), //URL("http:// has been found so capture the rest of the URL");private final String description;TweetState(String description) {this.description = description;}@Overridepublic String toString() {return "TweetState: " + description;}}

读取字节

接下来需要的是一个类,它一次读取一个输入流一个字节,获取与机器当前状态相关联的动作类,并使用该动作处理该字节。 这是通过StateMachine类完成的,如下所示:

public class StateMachine<T extends Enum<?>> {private final byte[] inputBuffer = new byte[32768];private T currentState;private final Map<T, AbstractAction<T>> stateActionMap = new HashMap<T, AbstractAction<T>>();public StateMachine(T startState) {this.currentState = startState;}/*** Main method that loops around and processes the input stream*/public void processStream(InputStream in) {// Outer loop - continually refill the buffer until there's nothing// left to readtry {processBuffers(in);terminate();} catch (Exception ioe) {throw new StateMachineException("Error processing input stream: "+ ioe.getMessage(), ioe);}}private void processBuffers(InputStream in) throws Exception {for (int len = in.read(inputBuffer); (len != -1); len = in.read(inputBuffer)) {// Inner loop - process the contents of the Bufferfor (int i = 0; i < len; i++) {processByte(inputBuffer[i]);}}}/*** Deal with each individual byte in the buffer*/private void processByte(byte b) throws Exception {// Get the set of actions associated with this stateAbstractAction<T> action = stateActionMap.get(currentState);// do the action, get the next statecurrentState = action.processByte(b, currentState);}/*** The buffer is empty. Make sue that we tidy up*/private void terminate() throws Exception {AbstractAction<T> action = stateActionMap.get(currentState);action.terminate(currentState);}/*** Add an action to the machine and associated state to the machine. A state* can have more than one action associated with it*/public void addAction(T state, AbstractAction<T> action) {stateActionMap.put(state, action);}/*** Remove an action from the state machine*/public void removeAction(AbstractAction<T> action) {stateActionMap.remove(action); // Remove the action - if it's there}}

这里的关键方法是processByte(...)

/*** Deal with each individual byte in the buffer*/private void processByte(byte b) throws Exception {// Get the set of actions associated with this stateAbstractAction<T> action = stateActionMap.get(currentState);// do the action, get the next statecurrentState = action.processByte(b, currentState);}

对于每个字节,此方法将从stateActionMap获取与当前状态关联的动作。 然后调用该动作并执行以更新当前状态,以准备下一个字节。

整理好状态和状态机之后,下一步就是编写操作。 在这一点上,我通过创建一个AbstractAction类来更紧密地遵循GOF模式,该类使用以下方法处理每个事件:

public abstract T processByte(byte b, T currentState) throws Exception;

给定当前状态,此方法将处理一个信息字节,并使用该字节返回下一个状态。 AbstractAction的完整实现是:

public abstract class AbstractAction<T extends Enum<?>> {/*** This is the next action to take - See the Chain of Responsibility Pattern*/protected final AbstractAction<T> nextAction;/** Output Stream we're using */protected final OutputStream os;/** The output buffer */protected final byte[] buff = new byte[1];public AbstractAction(OutputStream os) {this(null, os);}public AbstractAction(AbstractAction<T> nextAction, OutputStream os) {this.os = os;this.nextAction = nextAction;}/*** Call the next action in the chain of responsibility* * @param b* The byte to process* @param state* The current state of the machine.*/protected void callNext(byte b, T state) throws Exception {if (nextAction != null) {nextAction.processByte(b, state);}}/*** Process a byte using this action* * @param b* The byte to process* @param currentState* The current state of the state machine* * @return The next state*/public abstract T processByte(byte b, T currentState) throws Exception;/*** Override this to ensure an action tides up after itself and returns to a* default state. This may involve processing any data that's been captured* * This method is called when the input stream terminates*/public void terminate(T currentState) throws Exception {// blank}protected void writeByte(byte b) throws IOException {buff[0] = b; // Write the data to the output directoryos.write(buff);}protected void writeByte(char b) throws IOException {writeByte((byte) b);}}

构建状态机

到目前为止,我编写的所有代码都是通用的,可以一次又一次地重复使用2 ,所有这些都意味着下一步就是编写一些特定于域的代码。 从上面的UML图表中,您可以看到特定于域的操作是: DefaultActionReadyActionCaptureTags 。 在继续描述它们的作用之前,您可能已经猜到我需要将某些动作注入StateMachine并将它们与TweetState关联。 下面的JUnit代码显示了此操作的完成方式…

StateMachine<TweetState> machine = new StateMachine<TweetState>(TweetState.OFF);// Add some actions to the statemachine// Add the default actionmachine.addAction(TweetState.OFF, new DefaultAction(bos));machine.addAction(TweetState.RUNNING, new DefaultAction(bos));machine.addAction(TweetState.READY, new ReadyAction(bos));machine.addAction(TweetState.HASHTAG, new CaptureTag(bos, new HashTagStrategy()));machine.addAction(TweetState.NAMETAG, new CaptureTag(bos, new UserNameStrategy()));machine.addAction(TweetState.HTTPCHECK, new CheckHttpAction(bos));machine.addAction(TweetState.URL, new CaptureTag(bos, new UrlStrategy()));

从上面的代码中,您可以看到DefaultAction被链接为OFFRUNNING状态, ReadyAction被链接为READY状态, CaptureTag操作被链接到HASHTAG,NAMETAGURL状态,并且HttpCheckAction被链接到HTTPCHECK状态。

您可能已经注意到, CaptureTag操作链接到一个以上的状态。 这很好,因为CaptureTag采用了Strategy模式来即时更改其行为。 因此,我使用一些通用代码执行一个操作,在注入一个策略对象之后,它可以完成三件事。

写作动作

回到编写动作,首先要编写的动作通常是DefaultAction ,这是在没有任何有趣事情发生时调用的动作。 这个动作愉快地获取输入字符并将它们放入输出流,同时寻找某些字符或字符/状态组合。 DefaultAction的核心是processByte(...)方法中的switch语句。

public class DefaultAction extends AbstractAction<TweetState> {public DefaultAction(OutputStream os) {super(os);}/*** Process a byte using this action* * @param b* The byte to process* @param currentState* The current state of the state machine*/@Overridepublic TweetState processByte(byte b, TweetState currentState) throws Exception {TweetState retVal = TweetState.RUNNING;// Switch state if a ' ' charif (isSpace(b)) {retVal = TweetState.READY;writeByte(b);} else if (isHashAtStart(b, currentState)) {retVal = TweetState.HASHTAG;} else if (isNameAtStart(b, currentState)) {retVal = TweetState.NAMETAG;} else if (isUrlAtStart(b, currentState)) {retVal = TweetState.HTTPCHECK;} else {writeByte(b);}return retVal;}private boolean isSpace(byte b) {return b == ' ';}private boolean isHashAtStart(byte b, TweetState currentState) {return (currentState == TweetState.OFF) && (b == '#');}private boolean isNameAtStart(byte b, TweetState currentState) {return (currentState == TweetState.OFF) && (b == '@');}private boolean isUrlAtStart(byte b, TweetState currentState) {return (currentState == TweetState.OFF) && (b == 'h');}}

从上面的代码中,您可以看到中央switch语句正在检查每个字节。 如果该字节是一个空格,则下一个字节可能是一个特殊字符:“#”代表井号标签,“ @”代表名称标签,“ h”代表URL; 因此,如果找到空间,则DefaultAction将返回READY状态,因为可能还有更多工作要做。 如果找不到空格,则它将返回RUNNING状态,该状态告诉StateMachine在读取下一个字节时调用DefaultAction

DefaultAction还会在一行的开头检查特殊字符,作为推文的第一个字符,例如“#”,“ @”或“ h”。

现在,控制已传递回StateMachine对象,该对象从输入流中读取下一个字节。 由于状态现在为READY ,因此对processByte(...)的下一次调用将检索ReadyAction

@Overridepublic TweetState processByte(byte b, TweetState currentState) throws Exception {TweetState retVal = TweetState.RUNNING;switch (b) {case '#':retVal = TweetState.HASHTAG;break;case '@':retVal = TweetState.NAMETAG;break;case 'h':retVal = TweetState.HTTPCHECK;break;default:super.writeByte(b);break;}return retVal;}

ReadyActionswitch语句中,您可以看到它的责任是通过分别检查'#','@'和'h'来确认代码已找到井号,名称或URL。 如果找到一个,则返回以下状态之一: HASHTAGNAMETAGHTTPCHECKStateMachine

假设ReadyAction找到了一个'#'字符并返回了HASHTAG状态,则StateMachine在读取下一个字节时,将从stateActionMap中 插入带有注入的HashTagStrategy类的CaptureTag类。

public class CaptureTag extends AbstractAction<TweetState> {private final ByteArrayOutputStream tagStream;private final byte[] buf;private final OutputStrategy output;private boolean terminating;public CaptureTag(OutputStream os, OutputStrategy output) {super(os);tagStream = new ByteArrayOutputStream();buf = new byte[1];this.output = output;}/*** Process a byte using this action* @param b* The byte to process* @param currentState* The current state of the state machine*/@Overridepublic TweetState processByte(byte b, TweetState currentState)throws Exception {TweetState retVal = currentState;if (b == ' ') {retVal = TweetState.READY; // fix 1output.build(tagStream.toString(), os);if (!terminating) {super.writeByte(' ');}reset();} else {buf[0] = b;tagStream.write(buf);}return retVal;}/*** Reset the object ready for processing*/public void reset() {terminating = false;tagStream.reset();}@Overridepublic void terminate(TweetState state) throws Exception {terminating = true;processByte((byte) ' ', state);}}

CaptureTag代码背后的想法是,它捕获字符并将其添加到ByteArrayOutputStream中,直到检测到空格或输入缓冲区为空。 检测到空间时, CaptureTag调用其OutputStrategy接口,在这种情况下,该接口由HashTagStrategy实现。

public class HashTagStrategy implements OutputStrategy {/*** @see state_machine.tweettohtml.OutputStrategy#build(java.lang.String,* java.io.OutputStream)*/@Overridepublic void build(String tag, OutputStream os) throws IOException {String url = "<a href=\"https://twitter.com/#!/search/%23" + tag + "\">#" + tag + "</a>";os.write(url.getBytes());}
}

HashTagStrategy构建一个标签搜索URL,并将其写入输出流。 将URL写入流后, CaptureTag返回READY状态-检测到空格并将控制权返回给StateMachine

StateMachine读取下一个字节,因此该过程继续。

处理主题标签只是该代码可以处理的几种可能方案中的一种,在演示此方案时,我试图演示如何使用状态机一次处理一个字节的输入流,以实现一些预定义的解决方案。 。 如果您对其他场景的处理方式感兴趣,请查看github上的源代码。

综上所述

总而言之,这不是您想定期使用的技术。 它很复杂,很难实现并且容易出错,而且通常有一种更简单的方法来解析传入的数据。 但是,有用的时候却很少,尽管它很复杂,但却是一个好的解决方案,所以我建议将其保存在隐喻工具箱中,以备不时之需。

1解决这个难题的方法有多种,其中有些可能比State Machine更简单,更简单

2此版本的StateMachine于2006年编写,用于处理XML。 在这种情况下,代码必须解压缩一些Base 64 XML字段,并且由于该模式可重用,因此我只是从我的代码示例工具箱中将其挖掘出来,以用于Tweet to HTML的情况。

3完整项目可在github上找到 ……

参考 Captain Debug的Blog博客上的JCG合作伙伴 Roger Hughes提供了将状态机模式实现为流处理器 的信息 。


翻译自: https://www.javacodegeeks.com/2012/05/implementing-state-machine-pattern-as.html

状态模式 处理订单状态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/356103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 连续签到天数_最大连续签到天数-sql

SELECT MIN(rq) as 起始日期, MAX(rq) as 终止日期, MAX(id1) - MIN(id1) 1 as 持续天数,id3 as 累计签到天数,nameFROM (SELECT datediff(rq,2020-02-01 )id1, (SELECT COUNT(1)FROM tmptableWHERE rq < a.rq andtype 是) id2,(SELECT COUNT(1)FROM tmptableWHEREtype 是…

mysql 两个查询结果合并去重_《MySQL 入门教程》第 21 篇 集合操作符

文章来源&#xff1a;https://blog.csdn.net/horses/article/details/108174837来源平台&#xff1a;CSDN原文作者&#xff1a;不剪发的Tony老师数据表与集合理论中的集合非常类似&#xff0c;表是由行组成的集合。SQL 标准定义了基于行的各种集合操作&#xff1a;并集运算(UNI…

binutils工具集之---ar

1.如果要将多个.o文件生成一个库文件&#xff0c;则存在两种类型的库&#xff0c;一种是静态库&#xff0c;在linux里面后缀是.a&#xff0c;另一种是动态库&#xff0c;后缀为.so。 当可执行程序要与静态库进行链接时&#xff0c;所用到的库中的函数和数据会被拷贝到最终的可执…

jax-rs jax-ws_Google App Engine JAX-RS REST服务

jax-rs jax-ws在本文中&#xff0c;您将学习如何使用JAX-RS参考实现&#xff08;Jersey&#xff09;创建REST服务并将其部署在Google AppEngine上。 先决条件 对于本教程&#xff0c;您将需要&#xff1a; 一个Google AppEngine帐户 Eclipse Galileo&#xff08;3.5.x&#xf…

[转]使用C#开发ActiveX控件

前言 ActiveX控件以前也叫做OLE控件&#xff0c;它是微软IE支持的一种软件组件或对象&#xff0c;可以将其插入到Web页面中&#xff0c;实现在浏览器端执行动态程序功能&#xff0c;以增强浏览器端的动态处理能力。通常ActiveX控件都是用C或VB语言开发&#xff0c;本文介绍另一…

用Java测试多线程代码

测试多线程代码是一个艰巨的挑战。 尝试测试并发性时获得的第一个建议是尽可能地在代码中隔离并发问题。 这是一般的设计建议&#xff0c;但在这种情况下甚至更重要。 确保首先正确地对并发构造所包装的逻辑进行单元测试。 否则&#xff0c;您可能会花费很长时间尝试找出一个并…

php pdo mysql query_PHP+MYSQL中使用PDO的query方法

一 代码class"php">PDO连接MySQL数据库IDPDO数据库时间$dbmsmysql; //数据库类型 ,对于开发者来说&#xff0c;使用不同的数据库&#xff0c;只要改这个&#xff0c;不用记住那么多的函数$hostlocalhost; //数据库主机名$dbNamedb_database15; //使用的数据库$use…

java 冒号 正则表达式_Java正则表达式问号冒号的使用

在Java和Javascript中正则表达式字符串前面加上?:表示非捕获型匹配&#xff0c;否则就是捕获型匹配。捕获型括号会将匹配到的内容捕获到一些变量里&#xff0c;这些变量按照捕获型括号的左括号为顺序从1开始编号。为了避免括号太多使编号混乱&#xff0c;也为了避免无用的捕获…

Hibernate中的一对多XML映射

一对多关系指出一个实体的单个实例与另一个实体的多个实例相关联。 换句话说&#xff0c;一个表中的每个记录与另一个表中的多个记录相关联。 让我们看看如何通过XML映射文件在Hibernate中定义这种关系。 1.实体关系图 假设我们已经在数据库中创建了学生表和部门表&#xff0…

camel eip_Apache Camel教程– EIP,路由,组件,测试和其他概念的简介

camel eip公司之间的数据交换增加了很多。 必须集成的应用程序数量也增加了。 这些接口使用不同的技术&#xff0c;协议和数据格式。 但是&#xff0c;这些应用程序的集成应以标准化的方式建模&#xff0c;有效实现并由自动测试支持。 企业集成模式&#xff08;EIP&#xff09;…

JavaOne和OOW 2015总结

大家好&#xff01; 终于&#xff0c;我回来了一个很棒的JavaOne和OOW2015。在这篇文章中&#xff0c;我想分享我的经验&#xff0c;一些照片和我参加的演讲的摘要。 会议前 我于2015年6月24日星期六乘Copa航空公司CLO-PTY-SFO飞往旧金山。 从哥伦比亚出发&#xff08;大约8小…

Marin说PCB之 PCB封装和原理图封装的藕断丝连--续集(2)

最近天气越来越冷了&#xff0c;小编我在上海漂泊的十多年了&#xff0c;感觉今年似乎是最冷的一年啊。家里的秋裤都不管用了&#xff0c;要换成大棉裤和军大衣啊。而且现在羽绒服大部分都很贵&#xff0c;动不动上千元了&#xff0c;都赶得上小编我几个月的私房钱了都&#xf…

调整线程池的重要性

无论您是否知道&#xff0c;您的Java Web应用程序很可能都使用线程池来处理传入的请求。 这是许多人忽略的实现细节&#xff0c;但是迟早您需要了解如何使用该池以及如何为您的应用程序正确调整池。 本文旨在说明线程模型&#xff0c;什么是线程池以及正确配置线程池所需执行的…

在线原理图绘制网站推荐

如今专业EDA软件已经基本在硬件公司普及并正版化&#xff0c;优秀的EDA工具包括 Cadence公司的OrCAD、Allegro软件&#xff0c;Mentor Graphics的PADS&#xff0c; Altium公司的Altium Designer等等&#xff0c;但是它们在功能异常强大的同时也在一些时候显得非常复杂&#xff…

java map与set的区别_java 集合(list,set,map)三者之间的关系和区别

原java 集合(list&#xff0c;set&#xff0c;map)三者之间的关系和区别一&#xff1a;先上一张关系图&#xff0c;让大家看的更明白。备注&#xff1a;其中红色部分为实现&#xff0c;其他地方均为接口。二&#xff1a;各自的特点。List 有序,可重复ArrayList优点: 底层数据结…

adf时间作用域_ADF任务流:页面片段的托管bean作用域

adf时间作用域介绍 当我们使用ADF任务流并需要实现一些特定于流的业务逻辑或存储一些与流相关的信息时&#xff0c;我们通常使用pageFlowScope托管bean。 而且&#xff0c;当我们需要为流的活动&#xff08;页面或页面片段&#xff09;提供服务时&#xff0c;我们会为此类托管b…

IMA文件如何打开,winimage使用方

一般先用UltraISO打开一个系统的镜像文件&#xff08;.iso&#xff09;。其中有些文件&#xff08;尤其是.ima,img&#xff09;比如下面雨林木风Ghost系统盘的这个IMA文件&#xff0c;我们先提取到桌面 用WinImage打开这个文件即可发现这个IMA文件整合了很多东西。所以&#x…

Java的几何布朗运动

Wiener过程是连续时间随机过程&#xff0c;以纪念Norbert Wiener命名。 通常用于用随机成分表示噪音或财务状况。 可以计算几何布朗运动以可视化某些界限&#xff08;以分位数表示&#xff09;以暗示绝对范围。 为了进行计算&#xff0c;需要以下参数&#xff1a; &#xff0…

mongodb java id 查询数据_java 用 _id 查找 MongoDB 下的数据

找网上的资料看了下增删改查&#xff0c;等日后补上。已经实现了数据的插入&#xff0c;现在想通过 _id属性来查找数据。一开始看到 类似 55b321df715cc162076eb466 这么一长串的内容觉得是string类型。但是发现并不能搜索到结果&#xff0c;在网上搜到了解决方案&#xff1a;S…

java maven部署_eclipse中maven项目部署到tomcat

下面就一一介绍这几种部署方式&#xff1a;1.打war包到tomcat/webapps目录这种方式其实跟非maven项目没什么区别&#xff0c;就是打包的方式不同之后在target目录下会生成war包&#xff0c;复制到tomcat/webapps目录即完成部署。2.使用tomcat-maven插件&#xff0c;在pom.xml的…