对访问者模式的理解

news2025/4/17 5:53:20

对访问者模式的理解

    • 一、场景
    • 二、不采用访问者模式
      • 1、代码
      • 2、特点
    • 三、采用访问者模式
      • 1、代码
      • 2、特点
    • 四、思考

一、场景

  • 我们有一个图形系统,系统中有多种图形对象(如圆形、方形等),每种图形对象都有不同的属性和行为。现在需要对这些图形对象执行不同的操作,比如计算面积、绘制图形等。

    • 图形对象:圆形(Circle)、方形(Square)。
    • 操作:计算面积(CalculateArea)、绘制图形(Draw)。

二、不采用访问者模式

1、代码

  • 各种图形类
public interface Shape {
    double calculateArea();
    void draw();
}

public class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a circle with radius: " + radius);
    }
}

public class Square implements Shape {
    private double side;

    public Square(double side) {
        this.side = side;
    }

    @Override
    public double calculateArea() {
        return side * side;
    }

    @Override
    public void draw() {
        System.out.println("Drawing a square with side: " + side);
    }
}
  • 客户端
public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape square = new Square(4);

        System.out.println("Circle area: " + circle.calculateArea());
        circle.draw();

        System.out.println("Square area: " + square.calculateArea());
        square.draw();
    }
}

/*
Circle area: 78.53981633974483
Drawing a circle with radius: 5.0
Square area: 16.0
Drawing a square with side: 4.0
*/

2、特点

我这里没说缺点,因为在当前的情况下,上述设计是不错的。

  • 上述设计的思路是各种图形实现各自的行为,例如:圆形和方形各自实现计算面积的算法。

  • 但随着业务的发展,原本小而美的类,会出现越来越多的方法。慢慢的,类不再是单一职责了。

    • 例如:我们还需要计算图形的周长。

三、采用访问者模式

1、代码

  • 各种图形类

    public interface Shape {
        void accept(ShapeVisitor visitor);
    }
    
    public class Circle implements Shape {
        private double radius;
    
        public Circle(double radius) {
            this.radius = radius;
        }
    
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitCircle(this);
        }
    
        public double getRadius() {
            return radius;
        }
    }
    
    public class Square implements Shape {
        private double side;
    
        public Square(double side) {
            this.side = side;
        }
    
        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visitSquare(this);
        }
    
        public double getSide() {
            return side;
        }
    }
    
    • 图形的行为外派给ShapeVistor
  • 各种访问者

    public interface ShapeVisitor {
        void visitCircle(Circle circle);
        void visitSquare(Square square);
    }
    
    public class DrawVisitor implements ShapeVisitor {
        @Override
        public void visitCircle(Circle circle) {
            System.out.println("Drawing a circle with radius: " + circle.getRadius());
        }
    
        @Override
        public void visitSquare(Square square) {
            System.out.println("Drawing a square with side: " + square.getSide());
        }
    }
    
    public class AreaVisitor implements ShapeVisitor {
        @Override
        public void visitCircle(Circle circle) {
            double area = Math.PI * circle.getRadius() * circle.getRadius();
            System.out.println("Circle area: " + area);
        }
    
        @Override
        public void visitSquare(Square square) {
            double area = square.getSide() * square.getSide();
            System.out.println("Square area: " + area);
        }
    }
    
    
  • 客户端

    public class Main {
        public static void main(String[] args) {
            Shape circle = new Circle(5);
            Shape square = new Square(4);
    
            ShapeVisitor areaVisitor = new AreaVisitor();
            ShapeVisitor drawVisitor = new DrawVisitor();
    
            circle.accept(areaVisitor);
            circle.accept(drawVisitor);
    
            square.accept(areaVisitor);
            square.accept(drawVisitor);
        }
    }
    
    /*
    Circle area: 78.53981633974483
    Drawing a circle with radius: 5.0
    Square area: 16.0
    Drawing a square with side: 4.0
    */
    

2、特点

  • 假设要计算图形的周长,我们新增一个PerimeterVisitor即可。

    • 如果不采用访问者模式,我们需要给Circle、Square这两个类各自新增计算周长的方法。
    • 显然,访问者模式更遵循开闭原则。

