1. 什么是访问者模式?
访问者模式是一种行为设计模式,它允许你在不改变对象结构的前提下,定义新的操作。通过将操作封装在访问者对象中,访问者模式使得你可以在不修改元素类的情况下,向元素类添加新的功能。
访问者模式的核心思想是将操作与对象结构分离。通过引入一个访问者对象,允许你在不修改对象结构的情况下,定义新的操作。这样可以提高系统的灵活性和可扩展性。
访问者模式通常包含以下几个组成部分:
- 访问者接口(Visitor Interface):定义访问者的接口,通常包含对每个元素类的访问方法。
- 具体访问者(Concrete Visitor):实现访问者接口,定义对每个元素类的具体操作。
- 元素接口(Element Interface):定义接受访问者的接口,通常包含一个接受访问者的方法。
- 具体元素(Concrete Element):实现元素接口,定义具体的元素类。
访问者模式在软件设计中具有多种优点:
- 分离操作与对象结构:通过将操作封装在访问者中,访问者模式使得操作与对象结构分离,增强了系统的灵活性。
- 易于扩展:可以通过添加新的访问者来扩展系统的功能,而不需要修改现有的元素类。
- 集中操作:所有操作都集中在访问者中,便于管理和维护。
python
# 访问者接口
class DiscountVisitor:
def visit_book(self, book):
pass
def visit_electronic(self, electronic):
pass
# 具体访问者
class PercentageDiscount(DiscountVisitor):
def visit_book(self, book):
return book.price * 0.9 # 书籍享受10%的折扣
def visit_electronic(self, electronic):
return electronic.price * 0.85 # 电子产品享受15%的折扣
# 元素接口
class Item:
def accept(self, visitor):
pass
# 具体元素
class Book(Item):
def __init__(self, price):
self.price = price
def accept(self, visitor):
return visitor.visit_book(self) # 接受访问者
class Electronic(Item):
def __init__(self, price):
self.price = price
def accept(self, visitor):
return visitor.visit_electronic(self) # 接受访问者
# 客户端代码
if __name__ == "__main__":
items = [Book(100), Electronic(200)] # 创建购物车中的商品
discount_visitor = PercentageDiscount() # 创建折扣访问者
for item in items:
discounted_price = item.accept(discount_visitor) # 计算折扣后的价格
print(f"Discounted price: {discounted_price}")
- 访问者接口 :
DiscountVisitor
类定义了访问者的接口,包含对每个元素类的访问方法(visit_book
和visit_electronic
)。 - 具体访问者 :
PercentageDiscount
类实现了访问者接口,定义了对每个元素类的具体操作(计算折扣后的价格)。- 在
visit_book
方法中,书籍享受10%的折扣;在visit_electronic
方法中,电子产品享受15%的折扣。
- 元素接口 :
Item
类定义了接受访问者的接口,包含一个accept
方法。 - 具体元素 :
Book
和Electronic
类实现了元素接口,定义具体的商品类,并实现accept
方法,接受访问者。
- 客户端代码 :
- 在客户端代码中,创建购物车中的商品和折扣访问者,并通过调用
accept
方法来计算折扣后的价格。
- 在客户端代码中,创建购物车中的商品和折扣访问者,并通过调用
2. 示例1:宠物店中的访问者模式
- 访问者接口(Visitor Interface):定义访问者的接口,通常包含对每个宠物类的访问方法。
- 具体访问者(Concrete Visitor):实现访问者接口,定义对每个宠物类的具体操作(如计算数量、平均体重和最大年龄)。
- 元素接口(Element Interface):定义接受访问者的接口,通常包含一个接受访问者的方法。
- 具体元素(Concrete Element):实现元素接口,定义具体的宠物类(如猫和狗)。
python
# 访问者接口
class PetVisitor:
def visit_cat(self, cat):
pass
def visit_dog(self, dog):
pass
# 具体访问者:数量统计
class CountVisitor(PetVisitor):
def __init__(self):
self.cat_count = {'male': 0, 'female': 0}
self.dog_count = {'male': 0, 'female': 0}
def visit_cat(self, cat):
self.cat_count[cat.gender] += 1
def visit_dog(self, dog):
self.dog_count[dog.gender] += 1
def report(self):
print(f"Cat Count: {self.cat_count['male']} males, {self.cat_count['female']} females")
print(f"Dog Count: {self.dog_count['male']} males, {self.dog_count['female']} females")
# 具体访问者:平均体重计算
class WeightVisitor(PetVisitor):
def __init__(self):
self.total_cat_weight = 0
self.total_dog_weight = 0
self.cat_count = 0
self.dog_count = 0
def visit_cat(self, cat):
self.total_cat_weight += cat.weight
self.cat_count += 1
def visit_dog(self, dog):
self.total_dog_weight += dog.weight
self.dog_count += 1
def report(self):
avg_cat_weight = self.total_cat_weight / self.cat_count if self.cat_count > 0 else 0
avg_dog_weight = self.total_dog_weight / self.dog_count if self.dog_count > 0 else 0
print(f"Average Cat Weight: {avg_cat_weight:.2f} kg")
print(f"Average Dog Weight: {avg_dog_weight:.2f} kg")
# 具体访问者:最大年龄查找
class AgeVisitor(PetVisitor):
def __init__(self):
self.max_cat_age = 0
self.max_dog_age = 0
def visit_cat(self, cat):
if cat.age > self.max_cat_age:
self.max_cat_age = cat.age
def visit_dog(self, dog):
if dog.age > self.max_dog_age:
self.max_dog_age = dog.age
def report(self):
print(f"Oldest Cat Age: {self.max_cat_age} years")
print(f"Oldest Dog Age: {self.max_dog_age} years")
# 具体元素:猫
class Cat:
def __init__(self, gender, weight, age):
self.gender = gender # 'male' or 'female'
self.weight = weight # 体重
self.age = age # 年龄
def accept(self, visitor):
visitor.visit_cat(self) # 接受访问者
# 具体元素:狗
class Dog:
def __init__(self, gender, weight, age):
self.gender = gender # 'male' or 'female'
self.weight = weight # 体重
self.age = age # 年龄
def accept(self, visitor):
visitor.visit_dog(self) # 接受访问者
# 客户端代码
if __name__ == "__main__":
pets = [
Cat('male', 4.5, 3),
Cat('female', 3.0, 2),
Dog('male', 10.0, 5),
Dog('female', 8.5, 4),
Cat('male', 5.0, 6),
Dog('female', 9.0, 7)
]
count_visitor = CountVisitor() # 创建数量统计访问者
weight_visitor = WeightVisitor() # 创建平均体重计算访问者
age_visitor = AgeVisitor() # 创建最大年龄查找访问者
# 统计数量、计算平均体重和查找最大年龄
for pet in pets:
pet.accept(count_visitor) # 统计数量
pet.accept(weight_visitor) # 计算平均体重
pet.accept(age_visitor) # 查找最大年龄
# 输出统计结果
count_visitor.report()
weight_visitor.report()
age_visitor.report()
python
Cat Count: 2 males, 1 females
Dog Count: 1 males, 2 females
Average Cat Weight: 4.17 kg
Average Dog Weight: 9.17 kg
Oldest Cat Age: 6 years
Oldest Dog Age: 7 years
-
访问者接口 :
PetVisitor
类定义了访问者的接口,包含对每个宠物类的访问方法(visit_cat
和visit_dog
)。 -
具体访问者:
CountVisitor
类实现了访问者接口,负责统计宠物的数量(按性别分类)。WeightVisitor
类实现了访问者接口,负责计算宠物的平均体重。AgeVisitor
类实现了访问者接口,负责找出年龄最大的猫和狗。
-
具体元素:
Cat
和Dog
类实现了元素接口,定义具体的宠物类,并实现accept
方法,接受访问者。
-
客户端代码:
- 在客户端代码中,创建宠物对象(猫和狗)并创建数量统计、平均体重计算和最大年龄查找的访问者。
- 通过调用
accept
方法,分别统计数量、计算平均体重和查找最大年龄,并打印结果。