企业网站搭建流程/360搜索引擎下载
状态(state)模式在实现上类似于策略模式,但是它们的目标非常的不同。状态模式呈现的是一个状态转换系统:系统中的对象处在一个特定的状态当中,经过某些操作之后可以抵达另外的状态。
为了实现这一目标,需要一个状态管理器manager,或者一个上下文context来提供交换状态的接口。在实现上,这个类需要包含一个指向状态的指针,每个状态需要知道下一个状态是什么,并且在触发之后达到哪种状态。
状态设计模式主要包含状态管理器以及多种状态两中设计类。管理器维护当前状态,推动操作然后抵达目标状态。而状态类相对于调用者来说是个黑箱,需要内部进行自管理。
一个状态模式的例子
为了展示状态模式设计,我们构建一个XML的分析工具。上下文管理器就是分析工具本身。参数是文本输入,寻找指定的值然后进入不同的状态进行操作,最终目标是创建每个标签(Tag)节点的对象树。为了能够完成任务,我们需要对XML的Tag进行分析。为了简化,我们不处理Tag的属性,以及Tag嵌套在Tag内容中的情形,下面是一个用于分析的XML文件:
Kelvin Gao
Packet Publishing
Python3 Programming1
Object Oriented Design2
Objects In Python我们先来看一下程序的输出是什么,目标是输出一个节点对象树,节点到底什么样呢?我们需要Tag的名字,一个指向父节点的指针,按顺序排列的子节点列表,注意某些节点有文本内容,而有些没有。下面是设计的节点类:
class Node:
def __init__(self, tag_name, parent=None):
self.parent = parent
self.tag_name = tag_name
self.children = []
self.text=""
def __str__(self):
if self.text:
return self.tag_name + ": " + self.text
else:
return self.tag_name
现在,我们看一下前面的文本,我们需要定义可以进入什么样的状态。首先,需要一个没有处理过任何节点的状态,我们还需要一个处理打开的和关闭的tags状态,另外需要一个状态用处理具有文本内容的tag。
问题是我们如何知道下一个节点是打开的tag,关闭的tag或者是一个文本节点?这需要在每个状态中增加一点逻辑来表示,但更合理的是创建一个新的状态,仅仅负责指出下一个状态是什么,我们称这种状态转换类叫做ChildNode,最后我们需要以下几种状态:FirstTag
ChildNode
OpenTag
CloseTag
Text
FirstTag状态将会转换到ChildNode,然后决定去往哪一个状态,当其他的状态都完成时,返回ChildNode,状态操作尽可能处理“字符中有什么”然后告诉分析器(上下文管理器)处理“剩余部分”。下面给出分析器类的定义:
class Parser:
def __init__(self, parse_string):
self.parse_string = parse_string
self.root = None
self.current_node = None
self.state = FirstTag()
def process(self, remaining_string):
remaining = self.state.process(remaining_string, self)
if remaining:
self.process(remaining)
def start(self):
self.process(self.parse_string)
root属性代表XML结构的最顶层节点,current_node变量代表子节点。分析器种最重要的部分是process方法,将其传递给当前的状态,处理”剩余的字符串“。分析器自己也会传递给状态的处理方法,这样状态便可以对其进行操作。状态会返回没有处理的字符串,随后分析器递归的调用process方法对剩余的字符串进行操作,最终构建起整个树结构。
下面是FirstTag状态类的实现:
class FirstTag:
def process(self, remaining_string, parser):
i_start_tag = remaining_string.find('
i_end_tag = remaining_string.find('>')
tag_name = remaining_string[i_start_tag+1:i_end_tag]
root = Node(tag_name)
parser.root = parser.current_node = root
parser.state = ChildNode()
return remaining_string[i_end_tag+1:]
注意这里没有进行验证输入的是否为有效的文件,正确的做法应该严格的进行有效性验证,能够在错误中恢复,或者给出描述性错误信息。
这里的process方法提取Tag的名字,赋给分析器的根节点,同时赋给current_node子节点,之后改变分析器的状态进入ChildNode,并返回剩余字符作进一步处理。
下面是中控状态ChildNode的实现:
class ChildNode:
def process(self, remaining_string, parser):
stripped = remaining_string.strip()
if stripped.startswith(""):
parser.state = CloseTag()
elif stripped.startswith("
parser.state = OpenTag()
else:
parser.state = TextNode()
return stripped
strip()方法用以清除whitespace,之后分析器查看是元素是open标签,close标签,或者是一个字符串文本。根据不同的可能性,配置分析器到不同的状态,对字符串进行进一步分析。
OpenTag状态类似于FirstTag状态,除了添加一个新创建的node赋给current_node之外
class OpenTag:
def process(self, remaining_string, parser):
i_start_tag = remaining_string.find('
i_end_tag = remaining_string.find('>')
tag_name = remaining_string[i_start_tag+1:i_end_tag]
node = Node(tag_name, parser.current_node)
parser.current_node.children.append(node)
parser.current_node = node
parser.state = ChildNode()
return remaining_string[i_end_tag+1:]
CloseTag状态则相反,他设置分析器的current_node回到父节点
class CloseTag:
def process(self, remaining_string, parser):
i_start_tag = remaining_string.find('
i_end_tag = remaining_string.find('>')
assert remaining_string[i_start_tag+1] == "/"
tag_name = remaining_string[i_start_tag+2:i_end_tag]
assert tag_name == parser.current_node.tag_name
parser.current_node = parser.current_node.parent
parser.state = ChildNode()
return remaining_string[i_end_tag+1:].strip()
最后,TextNode状态仅仅是提取文本内容,并且赋给当前节点:
class TextNode:
def process(self, remaining_string, parser):
i_start_tag = remaining_string.find('
text = remaining_string[:i_start_tag]
parser.current_node.text = text
parser.state = ChildNode()
return remaining_string[i_start_tag:]
下面我们创建一个主脚本,从命令行打开文件,分析内容,并且输出节点:
if __name__ == "__main__":
import sys
with open(sys.argv[1]) as file:
contents = file.read()
p = Parser(contents)
p.start()
nodes = [p.root]
while nodes:
node = nodes.pop(0)
print(node)
nodes = node.children + nodes
打印所有Tag及子Tag相关信息,输出的内容如下:
book
author: Kelvin Gao
publisher: Packet Publishing
title: Python3 Programming
content
chapter
number: 1
title: Object Oriented Design
chapter
number: 2
title: Objects In Python
状态模式对比策略模式
实际上,我们可以将状态写成first-class函数,而不必包装到类中;在结构上看,状态模式和策略模式两类设计具有类似的结构,但是他们解决完全不同的问题。策略模式在运行时选择一个算法,通常情况下只有一个算法被执行。然而状态模式允许处理过程在不同的状态之间进行切换,这里的主要区别是策略模式不关心彼此的策略,而在状态模式下,上下文管理器需要知道能够切换进的状态是什么。