四、思考

  • 在实际开发中,我们先定义接口,再定义实现类。往往会面临一个尴尬地处境:接口中的方法越加越大,实现类也越发臃肿。

  • 这个时候,访问者模式会发挥一定的作用:想明白接口中哪些方法是这个接口必须的,哪些方法是随着业务发展不断扩展的。将扩展的方法用访问者模式实现。

    // 在访问器模式中,这种也叫Element接口。
    public interface InterfaceA {
    	// 必须的方法
    	void methodA();
      
    	// 扩展方法
        void accept(Vistor vistor);
    }
    
    • 很显然,传入不同的vistor,就实现了不同的扩展。
  • 这时候有人可能会说:增加一种类型,Vistor接口也会增加方法啊。Vistor接口的方法也可能越来越多啊。

    • 这时候就要具体问题具体分析了,

      • 情况1:如果具体的Element随着业务的发展越来越多,但Element接口的方法不怎么增加,显然,不采用访问者模式更好。
      • 情况2:但如果具体的Element在软件设计时确定下来了,后续也不怎么增加了,但每个Element中的方法会越来越多,显然,采用访问者模式更好。
  • 假设我们遇到的是情况2,采用了访问者模式进行软件设计,正在写如下代码:

    public class AreaVisitor implements ShapeVisitor {
        @Override
        public void visitCircle(Circle circle) {
            // 现有的Circle的方法不足以实现这个功能,没办法,得给Circle增加方法。
        }
    }
    
    • 如果我们在实现Vistor时,强依赖Element的方法,那么说明这个方法不适合由Vistor来实现,因为Circle提供一个public方法,结果只给visitCircle方法用,这是不合理的(不够封装)。此时,可以将该方法挪到Element接口中。
  • 假设其他人设计了访问者模式,我们来接手开发,当我们要实现一个新需求的时候,我们既可以在具体Element类中实现方法来满足诉求,又可以实现一个XxxVisitor类来满足诉求。

    • 例如:要计算图形的周长

      • 选择1:我们可以在Shape接口中新增计算周长的方法,然后Circle和Square去实现这个方法。
      • 选择2:我们也可以新增一个PerimeterVisitor类,在这个类中新增2个方法,一个给Circle计算周长,另一个给Square计算周长。
    • 每个人对设计模式的了解程度是不同的, 必然会出现有的人按选择1进行开发,有的人按选择2进行开发。慢慢地,这些代码变成了屎山。

  • 经过上述思考,我个人认为访问者模式并不是好的设计模式。

    • 访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。
    • 但事实是,Vistor接口的实现还是依赖于具体的Element。算法还是没法和对象真正隔开。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2330527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI前端组件库Ant DesIgn X

Ant Design X AI:体验新秩序 Ant Design 团队精心打造 RICH 设计范式,为 AI 界面提供卓越解决方案,引领智能交互新体验。 设计语言与理论 官网: Ant Design X - 轻松打造 AI 驱动的界面。 AI 设计范式 —— RICH 是我们在蚂蚁…

追踪大型语言模型的思维过程:提示词工程重要

追踪大型语言模型的思维过程:提示词工程重要 目录 追踪大型语言模型的思维过程:提示词工程重要**1. 分步思考能力:像人类一样打草稿****2. 跨语言概念词典:突破语言符号的束缚****3. 诗歌押韵规划:神经元提前预留韵脚****4. 编造专业解释:数据模式导致的“客服式回应”**…

BGP路由协议之属性1

公认属性是所有 BGP 路由器都必须能够识别的属性 公认必遵 (Well-known Mandatory) : 必须包括在每个 Update 消息里公认任意 (Well-known Discretionary) : 可能包括在某些 Update 消息里。 可选属性不需要都被 BGP 路由器所识别 可选过渡(OptionalTransitive) : BGP 设备不…

什么是 k8s 的 Taints(污点) 和 Tolerations(容忍度)

什么是 k8s 的 Taints(污点) 和 Tolerations(容忍度) 在 Kubernetes(K8s)中,Taints(污点)和 Tolerations(容忍度)用于影响 Pod 调度到节点的行为…

C++类模板的运用

使用vector实现一个简单的本地注册登录系统 注册&#xff1a;将账号密码存入vector里面&#xff0c;注意防重复判断 登录&#xff1a;判断登录的账号密码是否正确 #include <iostream> #include <vector> #include <string> #include <algorithm>us…

coze生成流程图和思维导图工作流

需求&#xff1a;通过coze平台实现生成流程图和思维导图&#xff0c;要求支持文档上传 最终工作流如下&#xff1a; 入参&#xff1a; 整合用户需求文件内容的工作流&#xff1a;https://blog.csdn.net/YXWik/article/details/147040071 选择器分发&#xff0c;不同的类型走…

【数据库】达梦arm64安装

话不多说&#xff0c;快速开始~ 1.下载 进入官网&#xff1a; 产品下载 | 达梦在线服务平台 下载安装包。 选飞腾、鲲鹏都可以&#xff0c;都是arm架构的。我选择的是&#xff1a; 直接下载地址是https://download.dameng.com/eco/adapter/DM8/202502/dm8_20250117_HWarm920…

leetcode274.H指数

直接排序完后进行遍历 class Solution {public int hIndex(int[] citations) {Arrays.sort(citations);int result 0;for (int i citations.length-1; i >0; i--) {if(citations[i]>citations.length-i)resultcitations.length-i;elsebreak;}return result;} }

内网文件传输新体验,聊天、传输、自定义,一应俱全

