定义
- 封装一些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
- 核心思想在于:可以在不改变数据结构的前提下定义作用于这些元素的新操作。将数据结构和操作(或算法)进行解耦,而且能更方便地拓展新的操作。
访问模式的模型抽象
代码框架
from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法
class DataNode(metaclass=ABCMeta):
"""数据结构类"""
def accept(self, visitor):
"""接受访问者的访问"""
visitor.visit(self)
class Visitor(metaclass=ABCMeta):
"""访问者"""
@abstractmethod
def visit(self, data):
"""对数据对象的访问操作"""
pass
class ObjectStructure:
"""数据结构的管理类,也是数据对象的一个容器,可遍历容器内的所有元素"""
def __init__(self):
self.__datas = []
def add(self, dataElement):
self.__datas.append(dataElement)
def action(self, visitor):
"""进行数据访问的操作"""
for data in self.__datas:
data.accept(visitor)
类图
基于框架的实现
from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法
class DataNode(metaclass=ABCMeta):
"""数据结构类"""
def accept(self, visitor):
"""接受访问者的访问"""
visitor.visit(self)
class Visitor(metaclass=ABCMeta):
"""访问者"""
@abstractmethod
def visit(self, data):
"""对数据对象的访问操作"""
pass
class ObjectStructure:
"""数据结构的管理类,也是数据对象的一个容器,可遍历容器内的所有元素"""
def __init__(self):
self.__datas = []
def add(self, dataElement):
self.__datas.append(dataElement)
def action(self, visitor):
"""进行数据访问的操作"""
for data in self.__datas:
data.accept(visitor)
class Animal(DataNode):
"""动物类"""
def __init__(self, name, isMale, age, weight):
self.__name = name
self.__isMale = isMale
self.__age = age
self.__weight = weight
def getName(self):
return self.__name
def isMale(self):
return self.__isMale
def getAge(self):
return self.__age
def getWeight(self):
return self.__weight
class Cat(Animal):
"""猫"""
def __init__(self, name, isMale, age, weight):
super().__init__(name, isMale, age, weight)
def speak(self):
print("miao~")
class Dog(Animal):
"""狗"""
def __init__(self, name, isMale, age, weight):
super().__init__(name, isMale, age, weight)
def speak(self):
print("wang~")
class GenderCounter(Visitor):
"""性别统计"""
def __init__(self):
self.__maleCat = 0
self.__femaleCat = 0
self.__maleDog = 0
self.__femalDog = 0
def visit(self, data):
if isinstance(data, Cat):
if data.isMale():
self.__maleCat += 1
else:
self.__femaleCat += 1
elif isinstance(data, Dog):
if data.isMale():
self.__maleDog += 1
else:
self.__femalDog += 1
else:
print("Not support this type")
def getInfo(self):
print("%d只雄猫,%d只雌猫,%d只雄狗,%d只雌狗。"
% (self.__maleCat, self.__femaleCat, self.__maleDog, self.__femalDog))
class WeightCounter(Visitor):
"""体重的统计"""
def __init__(self):
self.__catNum = 0
self.__catWeight = 0
self.__dogNum = 0
self.__dogWeight = 0
def visit(self, data):
if isinstance(data, Cat):
self.__catNum += 1
self.__catWeight += data.getWeight()
elif isinstance(data, Dog):
self.__dogNum += 1
self.__dogWeight += data.getWeight()
else:
print("Not support this type")
def getInfo(self):
print("猫的平均体重是:%0.2fkg, 狗的平均体重是:%0.2fkg" %
((self.__catWeight / self.__catNum), (self.__dogWeight / self.__dogNum)))
class AgeCounter(Visitor):
"""年龄统计"""
def __init__(self):
self.__catMaxAge = 0
self.__dogMaxAge = 0
def visit(self, data):
if isinstance(data, Cat):
if self.__catMaxAge < data.getAge():
self.__catMaxAge = data.getAge()
elif isinstance(data, Dog):
if self.__dogMaxAge < data.getAge():
self.__dogMaxAge = data.getAge()
else:
print("Not support this type")
def getInfo(self):
print("猫的最大年龄是:%s,狗的最大年龄是:%s" % (self.__catMaxAge, self.__dogMaxAge))
def testAnimal():
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))
genderCounter = GenderCounter()
animals.action(genderCounter)
genderCounter.getInfo()
print()
weightCounter = WeightCounter()
animals.action(weightCounter)
weightCounter.getInfo()
print()
ageCounter = AgeCounter()
animals.action(ageCounter)
ageCounter.getInfo()
testAnimal()
模型说明
- 设计要点
- 访问模式中主要有三个角色,在设计访问模式时要找到并区分这些角色。
- 访问者(Visitor):负责对数据节点进行访问和操作。
- 数据节点(DataNode):即要被操作的数据对象。
- 对象结构(ObjectStructure):数据结构的管理类,也是数据对象的一个容器,可遍历容器内的所有元素。
- 访问模式的优缺点
- 优点:
- 将数据和操作(算法)分离,降低了耦合度。将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中,类的职责更加清晰。
- 增加新的访问操作很方便。使用访问模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。
- 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。
- 缺点:
- 增加新的元素类很困难。在访问模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭 – 原则”的要求。
- 破坏数据对象的封装性。访问模式要求访问者对象能够访问并调用每一个元素的操作细节,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
应用场景
- 对象结构中包含的对象类型比较少,而且这些类需求比较固定,很少改变,但经常需要在此对象结构上定义新的操作。
- 一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问模式中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。