java状态模式 应用场景_java设计模式之状态模式

本文介绍如何使用状态模式简化HTML解析器的设计与实现。通过定义不同的状态类,可以根据HTML的不同部分触发相应的行为,使得代码更易于理解和扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义:允许对象在内部状态改变时改变它的行为,对象看起来就好像修改了它的类

简单来说就是将状态的行为封装到各个状态类内部,在内部状态改变时,通过委托不同的状态类改变对象的行为,从外部看起来就像是重新初始化了一个类

下面举一个例子来看

之前看过一个html解析的处理,核心处理过程如下

switch(state) {//we are in an unknown state before there's actual content

caseUNKNOWN:if (character == '

saveState(TEXT);

state=TAG_ENCOUNTERED;

}break;//we can encounter any content

caseTEXT:if (character == '

flush();

saveState(state);

state=TAG_ENCOUNTERED;

}else if (character == '&') {

saveState(state);

entity.setLength(0);

state=ENTITY;

}else if (Character.isWhitespace((char) character) && character != 12288) { //全角空格在浏览器中会被解释成汉字,不会被识别成分隔符,按照实际空格数显示

if(nowhite)

text.append((char) character);

nowhite= false;

}else{

text.append((char) character);

nowhite= true;

}break;//we have just seen a < and are wondering what we are looking at//, , , etc.

caseTAG_ENCOUNTERED:

initTag();if (character == '/') {

state=IN_CLOSETAG;

}else if (character == '?') {

restoreState();

state=PI;

}else{

text.append((char) character);

state=EXAMIN_TAG;

}break;//we are processing something like this .//It could still be a or something.

caseEXAMIN_TAG:if (character == '>') {

doTag();

processTag(true);

initTag();

state=restoreState();

}else if (character == '/') {

state=SINGLE_TAG;

}else if (character == '-' && text.toString().equals("!-")) {

flush();

state=COMMENT;

}else if (character == '[' && text.toString().equals("![CDATA")) {

flush();

state=CDATA;

}else if (character == 'E' && text.toString().equals("!DOCTYP")) {

flush();

state=PI;

}else if (Character.isWhitespace((char) character)) {

doTag();

state=TAG_EXAMINED;

}else{

text.append((char) character);

}break;

}

上面只截取其中一段,后面还有很多case,看着就不美观,而且不易扩展

下面使用状态模式看看能不能实现类似的html解析逻辑

对于一段简单的html,像"

test
"这样的html字符串,我们需要解析出标签内部的数据,可以在遍历字符串中的每个字符的时候设置不同的状态,并且触发不同状态的行为

首先需要定义一个State接口,里面有开始标签、结束标签、关闭标签和设置Context这几种方法,然后定义4种状态实现(NoState, CloseTagState, EndTagState, StartTagState)

NoState一般是还未解析html之前的状态,它的下一个状态只能是开始标签状态, 开始标签状态的下一个状态只能是关闭标签状态, 关闭标签状态的下一个状态是结束标签状态

图示:

15f3e876ab48181357d9a4d3f8cd1fe5.png

类结构图如图所示:

9bcfb997d7c2e9af1e6a5d55543e0923.png

Context类:

