Python使用方法重载实现访问者模式
Python上的访问者模式,看了一下网上其他人的例子,一般都是类似下面的代码。
from abc import ABC, abstractmethod # 抽象访问者 class AnimalVisitor(ABC): @abstractmethod def visit_dog(self, dog: "Dog"): pass @abstractmethod def visit_cat(self, cat: "Cat"): pass # 抽象动物类 class Animal(ABC): def __init__(self, name: str): self.name = name @abstractmethod def accept(self, visitor: AnimalVisitor): pass # 具体动物类 class Dog(Animal): def accept(self, visitor): visitor.visit_dog(self) class Cat(Animal): def accept(self, visitor): visitor.visit_cat(self) # 具体访问者:喂食访问者 class FeedingVisitor(AnimalVisitor): def visit_dog(self, dog): print(f"给{dog.name}喂狗粮") def visit_cat(self, cat): print(f"给{cat.name}喂猫粮") # 具体访问者:检查健康访问者 class HealthCheckVisitor(AnimalVisitor): def visit_dog(self, dog): print(f"检查{dog.name}的疫苗接种情况") def visit_cat(self, cat): print(f"检查{cat.name}是否需要洗澡")
使用示例如下:
def main(): # 创建动物 dog = Dog("旺财") cat = Cat("咪咪") # 创建访问者 feeding_visitor = FeedingVisitor() health_visitor = HealthCheckVisitor() # 创建动物列表 animals = [dog, cat] # 执行不同的操作 print("=== 喂食时间 ===") for animal in animals: animal.accept(feeding_visitor) print("\n=== 健康检查 ===") for animal in animals: animal.accept(health_visitor) if __name__ == "__main__": main()
以上实现的访问者模式,访问者的接口类(抽象类)一般通过定义不同的方法(带visit前缀的方法)来对不同的被访问者进行访问。有些奇怪,为什么不通过方法重载的方式来实现访问者模式呢?
通过定义不同方法来实现访问者模式,明显违背一些设计原则的。访问者的接口类需要负责访问不同方法,而这些方法之间相关性不大(删除某个方法对其他方法没有影响),这明显违背了单一职责原则(SRP)
;如果要添加一种动物(被访问者),那么就要修改访问者接口类以添加相应的方法,这是违背开闭原则(OCP)
的;访问者的接口类的方法还依赖了具体类(比如Dog
、Cat
类),这违背了依赖倒转原则(DIP)
。
使用方法重载实现的访问者模式,则没有上面的问题,而且代码也更简单明了。Python没有传统的方法重载方式,不过在functools
模块里有个singledispatchmethod
单分派装饰器,这里可以借用它来实现“方法重载”。
使用方法重载的方式代码如下:
from abc import ABC, abstractmethod from functools import singledispatchmethod # 抽象访问者 class AnimalVisitor(ABC): @abstractmethod def visit(self, animal: "Animal"): pass # 抽象动物类 class Animal(ABC): def __init__(self, name: str): self.name = name def accept(self, visitor: AnimalVisitor): visitor.visit(self) # 具体动物类 class Dog(Animal): ... class Cat(Animal): ... # 具体访问者:喂食访问者 class FeedingVisitor(AnimalVisitor): @singledispatchmethod def visit(self, animal: Animal): raise NotImplementedError(f"{type(animal)} 未重载 visit方法") @visit.register(Dog) def _(self, dog): print(f"给{dog.name}喂狗粮") @visit.register(Cat) def _(self, cat): print(f"给{cat.name}喂猫粮") # 具体访问者:检查健康访问者 class HealthCheckVisitor(AnimalVisitor): @singledispatchmethod def visit(self, animal: Animal): raise NotImplementedError(f"{type(animal)} 未重载 visit方法") @visit.register(Dog) def _(self, dog): print(f"检查{dog.name}的疫苗接种情况") @visit.register(Cat) def _(self, cat): print(f"检查{cat.name}是否需要洗澡")
可以看到,这一种方式访问者抽象类AnimalVisitor
的方法只有一个,就是visit
。AnimalVisitor
只负责一个职责,那就是访问动物,符合单一职责原则
;添加其他种动物的时候,不用修改抽象类,符合开闭原则
;AnimalVisitor
现在只依赖Animal
这个抽象类,符合依赖倒转原则
。
有人可能担心,如果访问者抽象类没有把访问动物类的相应方法都列出来,会导致具体访问者类漏实现一些方法重载。这个问题在上面的代码中考虑到了,在singledispatchmethod
装饰的visit
方法里使用NotImplementedError
异常进行防御,如果某个具体动物类没有重载visit
方法,将抛出异常。
到此这篇关于Python使用方法重载实现访问者模式的文章就介绍到这了,更多相关Python访问者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Python+Pillow+Pytesseract实现验证码识别
这篇文章主要为大家详细介绍了如何利用pillow和pytesseract来实现验证码的识别,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下2022-05-05Python PyQt5中弹出子窗口解决子窗口一闪而过的问题
这篇文章主要介绍了Python PyQt5中弹出子窗口解决子窗口一闪而过的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2021-03-03
最新评论