Flix 是一款高效、便捷的跨平台局域网文件传输工具&#xff0c;支持 Windows、macOS、Android、iOS 和 Linux 等多种操作系统。它以简洁直观的聊天式界面为特色&#xff0c;让用户能够像发送消息一样轻松地传输文件&#xff0c;无需复杂的设置或登录。Flix 支持大文件和多种格式…

Vue PDF Annotation plugin library online API examples

This article introduces the online version of the ElasticPDF API tutorial for the PDF annotation plug-in library in Vue projects. The API includes ① Export edited PDF data; ② Export annotations json data; ③ Reload old annotations; ④ Change files; ⑤ Se…

C语言传参寄存器压栈流程总结

相关 《Linux函数调用栈的实现原理&#xff08;X86&#xff09;》 总结 rsp向低地址生长&#xff08;栈顶&#xff09;&#xff0c;rbp记录旧值&#xff08;栈底&#xff09;。 intel x86测试&#xff0c;六个和六个以内的参数用寄存器传递。8个参数场景&#xff0c;6个用寄存…

C盘清理——快速处理

C盘清理 | 快速处理 软件&#xff1a;小番茄C盘清理 https://ccleancdn.xkbrowser.com/cleanmaster/FanQieClean_13054_st.exe 前言&#xff1a;为什么需要专业的C盘清理工具&#xff1f; 作为一位长期与Windows系统打交道的技术博主&#xff0c;我深知C盘空间不足带来的痛苦…

前端服务配置详解:从入门到实战

前端服务配置详解&#xff1a;从入门到实战 一、环境配置文件&#xff08;.env&#xff09; 1.1 基础结构 在项目根目录创建 .env 文件&#xff1a; # 开发环境 VUE_APP_API_BASE_URL http://localhost:3000/api VUE_APP_VERSION 1.0.0# 生产环境&#xff08;.env.produc…

历年跨链合约恶意交易详解(四)——Chainswap20210711

漏洞合约函数 function receive(uint256 fromChainId, address to, uint256 nonce, uint256 volume, Signature[] memory signatures) virtual external payable {_chargeFee();require(received[fromChainId][to][nonce] 0, withdrawn already);uint N signatures.length;r…

Python基于OpenCV和SVM实现中文车牌识别系统GUI界面

说明&#xff1a;这是一个系统实战项目&#xff0c;如需项目代码可以直接到文章最后关注获取。 项目背景 随着智能交通系统和智慧城市的发展&#xff0c;车牌识别技术在车辆管理、交通监控、停车场收费等领域发挥着重要作用。传统的车牌识别系统主要针对英文和数字的识别&…

有瓶颈设备的多级生产计划问题:基于Matlab的深度解析与实践

内容摘要 本文围绕有瓶颈设备的多级生产计划问题展开&#xff0c;通过实例详细阐述问题背景、建立数学模型&#xff0c;并用Matlab代码进行求解。旨在帮助读者理解该问题的本质&#xff0c;掌握利用Matlab解决此类生产计划优化问题的方法&#xff0c;为企业在实际生产中合理规…

网络性能优化参数关系解读 | TCP Nagle / TCP_NODELAY / TCP_QUICKACK / TCP_CORK

注&#xff1a;本文为 “网路性能优化” 相关文章合辑。 未整理去重。 如有内容异常&#xff0c;请看原文。 TCP_NODELAY 详解 lenky0401 发表于 2012-08-25 16:40 在网络拥塞控制领域&#xff0c;Nagle 算法&#xff08;Nagle algorithm&#xff09;是一个非常著名的算法&…

如何将内网的IP地址映射到外网?详细方法与步骤解析

01 为什么需要将内网IP映射到外网 在当今数字化时代&#xff0c;远程访问内网资源已成为许多企业和个人的刚需。将内网IP地址映射到外网的主要目的是允许外部网络访问内网中的特定服务&#xff0c;比如Web服务器、远程桌面、文件共享等应用场景。无论是企业需要远程办公访问内…

HTTP 响应头 Strict-Transport-Security 缺失漏洞

HTTP 响应头 Strict-Transport-Security 缺失漏洞 这个漏洞就是说明网站的HTTP响应头中没有设置Strict-Transport-Security&#xff0c;没有设置则可以通过将https自己手动改成htttp的方式进行访问。不安全 解决方法 1.nginx配置 nginx中增加如下配置&#xff1a; location / …

【SPSS/EXCEl】主成分分析构建__综合评价指数

学习过程中实验操作的记录 1.数据准备和标准化&#xff1a; (1)区分正负相关性:判断每个因子是正向指标还是负向指标,计算每个的最大值和最小值 (2) 标准化: Min-Max标准化 Min-Max标准化&#xff08;最大最小值法&#xff09;&#xff1a; 将数据映射到指定的区间&#xff…