public classSimpleXmlParserContext {public static State startTagState = newStartTagState();public static State closeTagState = newCloseTagState();public static State noTagState = newNoState();public static State endTagState = newEndTagState();private State state =noTagState;private StringBuffer tempString = newStringBuffer();publicStringBuffer getTempString() {returntempString;

}public void flush(booleanflush){if(flush){

System.out.print(tempString.toString()+"\n");

}

tempString.setLength(0);

}publicState getState() {returnstate;

}public voidsetState(State state) {this.state =state;this.state.setContext(this);

}public voidstartTag() {this.state.startTag();

}public voidendTag() {this.state.endTag();

}public voidcloseTag() {this.state.closeTag();

}public static voidmain(String [] args){

String html= "

<123>
";

SimpleXmlParserContext context= newSimpleXmlParserContext();

context.setState(noTagState);for(int a = 0; a < html.length(); a++){char c =html.charAt(a);if(String.valueOf(c).equals("

context.startTag();

}else if(String.valueOf(c).equals("/")){

context.endTag();

}else if(String.valueOf(c).equals(">")){

context.closeTag();

}else{

context.getTempString().append(c);

}continue;

}

context.flush(true);

}

}

State:

public interfaceState {voidstartTag();voidendTag();voidcloseTag();voidsetContext(SimpleXmlParserContext context);

}

AbstractState:

public abstract class AbstractState implementsState {protectedSimpleXmlParserContext context;public voidsetContext(SimpleXmlParserContext context) {this.context =context;

}public voidendTag(){

}

}

StartTagState:

public class StartTagState extendsAbstractState {

@Overridepublic voidstartTag() {//开启标签

System.out.print("开始标签\n");

}public voidendTag(){this.context.setState(SimpleXmlParserContext.endTagState);this.context.endTag();

}

@Overridepublic voidcloseTag() {this.context.setState(SimpleXmlParserContext.closeTagState);this.context.closeTag();

}

}

NoState:

public class NoState extendsAbstractState {

@Overridepublic voidstartTag() {this.context.setState(SimpleXmlParserContext.startTagState);this.context.startTag();

}

@Overridepublic voidcloseTag() {

}

}

EndTagState:

public class EndTagState extendsAbstractState {

@Overridepublic voidstartTag() {

}public voidendTag(){this.context.flush(true);

}

@Overridepublic voidcloseTag() {this.context.setState(SimpleXmlParserContext.closeTagState);this.context.closeTag();

}

}

CloseTagState:

public class CloseTagState extendsAbstractState {private static final List TAGS = new ArrayList();static{

TAGS.add("div");

TAGS.add("p");

}

@Overridepublic voidstartTag() {this.context.setState(SimpleXmlParserContext.startTagState);this.context.startTag();

}public voidendTag(){this.context.setState(SimpleXmlParserContext.endTagState);this.context.endTag();

}

@Overridepublic voidcloseTag() {

System.out.print("结束标签\n");//判断下标签是否合规范

if(!TAGS.contains(this.context.getTempString().toString())){this.context.flush(true);

}else{this.context.flush(false);

}

}

}

执行main可以获取想要得到的解析结果

82737f4ee2b163b6d858befff522d1ce.png

这样的话可以避免过多的switch case语句,而且也方便扩展,如果想要添加其他状态,添加一个状态实现子类就可以

比如想要过滤掉html字符串中标签是a的内部数据,如果按照之前的写法的话,需要添加一个case ATAG:  并且修改其他和改状态相关的case内部代码

使用状态模式的话可以直接添加一个ATagState子类,

因为只有在closeTag的时候才可以知道具体是什么标签,也就是说CloseTagState的下一个状态也有可能是ATagState

状态流程图改变如下

d2d4bfdef1a3da8907d38e9b1cf79d9e.png

public class ATagState extendsAbstractState {public voidendTag(){this.context.flush(false);

}public voidcloseTag(){this.context.setState(SimpleXmlParserContext.closeTagState);this.context.closeTag();

}

}

修改ClosetTagState类的closeTag方法

public voidcloseTag() {

System.out.print("结束标签\n");

//a标签的时候切换到ATagStateif(this.context.getTempString().toString().equals("a")){this.context.setState(SimpleXmlParserContext.aTagState);this.context.flush(false);return;

}//判断下标签是否合规范

if(!TAGS.contains(this.context.getTempString().toString())){this.context.flush(true);

}else{this.context.flush(false);

}

}

使用"123123"测试,结果如下:

fde3828728a053d000ed67c9bbebaef2.png

总结:

状态模式主要的应用场景有:

一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。

一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。

在使用状态模式前需要先理清各个状态之间的关系,制作状态流程图,然后在各个状态内部实现各自的行为

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值