源码地址:https://github.com/weilanhanf/PythonDesignPatterns
说明:
访问者模式的基本想法是,软件系统中拥有一个由许多对象构成的、比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接受访问者对象的访问。访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理。在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施 accept 方法,在每一个元素的 accept 方法中会调用访问者的 visit 方法,从而使访问者得以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。
访问者模式定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
结构:
抽象访问者,具体访问者,抽象元素,具体元素,对象结构。
实例:
假设一个药房,有一些大夫,一个药品划价员和一个药房管理员,它们通过一个药房管理系统组织工作流程。大夫开出药方后,药品划价员确定药品是否正常,价格是否正确;通过后药房管理员进行开药处理。该系统可以如何实现?最简单的想法,是分别用一个一个if…else…把划价员处理流程和药房管理流程实现,这样做的问题在于,扩展性不强,而且单一性不强,一旦有新药的加入或者划价流程、开药流程有些变动,会牵扯比较多的改动。
#构造药品类和工作人员类 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("CHARGE: %s lists the Medicine %s. Price:%s " % (self.name,medicine.getName(),medicine.getPrice())) class Pharmacy(Visitor): def visit(self,medicine): print("PHARMACY:%s offers the Medicine %s. Price:%s" % (self.name,medicine.getName(),medicine.getPrice())) """ 在药品类中,有一个accept方法,其参数是个visitor; 而工作人员就是从Visitor类中继承而来的, 也就是说,他们就是Visitor,都包含一个visit方法,其参数又恰是medicine。 药品作为处理元素,依次允许(Accept)Visitor对其进行操作, 这就好比是一条流水线上的一个个工人,对产品进行一次次的加工。 整个业务流程还差一步,即药方类的构建(流水线大机器) """ 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) #药方类将待处理药品进行整理,并组织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)
打印结果:
CHARGE: Doctor Strange lists the Medicine Yinqiao Pill. Price:2.0
CHARGE: Doctor Strange lists the Medicine Penicillin. Price:3.0
PHARMACY:Doctor Wei offers the Medicine Yinqiao Pill. Price:2.0
PHARMACY:Doctor Wei offers the Medicine Penicillin. Price:3.0
优点:
符合单一指责,凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。扩展性良好,元素类可以通过接受不同的访问者来实现对不同操作的扩展。
缺点:
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。破坏封装。当采用访问者模式的时候,就会打破组合类的封装。
使用场景:
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。