【浅谈设计模式】(21):访问者模式 | 系数计算

前言

3月卷起来!

访问者模式是一种行为型模式,通过这种模式,我们可以在不修改对象自身的情况下,为对象结构的元素添加新的操作。比如网络安全运维人员,在对堡垒机等安全网站后台审核证书的时候,发现证书即将过期,就可以重新上传安全包到后台,在不修改服务器相关资源下就完成版本的升级。

一、概述

在数据结构中保存着很多元素,我们会对这些元素进行 "处理" ,这些处理的代码放在哪里比较好呢?

通常做法是把它们放在表示数据结构的类中,比如我们的处理类,有针对 A 对象的处理方法,有针对 B 对象的处理方法,但是新增 C 对象的时候,就需要修改处理类,新增 C 对象的处理方法。

访问者模式是一种将数据结构和处理分离开的思想。

我们定义一个访问者接口,里面有个处理方法,然后在每个对象中实现这个接口并在处理方法中自定义自己的实现。假设动物园有很多动物,然后访问者接口就是兽医,里面的处理方法就是体检,每个动物的体检方式还不一样,就在自己内部实现,比如大象的体检可能是称体重,看耳朵,狮子的体检可能是看牙齿,测体温。访问者接口依次访问每种动物,并根据不同的动物执行不同的体检方式。

在访问者模式中,有几种角色:

  • Visitor : 它定义对每一个元素访问的行为,常常是抽象方法或接口;

  • ConcreteVisitor:具体访问者,实现具体行为,处理具体角色的行为;

  • element:表示 Visitor 角色的访问对象,每个元素都要被访问者访问。

  • ConcreteElement:负责实现 Element 角色所定义的接口API

  • ObjectStructure:对象结构。具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素(Element),并且可以迭代这些元素,供访问者访问

优点:

  • 扩展性好,不修改对象结构的情况下,就可以为对象新增功能
  • 复用性好
  • 将数据与行为分离开,每个访问者的职责单一

缺点:

  • 对象结构变化很困难,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了"开闭原则"。

访问者模式常常用于对象结构稳定,操作算法经常变化的程序。

二、案例

名字 说明
Visitor 表示访问者的抽象接口,通过它访问各个元素
GreenVisitor Visitor 的具体实现类,通过它访问绿色节能减排的对象
InComeVisitor Visitor 的具体实现类,通过它访问经济效益的对象
PointValue 表示 Visitor 角色的访问对象,每个元素都要被访问者访问。
  • Visitor
csharp 复制代码
public interface Visitor {
    void visit(PointValue pointValue);
}
  • GreenVisitor
java 复制代码
public class GreenVisitor implements Visitor{
    // 计算系数
private  double pointRate;

    // 计算绿色系数
public GreenVisitor(double pointRate){
        this.pointRate = pointRate;
    }


    @Override
    public void visit(PointValue pointValue) {
        // 根据采集的点位做一个系数计算,并打印结果
double value = pointRate * Double.parseDouble(pointValue.getValue());
       System.out.println(pointValue.getDeviceName()+"每年可减少二氧化碳排放:"+value);
    }
}
  • InComeVisitor
java 复制代码
public class InComeVisitor implements Visitor{
    // 计算系数
private  double pointRate;

    // 计算绿色系数
public InComeVisitor(double pointRate){
        this.pointRate = pointRate;
    }

    @Override
    public void visit(PointValue pointValue) {
        // 根据采集的点位做一个系数计算,并打印结果
double value = pointRate * Double.parseDouble(pointValue.getValue());
        System.out.println(pointValue.getDeviceName()+"累计收益:"+value);
    }
}
  • PointValue
typescript 复制代码
 /**
* @author xiaolei
* @version 1.0 采集点值
* @ date 2024-03-10 20:59
*/
public class PointValue {
    private String deviceName;
    private String pointName;
    private String value;

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

    public PointValue(String deviceName, String pointName, String value) {
        this.deviceName = deviceName;
        this.pointName = pointName;
        this.value = value;
    }

    public String getDeviceName() {
        return deviceName;
    }

    public void setDeviceName(String deviceName) {
        this.deviceName = deviceName;
    }

    public String getPointName() {
        return pointName;
    }

    public void setPointName(String pointName) {
        this.pointName = pointName;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

测试

ini 复制代码
public class Test {
    public static void main(String[] args) {
        // 创建两个采集对象,这两个对象分别从两个电站的电表采集总电能数据,这个数据经过系数换算后可以得到
PointValue pointValue = new PointValue("松江园区电表","电表正向总有功电能","10086");

        PointValue pointValue2 = new PointValue("长宁园区电表","电表正向总有功电能","554");

        // 使用不同的访问方式进行访问
GreenVisitor greenVisitor = new GreenVisitor(0.001);
        InComeVisitor inComeVisitor = new InComeVisitor(100);

        pointValue.accept(greenVisitor);
        pointValue.accept(inComeVisitor);

        pointValue2.accept(greenVisitor);
        pointValue2.accept(inComeVisitor);

    }
}

打印结果:

makefile 复制代码
松江园区电表每年可减少二氧化碳排放:10.086
松江园区电表累计收益:1008600.0
长宁园区电表每年可减少二氧化碳排放:0.554
长宁园区电表累计收益:55400.0

三、总结

访问者模式作为一种行为型模式,允许定义一些新的操作,而不改变元素的结构。

主要角色有访问者和元素两个。

  • 访问者定义了对元素进行操作的方法,每个具体访问者类来实现这些方法来执行不同的操作
  • 元素:定义了接收访问者的方法,每个具体元素类类实现这个方法接受访问者的访问。

工作原理大概就是,具体元素类接受一个具体访问者的对象,并将自己作为参数传递给访问者对象的方法,具体访问者类根据具体的元素的元素类型执行不同的操作。

总的来说,访问者模式可以帮助我们在不改变对结构的情况下为对象添加新的操作,提高代码的扩展性和灵活性。

相关推荐
monkey_meng9 分钟前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
草莓base22 分钟前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
Estar.Lee24 分钟前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
drebander1 小时前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天2491 小时前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn1 小时前
java入门 自定义springboot starter
java·开发语言·spring boot
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
Grey_fantasy1 小时前
高级编程之结构化代码
java·spring boot·spring cloud
新知图书1 小时前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
弗锐土豆1 小时前
工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程
java·opencv·安全·检测·面部