状态模式 处理订单状态_将状态机模式实现为流处理器
狀態(tài)模式 處理訂單狀態(tài)
在我的上一個博客中,我說過我真的以為某些“四人行”(GOF)模式已經(jīng)過時了,如果不是過時的話肯定不受歡迎。 特別是我說過StateMachine沒什么用,因為您通常會想到另一種更簡單的方式來執(zhí)行您正在執(zhí)行的事情,而不是使用它。 為了進(jìn)行修正,無論是為了宣講過時的內(nèi)容還是我在上一個博客末尾附加的丑陋的“ C”代碼,我都認(rèn)為我將演示使用StateMachine將Twitter推文轉(zhuǎn)換為HTML。這個場景只是一次,不是虛構(gòu)的或牽強的,而是前幾天要做的事情。 在這種情況下,我有一個應(yīng)用程序剛剛為經(jīng)過身份驗證的Twitter用戶下載了一系列時間軸推文。 解析了XML(或JSON)并掌握了我需要格式化以進(jìn)行顯示的推文。 問題在于它們是純文本格式,我需要將它們轉(zhuǎn)換為HTML,并添加錨標(biāo)記以生成類似于twitter在Twitter主頁上格式化相同內(nèi)容時所執(zhí)行的操作的方式。
僅供參考,可以使用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格式,則會在文本標(biāo)簽中返回一條tweet,其外觀如下所示:
Deputy PM Nick Clegg visits #Bentley today to tour Manufacturing facilities. #RegionalGrowthFund http://t.co/kX81aZmY http://t.co/Eet31cCA……這需要轉(zhuǎn)換成如下形式:
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的一個好主意是使用狀態(tài)機 ,該狀態(tài)機一次讀取一個輸入流,以查找主題標(biāo)簽,用戶名和URL,并將其轉(zhuǎn)換為HTML錨標(biāo)簽。 例如,從#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。
該狀態(tài)機的URL圖如下所示:
此實現(xiàn)與以下GOF圖表的不同之處在于,對于該應(yīng)用程序,我已將狀態(tài)與事件/操作分開。 這具有改善去耦的好處,并且動作可以與多個狀態(tài)相關(guān)聯(lián)。
聚集你的狀態(tài)
構(gòu)建任何狀態(tài)機時,要做的第一件事就是將您的狀態(tài)收集在一起。 在最初的GOF模式中,狀態(tài)是抽象類。 但是,為了簡化起見,我更喜歡使用更多的現(xiàn)代枚舉。 該狀態(tài)機的狀態(tài)為:
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;}}讀取字節(jié)
接下來需要的是一個類,它一次讀取一個輸入流一個字節(jié),獲取與機器當(dāng)前狀態(tài)相關(guān)聯(lián)的動作類,并使用該動作處理該字節(jié)。 這是通過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}}這里的關(guān)鍵方法是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);}對于每個字節(jié),此方法將從stateActionMap獲取與當(dāng)前狀態(tài)關(guān)聯(lián)的動作。 然后調(diào)用該動作并執(zhí)行以更新當(dāng)前狀態(tài),以準(zhǔn)備下一個字節(jié)。
整理好狀態(tài)和狀態(tài)機之后,下一步就是編寫操作。 在這一點上,我通過創(chuàng)建一個AbstractAction類來更緊密地遵循GOF模式,該類使用以下方法處理每個事件:
public abstract T processByte(byte b, T currentState) throws Exception;給定當(dāng)前狀態(tài),此方法將處理一個信息字節(jié),并使用該字節(jié)返回下一個狀態(tài)。 AbstractAction的完整實現(xiàn)是:
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);}}構(gòu)建狀態(tài)機
到目前為止,我編寫的所有代碼都是通用的,可以一次又一次地重復(fù)使用2 ,所有這些都意味著下一步就是編寫一些特定于域的代碼。 從上面的UML圖表中,您可以看到特定于域的操作是: DefaultAction , ReadyAction和CaptureTags 。 在繼續(xù)描述它們的作用之前,您可能已經(jīng)猜到我需要將某些動作注入StateMachine并將它們與TweetState關(guān)聯(lián)。 下面的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被鏈接為OFF和RUNNING狀態(tài), ReadyAction被鏈接為READY狀態(tài), CaptureTag操作被鏈接到HASHTAG,NAMETAG和URL狀態(tài),并且HttpCheckAction被鏈接到HTTPCHECK狀態(tài)。
您可能已經(jīng)注意到, CaptureTag操作鏈接到一個以上的狀態(tài)。 這很好,因為CaptureTag采用了Strategy模式來即時更改其行為。 因此,我使用一些通用代碼執(zhí)行一個操作,在注入一個策略對象之后,它可以完成三件事。
寫作動作
回到編寫動作,首先要編寫的動作通常是DefaultAction ,這是在沒有任何有趣事情發(fā)生時調(diào)用的動作。 這個動作愉快地獲取輸入字符并將它們放入輸出流,同時尋找某些字符或字符/狀態(tài)組合。 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語句正在檢查每個字節(jié)。 如果該字節(jié)是一個空格,則下一個字節(jié)可能是一個特殊字符:“#”代表井號標(biāo)簽,“ @”代表名稱標(biāo)簽,“ h”代表URL; 因此,如果找到空間,則DefaultAction將返回READY狀態(tài),因為可能還有更多工作要做。 如果找不到空格,則它將返回RUNNING狀態(tài),該狀態(tài)告訴StateMachine在讀取下一個字節(jié)時調(diào)用DefaultAction 。
DefaultAction還會在一行的開頭檢查特殊字符,作為推文的第一個字符,例如“#”,“ @”或“ h”。
現(xiàn)在,控制已傳遞回StateMachine對象,該對象從輸入流中讀取下一個字節(jié)。 由于狀態(tài)現(xiàn)在為READY ,因此對processByte(...)的下一次調(diào)用將檢索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;}從ReadyAction的switch語句中,您可以看到它的責(zé)任是通過分別檢查'#','@'和'h'來確認(rèn)代碼已找到井號,名稱或URL。 如果找到一個,則返回以下狀態(tài)之一: HASHTAG , NAMETAG或HTTPCHECK到StateMachine
假設(shè)ReadyAction找到了一個'#'字符并返回了HASHTAG狀態(tài),則StateMachine在讀取下一個字節(jié)時,將從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中,直到檢測到空格或輸入緩沖區(qū)為空。 檢測到空間時, CaptureTag調(diào)用其OutputStrategy接口,在這種情況下,該接口由HashTagStrategy實現(xiàn)。
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構(gòu)建一個標(biāo)簽搜索URL,并將其寫入輸出流。 將URL寫入流后, CaptureTag返回READY狀態(tài)-檢測到空格并將控制權(quán)返回給StateMachine 。
StateMachine讀取下一個字節(jié),因此該過程繼續(xù)。
處理主題標(biāo)簽只是該代碼可以處理的幾種可能方案中的一種,在演示此方案時,我試圖演示如何使用狀態(tài)機一次處理一個字節(jié)的輸入流,以實現(xiàn)一些預(yù)定義的解決方案。 。 如果您對其他場景的處理方式感興趣,請查看github上的源代碼。
綜上所述
總而言之,這不是您想定期使用的技術(shù)。 它很復(fù)雜,很難實現(xiàn)并且容易出錯,而且通常有一種更簡單的方法來解析傳入的數(shù)據(jù)。 但是,有用的時候卻很少,盡管它很復(fù)雜,但卻是一個好的解決方案,所以我建議將其保存在隱喻工具箱中,以備不時之需。
1解決這個難題的方法有多種,其中有些可能比State Machine更簡單,更簡單
2此版本的StateMachine于2006年編寫,用于處理XML。 在這種情況下,代碼必須解壓縮一些Base 64 XML字段,并且由于該模式可重用,因此我只是從我的代碼示例工具箱中將其挖掘出來,以用于Tweet to HTML的情況。
3完整項目可在github上找到 ……
參考 : Captain Debug的Blog博客上的JCG合作伙伴 Roger Hughes提供了將狀態(tài)機模式實現(xiàn)為流處理器 的信息 。
翻譯自: https://www.javacodegeeks.com/2012/05/implementing-state-machine-pattern-as.html
狀態(tài)模式 處理訂單狀態(tài)
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的状态模式 处理订单状态_将状态机模式实现为流处理器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 留尼汪岛属于哪个国家(留尼汪岛位于哪个洲
- 下一篇: jax-rs jax-ws_Google