访问者模式python实现

访问者模式

介绍

它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

应用场景:

  • 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  • 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

代码实现

访问者模式

你要处理由大量不同类型的对象组成的复杂数据结构,每一个对象都需要进行不同的处理。 比如,遍历一个树形结构,然后根据每个节点的相应状态执行不同的操作。

这里遇到的问题在编程领域中是很普遍的,有时候会构建一个由大量不同对象组成的数据结构。 假设你要写一个表示数学表达式的程序,那么你可能需要定义如下的类:

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
class Node:
pass

class UnaryOperator(Node):
def __init__(self, operand):
self.operand = operand

class BinaryOperator(Node):
def __init__(self, left, right):
self.left = left
self.right = right

class Add(BinaryOperator):
pass

class Sub(BinaryOperator):
pass

class Mul(BinaryOperator):
pass

class Div(BinaryOperator):
pass

class Negate(UnaryOperator):
pass

class Number(Node):
def __init__(self, value):
self.value = value

然后利用这些类构建嵌套数据结构

1
2
3
4
5
# Representation of 1 + 2 * (3 - 4) / 5
t1 = Sub(Number(3), Number(4))
t2 = Mul(Number(2), t1)
t3 = Div(t2, Number(5))
t4 = Add(Number(1), t3)

这样做的问题是对于每个表达式,每次都要重新定义一遍,有没有一种更通用的方式让它支持所有的数字和操作符呢。 这里我们使用访问者模式可以达到这样的目的:

1
2
3
4
5
6
7
8
9
10
class NodeVisitor:
def visit(self, node):
methname = 'visit_' + type(node).__name__
meth = getattr(self, methname, None)
if meth is None:
meth = self.generic_visit
return meth(node)

def generic_visit(self, node):
raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))

为了使用这个类,可以定义一个类继承它并且实现各种visit_Name() 方法,其中Name是node类型。 例如,如果你想求表达式的值,可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Evaluator(NodeVisitor):
def visit_Number(self, node):
return node.value

def visit_Add(self, node):
return self.visit(node.left) + self.visit(node.right)

def visit_Sub(self, node):
return self.visit(node.left) - self.visit(node.right)

def visit_Mul(self, node):
return self.visit(node.left) * self.visit(node.right)

def visit_Div(self, node):
return self.visit(node.left) / self.visit(node.right)

def visit_Negate(self, node):
return -node.operand

# e = Evaluator()
# e.visit(t4) # 0.6

作为一个不同的例子,下面定义一个类在一个栈上面将一个表达式转换成多个操作序列:

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
class StackCode(NodeVisitor):
def generate_code(self, node):
self.instructions = []
self.visit(node)
return self.instructions

def visit_Number(self, node):
self.instructions.append(('PUSH', node.value))

def binop(self, node, instruction):
self.visit(node.left)
self.visit(node.right)
self.instructions.append((instruction,))

def visit_Add(self, node):
self.binop(node, 'ADD')

def visit_Sub(self, node):
self.binop(node, 'SUB')

def visit_Mul(self, node):
self.binop(node, 'MUL')

def visit_Div(self, node):
self.binop(node, 'DIV')

def unaryop(self, node, instruction):
self.visit(node.operand)
self.instructions.append((instruction,))

def visit_Negate(self, node):
self.unaryop(node, 'NEG')

# s = StackCode()
# s.generate_code(t4)
# [('PUSH', 1), ('PUSH', 2), ('PUSH', 3), ('PUSH', 4), ('SUB',),
('MUL',), ('PUSH', 5), ('DIV',), ('ADD',)]