当前位置: 首页 > news >正文

对访问者模式的理解

对访问者模式的理解

    • 一、场景
    • 二、不采用访问者模式
      • 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。算法还是没法和对象真正隔开。

相关文章:

  • 压力容器的断裂力学计算
  • ansible+docker+docker-compose快速部署4节点高可用minio集群
  • 2140 星期计算
  • 仿modou库one thread one loop式并发服务器
  • 浅谈进程的就绪状态与挂起状态
  • 【网络协议】WebSocket讲解
  • Kettle如何与应用集成
  • Python星球日记 - 第11天:文件操作
  • 【项目日记】高并发服务器项目总结
  • [环境配置] 1. 开发环境搭建
  • 自制简易 Shell:像搭建积木小屋一样打造命令交互小天地
  • (一)栈结构、队列结构
  • Quartz SpringBoot整合定时任务的基础使用方法 任务调度 定时器 单机版
  • [Android] 奇酷阅读V1.0.0 集小说、漫画、听书三合一 内置600多条源
  • MySQL 约束(入门版)
  • javaweb自用笔记:配置优先级、Bean管理、springBoot原理
  • Android SELinux权限使用
  • 数字音频基础​​
  • Vue3:初识Vue,Vite服务器别名及其代理配置
  • HCIP实验
  • 门户网站开发文档/广州seo黑帽培训
  • 真人视频发牌网站开发/广州网络运营课程培训班
  • 有创意的图文广告店名/苏州优化排名seo
  • 真人男女直接做的视频网站/西安seo外包服务
  • 网站建设内部问卷/百度站长工具怎么关闭
  • 网站整合营销建设/网络推广都有哪些平台