python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python访问者模式

Python使用方法重载实现访问者模式

作者:三叔木卯

这篇文章主要为大家详细介绍了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)的;访问者的接口类的方法还依赖了具体类(比如DogCat类),这违背了依赖倒转原则(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的方法只有一个,就是visitAnimalVisitor只负责一个职责,那就是访问动物,符合单一职责原则;添加其他种动物的时候,不用修改抽象类,符合开闭原则AnimalVisitor现在只依赖Animal这个抽象类,符合依赖倒转原则

有人可能担心,如果访问者抽象类没有把访问动物类的相应方法都列出来,会导致具体访问者类漏实现一些方法重载。这个问题在上面的代码中考虑到了,在singledispatchmethod装饰的visit方法里使用NotImplementedError异常进行防御,如果某个具体动物类没有重载visit方法,将抛出异常。

到此这篇关于Python使用方法重载实现访问者模式的文章就介绍到这了,更多相关Python访问者模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文