2026/1/15 15:37:52
网站建设
项目流程
塔城网站seo,抖音代运营费用明细,企业域名查询,哈尔滨建设网证照查询Python 中的访问者模式#xff08;Visitor Pattern#xff09;
访问者模式是一种行为型设计模式#xff0c;其核心目的是#xff1a; 将算法#xff08;操作#xff09;与对象结构分离#xff0c;让你在不改变对象结构的前提下#xff0c;为该结构中的元素添加新的操作…Python 中的访问者模式Visitor Pattern访问者模式是一种行为型设计模式其核心目的是将算法操作与对象结构分离让你在不改变对象结构的前提下为该结构中的元素添加新的操作。形象比喻就像一个动物园对象结构里有很多动物元素来了不同的“访客”访问者——摄影师会拍照、饲养员会喂食、兽医会检查健康。动物本身不需要改变就能支持不同的新操作。为什么需要访问者模式当你有一个稳定的对象结构例如 AST 抽象语法树、图形元素树、文件系统但需要频繁添加新操作时如果用继承在每个类中添加新方法 → 违反开闭原则如果用条件判断分散在各个类中 → 难以维护访问者模式通过双分派Double Dispatch解决这个问题第一次分派决定访问者第二次分派决定具体元素。典型应用场景编译器对 AST抽象语法树进行不同操作类型检查、代码生成、优化、打印文档转换将结构化文档转为 HTML、PDF、Markdown图形渲染对图形元素树执行绘制、计算面积、序列化等操作报表统计在组织架构树上统计人数、薪资等XML/JSON 处理遍历 DOM 树执行不同操作Python 实现示例图形元素访问者我们实现一个简单的图形编辑器支持圆形和矩形访问者包括绘制、计算面积、导出 XML。fromabcimportABC,abstractmethodfromtypingimportListimportxml.etree.ElementTreeasET# 元素接口ElementclassShape(ABC):abstractmethoddefaccept(self,visitor):pass# 具体元素1圆形classCircle(Shape):def__init__(self,x:float,y:float,radius:float):self.x,self.y,self.radiusx,y,radiusdefaccept(self,visitor):returnvisitor.visit_circle(self)# 具体元素2矩形classRectangle(Shape):def__init__(self,x:float,y:float,width:float,height:float):self.x,self.y,self.width,self.heightx,y,heightdefaccept(self,visitor):returnvisitor.visit_rectangle(self)# 访问者接口VisitorclassShapeVisitor(ABC):abstractmethoddefvisit_circle(self,circle:Circle):passabstractmethoddefvisit_rectangle(self,rectangle:Rectangle):pass# 具体访问者1绘制访问者classDrawVisitor(ShapeVisitor):defvisit_circle(self,circle:Circle):print(f绘制圆形中心({circle.x},{circle.y}), 半径{circle.radius})defvisit_rectangle(self,rectangle:Rectangle):print(f绘制矩形左上角({rectangle.x},{rectangle.y}), f宽{rectangle.width}, 高{rectangle.height})# 具体访问者2面积计算访问者classAreaVisitor(ShapeVisitor):def__init__(self):self.total_area0.0defvisit_circle(self,circle:Circle):importmath areamath.pi*circle.radius**2self.total_areaareaprint(f圆形面积:{area:.2f})defvisit_rectangle(self,rectangle:Rectangle):arearectangle.width*rectangle.height self.total_areaareaprint(f矩形面积:{area:.2f})# 具体访问者3XML 导出访问者classXMLExportVisitor(ShapeVisitor):def__init__(self):self.rootET.Element(shapes)defvisit_circle(self,circle:Circle):elemET.SubElement(self.root,circle)elem.set(x,str(circle.x))elem.set(y,str(circle.y))elem.set(radius,str(circle.radius))defvisit_rectangle(self,rectangle:Rectangle):elemET.SubElement(self.root,rectangle)elem.set(x,str(rectangle.x))elem.set(y,str(rectangle.y))elem.set(width,str(rectangle.width))elem.set(height,str(rectangle.height))defget_xml(self)-str:returnET.tostring(self.root,encodingunicode)# 对象结构画布可以包含多个图形classCanvas:def__init__(self):self.shapes:List[Shape][]defadd(self,shape:Shape):self.shapes.append(shape)defaccept(self,visitor:ShapeVisitor):forshapeinself.shapes:shape.accept(visitor)# 客户端使用if__name____main__:canvasCanvas()canvas.add(Circle(10,20,5))canvas.add(Rectangle(30,40,15,10))canvas.add(Circle(50,50,8))print( 绘制所有图形 )draw_visitorDrawVisitor()canvas.accept(draw_visitor)print(\n 计算总面积 )area_visitorAreaVisitor()canvas.accept(area_visitor)print(f总面积:{area_visitor.total_area:.2f})print(\n 导出为 XML )xml_visitorXMLExportVisitor()canvas.accept(xml_visitor)print(xml_visitor.get_xml())输出 绘制所有图形 绘制圆形中心(10, 20), 半径 5 绘制矩形左上角(30, 40), 宽 15, 高 10 绘制圆形中心(50, 50), 半径 8 计算总面积 圆形面积: 78.54 矩形面积: 150.00 圆形面积: 201.06 总面积: 429.60 导出为 XML shapescircle x10 y20 radius5 /rectangle x30 y40 width15 height10 /circle x50 y50 radius8 //shapes访问者模式结构总结角色说明Visitor抽象访问者接口ShapeVisitorConcreteVisitor具体访问者DrawVisitor、AreaVisitor 等Element元素接口Shape定义 accept 方法ConcreteElement具体元素Circle、RectangleObject Structure对象结构Canvas管理元素集合访问者模式 vs 其他模式对比模式目的扩展方向典型场景访问者在稳定结构上添加新操作添加新操作AST 处理、文档转换组合构建树形结构添加新元素GUI 树、文件系统策略替换算法替换行为支付、排序命令封装请求添加新命令撤销、宏Python 中的实用建议访问者模式在 Python 中使用较少因为 Python 是动态语言很多场景可以用函数作为访问者传入不同函数getattr(element, operation_name)()动态调用多分派库如functools.singledispatch或multipledispatch更 Pythonic 的替代方式fromfunctoolsimportsingledispatchsingledispatchdefprocess_shape(shape):raiseNotImplementedError(fUnsupported shape:{type(shape)})process_shape.registerdef_(shape:Circle):print(f处理圆形: 半径{shape.radius})process_shape.registerdef_(shape:Rectangle):print(f处理矩形: 宽高{shape.width}x{shape.height})# 使用forshapeincanvas.shapes:process_shape(shape)注意事项访问者模式违反了“依赖倒置原则”高层依赖抽象因为访问者需要知道所有具体元素类型添加新元素类型时需要修改所有访问者违反开闭原则适合元素结构稳定、操作频繁变化的场景如果元素经常变化考虑用组合 访问者双向结合总结访问者模式是处理稳定数据结构 多变操作的经典解决方案在编译器、解释器、序列化等系统中非常常见。但在 Python 中由于语言的动态性通常优先考虑更简单的方案如 singledispatch、函数式编程。如果你想看更实际的例子如 AST 遍历、HTML 渲染器、报表统计访问者或者如何结合组合模式构建复杂结构欢迎继续问