简介
题外话:这是要写的23种设计模式的最后一种,还没写完的时候,总想着赶紧写赶紧写,现在马上就要写完了,感觉时间过得好快啊,为了写这个系列花了三个月的时间,不过也将这块硬骨头啃下来了,值得记录一下。
访问者模式,被誉为最复杂的设计模式,一般都用不上,刚开始接触的时候,确实是无法理解为什么要这么设计(来回套娃),感觉是为了封装而封装。下面我们来看看这个访问者模式是怎么套娃的吧。
正文
样例demo
import random
class Interviewee:
def __init__(self, name) -> None:
self.name = name
self.academic_record = random.randrange(0,100) # 学业成绩
self.technical_proficiency = random.randrange(0,100) # 技术熟练度
def accept(self, visitor):
pass
class Student(Interviewee):
def accept(self, visitor):
visitor.visit(self)
def get_academic_record(self):
return self.academic_record
class SocialMan(Interviewee):
def accept(self, visitor):
visitor.visit(self)
def get_technical_proficiency(self):
return self.technical_proficiency
class Visitor:
def __init__(self) -> None:
pass
def visit(self, interviewee) -> None:
pass
class OnCampusRecruitment(Visitor):
def visit(self, interviewee) -> None:
print("校招平台")
if isinstance(interviewee, Student):
print("面试者{}是学生,他的学业成绩是:{}".format(interviewee.name,interviewee.get_academic_record()))
elif isinstance(interviewee, SocialMan):
print("{}先生,您的简历投错了,请移步社招平台".format(interviewee.name))
else:
print("您哪位?")
class SocialRecruitment(Visitor):
def visit(self, interviewee) -> None:
print("社招平台")
if isinstance(interviewee, Student):
print("{}同学,您简历投错了,请移步校招平台".format(interviewee.name))
elif isinstance(interviewee, SocialMan):
print("面试者{}是社会人,他的技术熟练度是:{}%".format(interviewee.name,interviewee.get_technical_proficiency()))
else:
print("您哪位?")
class InterviewList:
def __init__(self) -> None:
self.interview_list = []
def add_interview(self,interviewee):
self.interview_list.append(interviewee)
def display(self, visitor):
for interviewee in self.interview_list:
interviewee.accept(visitor)
i = InterviewList()
i.add_interview(SocialMan("李四"))
i.add_interview(Student("小明"))
i.add_interview(Student("小红"))
i.add_interview(Student("小芳"))
i.add_interview(SocialMan("王五"))
i.add_interview(Student("小兰"))
i.add_interview(SocialMan("张三"))
i.display(SocialRecruitment())
i.display(OnCampusRecruitment())
样例解析:
例子中我们有个应聘者基类,这个应聘者基类有两个子类,一个是学生,一个是社会人。每个应聘者都有属于自己的学业成绩和技能熟练度。
有个面试平台基类,这个面试平台基类也有两个子类,一个是校招平台,一个是社招平台。每个面试平台关注的点不一样。校招平台只关注学生的学业成绩,社招平台只关注社会人的技能熟练度。
现在我们有个应聘者中心,专门收集应聘者资料的,并且将应聘者资料投放到不同的面试平台。
面试流程如下:
应聘者投递简历到应聘中心,应聘中心将所有应聘者投放到指定面试平台,应聘者接受面试平台访问(accept(self,visitor))并将自己的资料传给面试平台查看(visit(self))。面试平台收到应聘者资料后,根据自己的需求访问相应的属性,并作出反馈。
上述流程主要有两个关键点:
- 应聘者接受面试平台访问accept(self,visitor)
- 面试者接受应聘者资料visit(self)
总结
访问者模式的优点 :
- 切换访问者比较方便,每次只需要指定不同的访问者即可
访问者模式的缺点:
- 新增被访者比较麻烦,可能需要改变访问者设定的访问方式
- 修改被访者属性和方法,会对访问者产生影响,导致修改所有访问者方式
下面贴一小段百度来的总结:访问者模式一篇就够了 - 简书
-
访问者模式的优点。
- 各角色职责分离,符合单一职责原则
通过UML类图和上面的示例可以看出来,Visitor、ConcreteVisitor、Element 、ObjectStructure,职责单一,各司其责。 - 具有优秀的扩展性
如果需要增加新的访问者,增加实现类 ConcreteVisitor 就可以快速扩展。 - 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
员工属性(数据结构)和CEO、CTO访问者(数据操作)的解耦。 - 灵活性
- 各角色职责分离,符合单一职责原则
-
访问者模式的缺点。
- 具体元素对访问者公布细节,违反了迪米特原则
CEO、CTO需要调用具体员工的方法。 - 具体元素变更时导致修改成本大
变更员工属性时,多个访问者都要修改。 - 违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有以来抽象
访问者 visit 方法中,依赖了具体员工的具体方法。
- 具体元素对访问者公布细节,违反了迪米特原则