设计模式1-访问者模式

访问者模式 是一种行为 设计模式,它允许你定义在对象结构中的元素上进行操作的新操作,而无需修改这些元素的类。这种模式的主要思想是将算法元素的结构分离开,使得可以在不修改元素结构的情况下定义新的操作。

所谓算法与元素结构分离,即保持元素(被访问对象)结构的稳定 ,而将算法 置于访问者之中,因为访问者可以新建,这样就符合了OCP(开闭原则)。

在访问者模式中,有两个主要的角色:

  1. 访问者(Visitor)

    定义了在对象结构中访问元素时的新操作接口。通常会有多个不同的访问者,每个访问者实现一组特定的操作。

  2. 元素(Element)

    定义了接受访问者的接口,通常会有多个不同的元素,每个元素实现了接口并提供了接受访问者的方法。

访问者模式的主要优势在于当需要在一组对象上执行一些复杂的操作时,你可以通过添加新的访问者而不是修改每个元素的类来扩展系统。

类图:

下面是一个简单的 Java 示例,演示了访问者模式的基本结构:

java 复制代码
// 访问者接口
interface Visitor {
    void visit(ElementA elementA);
    void visit(ElementB elementB);
}

// 元素接口
interface Element {
    void accept(Visitor visitor);
}

// 具体的元素A
class ElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    // 元素A的特定操作
    void operationA() {
        System.out.println("Performing operation A on ElementA");
    }
}

// 具体的元素B
class ElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    // 元素B的特定操作
    void operationB() {
        System.out.println("Performing operation B on ElementB");
    }
}

// 具体的访问者
class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ElementA elementA) {
        elementA.operationA();
    }

    @Override
    public void visit(ElementB elementB) {
        elementB.operationB();
    }
}

// 客户端代码
public class VisitorPatternExample {
    public static void main(String[] args) {
        Element elementA = new ElementA();
        Element elementB = new ElementB();

        Visitor visitor = new ConcreteVisitor();

        elementA.accept(visitor); // 执行 ElementA 的操作
        elementB.accept(visitor); // 执行 ElementB 的操作
    }
}

在这个例子中,Visitor 接口定义了两个访问方法,每个方法对应一个具体的元素。Element 接口定义了接受访问者的方法。具体的元素类 ElementAElementB 实现了 Element 接口,并分别实现了自己的特定操作。ConcreteVisitor 是一个具体的访问者类,实现了对每个元素的访问操作。

在客户端代码中,我们创建了一个 ConcreteVisitor 实例,并让每个元素接受这个访问者。换言之,是在Client 中操作了访问者与元素的连接印证了类图。这样,通过不同的访问者,我们可以执行不同的操作,而不需要修改元素的类。这是访问者模式的一种简单示例,实际中可能涉及更复杂的场景和多个元素。

怎么定义新的操作?

当使用访问者模式时,定义新的操作就是创建新的实现了访问者接口的具体访问者类。每个具体访问者类负责实现一组特定的操作,而这些操作可以是全新的、与原有操作不相关的,或者是对现有操作的扩展。

举个例子,假设我们有一个图形结构,包括圆形(Circle)和矩形(Rectangle)两种图形。现在我们希望实现两种不同的操作:计算图形的面积和计算图形的周长。

首先,定义图形接口和具体的图形类:

java 复制代码
// 图形接口
interface Shape {
    void accept(Visitor visitor);
}

// 圆形类
class Circle implements Shape {
    private double radius;

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

    public double getRadius() {
        return radius;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 矩形类
class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

然后,定义访问者接口和具体的访问者类:

java 复制代码
// 访问者接口
interface Visitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

// 计算面积的具体访问者类
class AreaCalculator implements Visitor {
    @Override
    public void visit(Circle circle) {
        double area = Math.PI * circle.getRadius() * circle.getRadius();
        System.out.println("Area of Circle: " + area);
    }

    @Override
    public void visit(Rectangle rectangle) {
        double area = rectangle.getWidth() * rectangle.getHeight();
        System.out.println("Area of Rectangle: " + area);
    }
}

// 计算周长的具体访问者类
class PerimeterCalculator implements Visitor {
    @Override
    public void visit(Circle circle) {
        double perimeter = 2 * Math.PI * circle.getRadius();
        System.out.println("Perimeter of Circle: " + perimeter);
    }

    @Override
    public void visit(Rectangle rectangle) {
        double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());
        System.out.println("Perimeter of Rectangle: " + perimeter);
    }
}

现在我们可以在不修改图形类的情况下定义新的操作。例如,创建了两个具体的访问者类 AreaCalculatorPerimeterCalculator 分别用于计算图形的面积和周长。在客户端代码中,我们可以通过接受不同的访问者来执行不同的操作:

java 复制代码
public class VisitorExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);

        Visitor areaCalculator = new AreaCalculator();
        Visitor perimeterCalculator = new PerimeterCalculator();

        circle.accept(areaCalculator); // 计算圆形的面积
        circle.accept(perimeterCalculator); // 计算圆形的周长

        rectangle.accept(areaCalculator); // 计算矩形的面积
        rectangle.accept(perimeterCalculator); // 计算矩形的周长
    }
}

这样,通过定义不同的访问者,我们可以轻松地扩展系统,而无需修改图形类的结构。

结论:

访问者模式 很好地实现了访问算法开放被访问元素封闭, 有这种需求时就可以考虑使用访问者模式。

相关推荐
火烧屁屁啦8 分钟前
【JavaEE进阶】初始Spring Web MVC
java·spring·java-ee
w_312345422 分钟前
自定义一个maven骨架 | 最佳实践
java·maven·intellij-idea
岁岁岁平安24 分钟前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA27 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
Q_192849990634 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
张国荣家的弟弟1 小时前
【Yonghong 企业日常问题 06】上传的文件不在白名单,修改allow.jar.digest属性添加允许上传的文件SH256值?
java·jar·bi
ZSYP-S1 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
越甲八千1 小时前
重温设计模式--享元模式
设计模式·享元模式
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
是小崔啊1 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