访问者模式的定义如下:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义于作用于这些元素的新操作。
提到访问者模式,就不得不提一下双分派。分派分为静态分派和动态分派。
静态分派即根据请求者的名称和接收到的参数,决定多态时处理的操作。
Python原生是不支持静态分派的,因而也不直接支持更高层次的分派。
访问者模式实现的分派,是一种动态双分派。但这并不妨碍Python通过访问者模式实现一种基于类的“双分派效果”。
双分派顾名思义,即最终的操作决定于两个接收者的类型,
优点:
1、将不同的职责非常明确地分离开来,符合单一职责原则;
2、职责的分开也直接导致扩展非常优良,灵活性非常高,加减元素和访问者都非常容易。
应用场景:
1、要遍历不同的对象,根据对象进行不同的操作的场景;或者一个对象被多个不同对象顺次处理的情况,可以考虑使用访问者模式。除本例外,报表生成器也可以使用访问者模式实现,报表的数据源由多个不同的对象提供,每个对象都是Visitor,报表这个Element顺次Accept各访问者完善并生成对象。
缺点
1、访问者得知了元素细节,与最小隔离原则相悖;
2、元素变更依旧可能引起Visitor的修改。
流程图:
python代码实现:
"""
访问者模式
假设一个药房,有一些大夫,一个药品划价员和一个药房管理员,它们通过一个药房管理系统组织工作流程。
大夫开出药方后,药品划价员确定药品是否正常,价格是否正确;
通过后药房管理员进行开药处理。
"""
# 构造药品类和工作人员类
class Medicine:
name=""
price=0.0
def __init__(self,name,price):
self.name=name
self.price=price
def getName(self):
return self.name
def setName(self,name):
self.name=name
def getPrice(self):
return self.price
def setPrice(self,price):
self.price=price
def accept(self,visitor):
pass
class Antibiotic(Medicine):
def accept(self,visitor):
visitor.visit(self)
class Coldrex(Medicine):
def accept(self,visitor):
visitor.visit(self)
# 抗生素和感冒药
class Visitor:
name=""
def setName(self,name):
self.name=name
def visit(self,medicine):
pass
class Charger(Visitor):
def visit(self,medicine):
print ("收费: %s 列出了药 %s. 价格:%s " \
% (self.name, medicine.getName (), medicine.getPrice ()))
class Pharmacy(Visitor):
def visit(self,medicine):
print ("医生:%s 列出了药 %s. 价格:%s" \
% (self.name, medicine.getName (), medicine.getPrice ()))
# 药方类的构建
class ObjectStructure:
pass
class Prescription(ObjectStructure):
medicines=[]
def addMedicine(self,medicine):
self.medicines.append(medicine)
def rmvMedicine(self,medicine):
self.medicines.append(medicine)
def visit(self,visitor):
for medc in self.medicines:
medc.accept(visitor)
if __name__=="__main__":
yinqiao_pill=Coldrex("Yinqiao Pill",2.0)
penicillin=Antibiotic("Penicillin",3.0)
doctor_prsrp=Prescription()
doctor_prsrp.addMedicine(yinqiao_pill)
doctor_prsrp.addMedicine(penicillin)
charger=Charger()
charger.setName("Doctor Strange")
pharmacy=Pharmacy()
pharmacy.setName("Doctor Wei")
doctor_prsrp.visit(charger)
doctor_prsrp.visit(pharmacy)
原文:https://yq.aliyun.com/articles/71075?spm=a2c4e.11153940.blogcont280715.26.175692aawkXRdB