概念解析
访问模式,指的是封装一些作用于某种数据结构中各个元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。该模式主要存在三个角色:
- 访问者(Visitor):负责对数据节点进行访问和操作
- 数据节点(DataNode):被操作的数据对象
- 对象结构(ObjectStructure):数据结构的管理类,也是数据对象的容器
访问模式类图如下:
优点:
- 数据和操作分离,降低了耦合度,职责清晰
- 增加新的访问操作很方便,符合开闭原则,且不需要更改数据结构
缺点:
- 元素类添加很困难,元素类的添加会影响访问者类的具体操作,不符合开闭原则
- 一定程度上破坏了数据对象的封装,因为访问者需要直接访问/调用元素,意味着元素需要暴露一些内部操作和内部状态。
适用场景:
- 对象结构中包含的对象少,相对比较固定,少改变,但是经常基于该数据结构定义新的操作
- 每个类型的数据需要提供特定的访问操作,操作类型多且不相关
- 不想让对象操作污染对象类,需要分离对象类和对象的访问类
设计模板
访问模式的框架模型如下:
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
from abc import ABCMeta, abstractmethod
class DataNode(metaclass=ABCMeta):
"""数据结构类"""
def accept(self, visitor):
"""接收访问者的访问"""
visitor.visit(self)
class Visitor(metaclass=ABCMeta):
"""访问者"""
def visit(self, data):
"""对数据对象的访问操作"""
pass
class ObjectStructure():
"""数据结构管理类"""
def __init__(self):
self.__datas = []
def add(self, data_element):
self.__datas.append(data_element)
def action(self, visitor):
"""进行数据的访问操作"""
for data in self.__datas:
data.accept(visitor)
实例分析
场景分析:宠物店存在猫狗等宠物,需要对猫狗的数量/体重/年龄/性别等进行统计操作:
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
119
120
121
122
123
124
125
126
127
128
129
130
131
class Animal(DataNode):
"""动物类"""
def __init__(self, name, is_male, age, weight):
self.__name = name
self.__is_male = is_male
self.__age = age
self.__weight = weight
def get_name(self):
return self.__name
def ismale(self):
return self.__is_male
def get_age(self):
return self.__age
def get_weight(self):
return self.__weight
class Cat(Animal):
"""猫"""
def speak(self):
print("miao~")
class Dog(Animal):
"""狗"""
def speak(self):
print("wang~")
class GenderCounter(self):
"""性别统计"""
def __init__(self):
self.__male_cat = 0
self.__female_cat = 0
self.__male_dog = 0
self.__female_dog = 0
def visit(self, data):
if isinstance(data, Cat):
if data.ismale():
self.__male_cat += 1
else:
self.__female_cat += 1
elif isinstance(data, Dog):
if data.ismale():
self.__male_dog += 1
else:
self.__female_dog += 1
else:
print("Not support this type!")
def get_info(self):
print("%d只雄猫,%d只雌猫,%d雄狗,%d只雌狗。"
% (self.__male_cat, self.__female_cat, self.__male_dog, self.__female_dog))
class WeightCounter(self):
"""体重统计"""
def __init__(self):
self.__cat_num = 0
self.__cat_weight = 0
self.__dog_num = 0
self.__dog_weight = 0
def visit(self, data):
if isinstance(data, Cat):
self.__cat_num += 1
self.__cat_weight += data.get_weight()
elif isinstance(data, Dog):
self.__dog_num += 1
self.__dog_weight += data.get_weight()
else:
print("Not support this type!")
def get_info(self):
print("猫的平均体重:%d,狗的平均体重:%d。"
% (self.__cat_weight / self.__cat_num, self.__dog_weight / self.__dog_num))
class AgeCounter(self):
"""年龄统计"""
def __init__(self):
self.__cat_max_age = 0
self.__dog_max_age = 0
def visit(self, data):
if isinstance(data, Cat):
if self.__cat_max_age < data.get_age():
self.__cat_max_age = data.get_age()
elif isinstance(data, Dog):
if self.__dog_max_age < data.get_age():
self.__dog_max_age = data.get_age()
else:
print("Not support this type!")
def get_info(self):
print("猫的最大年龄:%d,狗的最大年龄:%d。"
% (self.__cat_max_age, self.__dog_max_age))
def test_animal():
animals = ObjectStructure()
animals.add(Cat("Cat1", True, 1, 5))
animals.add(Cat("Cat2", False, 0.5, 3))
animals.add(Cat("Cat3", False, 1.2, 4.2))
animals.add(Dog("Dog1", True, 0.5, 8))
animals.add(Dog("Dog2", True, 3, 52))
animals.add(Dog("Dog3", False, 1, 21))
animals.add(Dog("Dog4", False, 2, 25))
gender_counter = GenderCounter()
# 进行性别统计
animals.action(gender_counter)
gender_counter.get_info()
# 进行重量统计
weight_counter = WeightCounter()
animals.action(weight_counter)
weight_counter.get_info()
# 进行年龄统计
age_counter = AgeCounter()
animals.action(age_counter)
age_counter.get_info()
"""
测试结果:
1只雄猫,2只雌猫,2只雄狗,2只雌狗
猫的平均体重是:4.07kg,狗的平均体重是:26.50kg
猫的最大年龄:1.2,狗的最大年龄是:3
"""