这两种设计模式非常相似,它们的 UML 图都是完全相同的。策略模式注重于对算法的选择,而状态模式注重对状态的切换,可以理解为状态的切换会改变处理的策略。
这是设计模式系列文章的一部分,点击查看该系列的其他文章。
策略模式
策略模式是一种面向对象编程中的抽象模式。针对同样的问题,它实现了不同的解决方案,你的代码可以在运行的时候自由选择最恰当的方案。
策略模式的一个应用是排序,比如你要实现一个排序算法,它可以自动选择排序方法来对输入的数组进行排序,排序的方法是自动选择的,而结果是不变的。
还有一个例子是设置电脑的墙纸,当你设置墙纸的时候,你的电脑会自动帮你设置很多事情:
- 根据屏幕的分辨率,自动将图片缩放到合适的大小
- 自动处理图片与系统组件之间的缩放、虚化关系
- 图片与背景色的结合
你可以定义不同的对象,它们接受的 input 是一样的(目标图片,屏幕分辨率),不管怎样,这些对象都都能达到设置屏幕壁纸的目的。
有人说我通过 if 判断也可以达到同样的目的,但是这意味着你需要将你的代码放到一个巨大的方法中,随着新的策略的增加,你的函数将变得非常笨重。
这里略过策略模式的实例。
状态模式
状态模式的目的是实现“状态切换”,对象的状态可以被外面知道,并且可能会被一些活动改变。来一个例子,需求是要对一个 xml 文件进行解析,一个简单的 xml 文件如下:
1 2 3
| <body> <title>welcome to scott's blog</title> </body>
|
我们想把这样的文件解析出来,结果是我可以通过 node.attr 的方式访问 xml 文件的内容。比如 xml_file.body 作为一个节点。该节点有一个属性叫子节点,我可以通过 xml_file.body.children 的方式拿到,对于 title 中的内容,我已通过 xml_file.body.title.text 的方式拿到。
首先需要一个节点类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Node: """节点类,记录节点的名字,文本,节点之间有上下级关系,所以它 还需要一个指向父节点的指针。 """ def __init__(self, tagname, parent=None): self.parent=parent self.tagname = tagname self.children = [] self.text = ""
def __str__(self): if self.text: return self.tagname + ':' + self.text
else: return self.tagname
|
有了节点后,我们还需要一个解析器,解析器会一层一层的解析字符串,我们可以定义解析器有几种状态,即处于:
如何根据解析器的状态判断是否还可以深入一步呢?可以这么定义:
- 开始节点
- 子节点
- 打开的节点
- 结束节点
- 文本节点(最底层无子节点的节点)
来看代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| class Parser: """Parser 是一个解析器。它会负责去切换状态,下图的状态之间会互相转换。 第一个状态是 First Tag,它永远将切换至子节点,再由子节点来决定切换到其他哪个状态。 每一个状态都会用自己的方法处理收到的剩余字符,然后将状态再设置为 Children Node,告诉解析器来处理剩下的部分。 +----------------------------------------------------------+ | .-------------. | | ( First Tag ) | | `-------------' | | | | | | | | .------v------. | | +--------->( Children Node )<----------+ | | | `-------------' | | | | ^ | | | v v v | | .-------------. .-------------. .-------------. | | ( Open tag ) ( Closed tag ) ( Text ) | | `-------------' `-------------' `-------------' | | | +----------------------------------------------------------+ """ def __init__(self, parse_str): self.parse_str = parse_str self.root = None self.current_node = None self.state = FirstTag()
def process(self, remain_str): remainning = self.state.process(remain_str, self) if remainning: self.process(remainning)
def start(self): self.process(self.parse_str)
class FirstTag: def process(self, remain_str, parser): """处理剩余的字符。
Args: remain_str (string): 剩余需要解析的字符 parser (Parser): parser 即上面定义的的 Parser,它会被闯进来修改它的属性,如current_node, root, state
Returns: string: 剩余需要解析的字符 """ i_start_tag = remain_str.find('<') i_end_tag = remain_str.find('>') tag_name = remain_str[i_start_tag+1:i_end_tag]
root = Node(tag_name) parser.root = parser.current_node = root parser.state = ChildrenNode()
return remain_str[i_end_tag+1:]
class ChildrenNode: def process(self, remain_str, parser): """同样只需要一个 process 方法来处理字符串。 作为 ChildrenNode,它需要根据字符来判断需要使用什么样的类状态器, 类状态器的 process 方法将会完成对剩余字符的处理。
Args: remain_str ([type]): [description] parser ([type]): [description] """ striped = remain_str.strip() if striped.startswith('<'): parser.state = OpenTag() elif striped.startswith('>'): parser.state = CloseTag() else: parser.state = TextNode()
class OpenTag: def process(self, remain_str, parser): """ """ i_start_tag = remain_str.find('<') i_end_tag = remain_str.find('>') tag_name = remain_str[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 = ChildrenNode()
return remain_str[i_end_tag+1:]
class CloseTag: def process(self, remain_str, parser): i_start_tag = remain_str.find('<') i_end_tag = remain_str.find('>') assert remain_str[i_start_tag+1] == '/' tagname = remain_str[i_end_tag+2:i_end_tag] assert tagname == parser.current_node.tagname parser.current_node = parser.current_node.parent parser.state = ChildrenNode()
return remain_str[i_end_tag+1:].strip()
class TextNode: def process(self, remain_str, parser): i_start_tag = remain_str.find('<') text = remain_str[:i_start_tag] parser.current_node.text = text parser.state = ChildrenNode()
return remain_str[i_start_tag:]
|
启动代码
1 2 3 4 5 6 7 8 9 10
| if __name__ == '__main__': with open("data.xml") as file: p = Parser(file.read()) p.start()
nodes = [p.root] while nodes: node = nodes.pop(0) print(node) nodes = node.children + nodes
|
将会产生下面的输出:
1 2
| body title:welcome to scott's blog
|