软件设计模式----访问者模式

2023-11-10

访问者模式

模式动机

对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同,访问者模式为解决这类问题而诞生。
在这里插入图片描述

  • 在实际使用时,对同一集合对象的操作并不是唯一的,对相同的元素对象可能存在多种不同的操作方式。
    而且这些操作方式并不稳定,可能还需要增加新的操作,以满足新的业务需求。
    此时,访问者模式就是一个值得考虑的解决方案。
    访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。

模式定义

访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。

  • 访问者模式包含如下角色:
    Vistor: 抽象访问者
    ConcreteVisitor: 具体访问者
    Element: 抽象元素
    ConcreteElement: 具体元素
    ObjectStructure: 对象结构

模式分析

访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。
访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。
相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。在访问者模式中,增加新的访问者无须修改原有系统,系统具有较好的可扩展性。

  • 典型的抽象访问者类代码如下所示:
    在这里插入图片描述
  • 典型的具体访问者类代码如下所示:
    在这里插入图片描述
  • 典型的抽象元素类代码如下所示:
    在这里插入图片描述
  • 典型的具体元素类代码如下所示:
    在这里插入图片描述
  • 典型的对象结构类代码如下所示:
    在这里插入图片描述

访问者模式实例与解析

  • 实例一:购物车
    顾客在超市中将选择的商品,如苹果、图书等放在购物车中,然后到收银员处付款。在购物过程中,顾客需要对这些商品进行访问,以便确认这些商品的质量,之后收银员计算价格时也需要访问购物车内顾客所选择的商品。此时,购物车作为一个ObjectStructure(对象结构)用于存储各种类型的商品,而顾客和收银员作为访问这些商品的访问者,他们需要对商品进行检查和计价。不同类型的商品其访问形式也可能不同,如苹果需要过秤之后再计价,而图书不需要。使用访问者模式来设计该购物过程。
    在这里插入图片描述
  • UML类图:
    在这里插入图片描述
package 访问者模式;
//抽象访问者类
public abstract class Visitor {
    protected String name;
    public void setName(String name){
        this.name = name;
    }
    public abstract void visit(Apple apple);
    public abstract void visit(Book book);
}

package 访问者模式;
//具体访问者类
public class Customer extends Visitor{

    @Override
    public void visit(Apple apple) {
        System.out.println("顾客"+ name + "选苹果");
    }

    @Override
    public void visit(Book book) {
        System.out.println("顾客"+ name + "买书");
    }
}

package 访问者模式;
//具体访问者类
public class Saler extends Visitor {
    @Override
    public void visit(Apple apple) {
        System.out.println("收银员"+ name + "给苹果过秤,然后计算价格。");
    }

    @Override
    public void visit(Book book) {
        System.out.println("收银员"+ name + "计算书本的价格。");
    }
}

package 访问者模式;
//抽象元素类
public interface  Product  {
    public void accept(Visitor visitor);
}

package 访问者模式;
//具体元素类
public class Book implements Product {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

package 访问者模式;
//具体元素类
public class Apple implements Product {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

package 访问者模式;

import java.util.ArrayList;
import java.util.Iterator;

//对象结构类
public class BuyBasket {
    private ArrayList list = new ArrayList();
    public void accept(Visitor visitor){
        Iterator i = list.iterator();
        while (i.hasNext()){
            ((Product)i.next()).accept(visitor);
        }
    }
    public void addProduct(Product product){
        list.add(product);
    }

    public void removeProduct(Product product){
        list.remove(product);
    }
}

package 访问者模式;

import 工厂方法模式2.XMLUtil;

public class client {
    public static void main(String[] args) {
        Product b1 = new Book();
        Product b2 = new Book();
        Product b3 = new Apple();

        Visitor visitor;

        BuyBasket basket = new BuyBasket();
        basket.addProduct(b1);
        basket.addProduct(b2);
        basket.addProduct(b3);

        visitor = (Visitor) XMLUtil.getBean();
        visitor.setName("Danny");
        basket.accept(visitor);

    }
}

  • 结果展示:
    在这里插入图片描述

  • 实例二:奖励审批系统
    某高校奖励审批系统可以实现教师奖励和学生奖励的审批(AwardCheck),如果教师发表论文数超过10篇或者学生论文超过2篇可以评选科研奖,如果教师教学反馈分大于等于90分或者学生平均成绩大于等于90分可以评选成绩优秀奖,使用访问者模式设计该系统,以判断候选人集合中的教师或学生是否符合某种获奖要求。
    在这里插入图片描述

package 访问者模式2;
//抽象访问者类
public abstract class AwardCheck {
    public abstract void visit(Teacher teacher);
    public abstract void visit(Student student);
}

package 访问者模式2;
//具体访问者类
public class ExcellenceAwardCheck extends AwardCheck{

    @Override
    public void visit(Teacher teacher) {
        if(teacher.getFeedbackScore()>=90){
            System.out.println(teacher.getName() + "可评选为教师成绩优异奖。");
        }
    }

    @Override
    public void visit(Student student) {
        if(student.getScore()>=90){
            System.out.println(student.getName() + "可评选为学生成绩优异奖。");
        }
    }
}

package 访问者模式2;
//具体访问者类
public class ScientificAwardCheck extends AwardCheck {
    @Override
    public void visit(Teacher teacher) {
        if(teacher.getFeedbackScore()>=10){
            System.out.println(teacher.getName() + "可评选为教师科研奖。");
        }
    }

    @Override
    public void visit(Student student) {
       if(student.getPaperAmount()>=2){
           System.out.println(student.getName()+"可评选为学生科研奖。");
       }
    }
}

package 访问者模式2;
//抽象元素类
public interface Person {
    public void accept(AwardCheck check);
}

package 访问者模式2;
//具体元素类
public class Student implements Person {
    private String name;
    private int paperAmount;
    private double score;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPaperAmount() {
        return paperAmount;
    }

    public void setPaperAmount(int paperAmount) {
        this.paperAmount = paperAmount;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public void accept(AwardCheck check){
        check.visit(this);
    }
}

package 访问者模式2;
//具体元素类
public class Teacher implements Person {
    private String name;
    private int paperAmount;
    private double feedbackScore;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPaperAmount() {
        return paperAmount;
    }

    public void setPaperAmount(int paperAmount) {
        this.paperAmount = paperAmount;
    }

    public double getFeedbackScore() {
        return feedbackScore;
    }

    public void setFeedbackScore(double feedbackScore) {
        this.feedbackScore = feedbackScore;
    }

    public void accept(AwardCheck check){
        check.visit(this);
    }
}

package 访问者模式2;

import java.util.ArrayList;
import java.util.Iterator;

//对象结构
public class CandidateList {
    private ArrayList<Person> list = new ArrayList<>();

    public void addPerson(Person person){
        list.add(person);
    }

    public void removePerson(Person person){
        list.remove(person);
    }
    public void accept(AwardCheck check){
        Iterator i = list.iterator();
        while (i.hasNext()){
            ((Person)i.next()).accept(check);
        }
    }
}

package 访问者模式2;

public class client {
    public static void main(String[] args) {
        CandidateList list = new CandidateList();
        AwardCheck sac,eac;
        Teacher teacher = new Teacher();
        Student student = new Student();
        teacher.setName("大冰");
        teacher.setFeedbackScore(92);
        teacher.setPaperAmount(15);
        student.setName("大童");
        student.setPaperAmount(2);
        student.setScore(85);

        list.addPerson(teacher);
        list.addPerson(student);
        sac = new ScientificAwardCheck();
        list.accept(sac);
        eac = new ExcellenceAwardCheck();
        list.accept(eac);

    }
}
  • 结果展示:
    在这里插入图片描述

模式优缺点

  • 访问者模式的优点
    使得增加新的访问操作变得很容易。
    将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。
    可以跨过类的等级结构访问属于不同的等级结构的元素类。
    让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作。
  • 访问者模式的缺点
    增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
    破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
模式适用环境

在以下情况下可以使用访问者模式:
一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

模式应用

(1) 在一些编译器的设计中运用了访问者模式,程序代码是被访问的对象,它包括变量定义、变量赋值、逻辑运算、算术运算等语句,编译器需要对代码进行分析,如检查变量是否定义、变量是否赋值、算术运算是否合法等,可以将不同的操作封装在不同的类中,如检查变量定义的类、检查变量赋值的类、检查算术运算是否合法的类,这些类就是具体访问者,可以访问程序代码中不同类型的语句。在编译过程中除了代码分析外,还包含代码优化、空间分配和代码生成等部分,也可以将每一个不同编译阶段的操作封装到了跟该阶段有关的一个访问者类中。
(2) 在常用的Java XML处理技术DOM4J中,可以通过访问者模式的方式来读取并解析XML文档,VisitorSupport是DOM4J提供的Visitor接口的默认适配器,具体访问者只需继承VisitorSupport类即可。

  • 与其他模式联用
    由于访问者模式需要对对象结构进行操作,而对象结构本身是一个元素对象的集合,因此访问者模式经常需要与迭代器模式联用,在对象结构中使用迭代器来遍历元素对象。
    在访问者模式中,元素对象可能存在容器对象和叶子对象,因此可以结合组合模式来进行设计。
  • 倾斜的“开闭原则”
    访问者模式以一种倾斜的方式支持“开闭原则”,增加新的访问者方便,但是增加新的元素很困难。

模式总结

访问者模式表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。
访问者模式包含五个角色:抽象访问者为对象结构类中每一个抽象元素类声明一个访问操作;具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素;抽象元素一般是抽象类或者接口,它定义一个accept()方法,该方法以一个抽象访问者作为参数;具体元素实现了accept()方法,在其accept()中调用访问者的访问方法以便完成对一个元素的操作;对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。
访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。访问者模式包括两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次结构,提供了抽象元素和具体元素。相同的访问者可以以不同的方式访问不同的元素,相同的元素可以接受不同访问者以不同访问方式访问。在访问者模式中,增加新的访问者无须修改原有系统,系统具有较好的可扩展性。
访问者模式的主要优点在于使得增加新的访问操作变得很容易,将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中,还可以跨过类的等级结构访问属于不同的等级结构的元素类,让用户能够在不修改现有类层次结构的情况下,定义该类层次结构的操作;其主要缺点在于增加新的元素类很困难,而且在一定程度上破坏系统的封装性。
访问者模式适用情况包括:一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作;需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类;对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

软件设计模式----访问者模式 的相关文章

  • 【C】PTA期末分数排序(归并排序)

    考试结束了 全班同学的分数都出来了 老师需要对分数做一次排序 看看从高到低 分数的排列是怎样的 输入格式 第一行是一个n 表示班级同学的人数 1 lt n lt 500000 第二行开始有n个分数 0 lt 分数 lt 100 分数都是整数

随机推荐

  • [从零开始学习FPGA编程-23]:进阶篇 - 基本组合电路-门电路级组合逻辑运算(Verilog语言)

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 125246093 目录 前言 第1章
  • 排序函数 dense_rank()

    dense rank 对结果集进行排序 排名值没有间断 特定行的排名等于该特定行之前不同排名值的数量加一 语法 DENSE RANK OVER
  • error when starting dev server: Error: Cannot find module ‘node:url‘

    需要升级node版本 直接在官网 下载 Node js 上下载最新的msi 安装到原来node的安装目录下即可
  • 降雨预测方法

    降雨预测方法 DBNPF Deep Belief Network for Precipitation Forecast 来源 张雷师兄论文 A deep learning based precipitation forecasting 模型
  • centos 7 安装总结

    CentOS Community ENterprise Operating System 社区企业操作系统 Linux发行版之一 来源于Red Hat Enterprise Linux依照开放代码规定释出的源代码所编译而成 安装注意 安装c
  • 如何用python爬取大量博客

    如果要使用 Python 爬取大量博客 可以使用爬虫框架 例如 Scrapy 首先 需要对目标网站进行分析 确定数据的 URL 格式和数据的 HTML 标签 然后 可以使用 Scrapy 来编写爬虫代码 自动访问目标网站并提取需要的数据 具
  • 在 CLion 中进行整体替换代码

    突然在打题的时候发现int型无法满足题目要求 想要批量换成long long 使用快捷键 Ctrl Shift R Windows Linux 或 Command Shift R Mac 来打开 Find and Replace 对话框 在
  • 第一章 python初窥 课后练习题

    目录 1 输入1 127的ascii码并输出对应字符 2 输入a b c d4个整数 计算a b c d的结果 3 计算一周有多少分钟 多少秒钟 4 3个人在餐厅吃饭 想分摊饭费 总共花费35 27美元 他们还想给15 的小费 每个人该怎么
  • SpringBoot: Mybatis配置事务管理

  • awk 正则表达式、正则运算符详细介绍

    http www cnblogs com chengmo archive 2010 10 11 1847772 html 前言 使用awk作为文本处理工具 正则表达式是少不了的 要掌握这个工具的正则表达式使用 其实 我们不必单独去学习它的正
  • STM32CubeMX—串口空闲中断+DMA接收

    一 实验说明 实验平台 STM32F103C8T6 实验内容 使用串口一空闲中断结合DMA 完成不定长数据接收 STM32的串口接收数据的方式 1 轮询接收 所谓轮询 就是在主函数中判断接收完成的标志位 举个不太恰当例子 就比如 此时你正在
  • 【8086汇编】字符串逆序的实现,附代码

    目录 引言 题目 程序思想 程序例程 程序结果 改进思路 引言 之前有本科的同学需要做一个汇编程序 简单补了补发现自己会嘻嘻嘻 题目 题目 从键盘输入任一字符串 字符数 gt 1 在下一行以相反的次序显示出来 如 输入字符串123abc 在
  • 企业微信三方应用开发(三)企业微信第三方应用开发常见问题

    加我微信li570467731 拉你进二百多人企业微信开发同行群 文末有二维码 企业微信开发三部曲 企业微信应用开发概述篇 免费 已完结 企业微信开发第三方应用开发篇 更新中 企业微信开发自建内部应用开发 筹备中 关注公众号 ToB Dev
  • 详细解析Python爬虫代理的使用方法

    嗨 大家好 作为一名专业的代理IP供应商 我想和你们聊一聊爬虫中常用的代理IP类型以及如何在Python中使用代理IP 相信这篇文章会让你对Python爬虫代理IP的使用有更深入的了解 那么 不多说 让我们开始吧 首先 让我们来了解一下爬虫
  • Java基础——环境变量配置、注释、关键字、标识符

    目录 01 01 计算机基础知识 计算机概述 01 02 计算机基础知识 软件开发和计算机语言概述 01 03 计算机基础知识 人机交互 01 04 计算机基础知识 键盘功能键和快捷键 01 05 计算机基础知识 如何打开DOS控制台 01
  • mes选型与实施指南_中小制造企业,如何选型信息系统?

    一 前言 最近走访客户和讲课比较多 接触到了大量的中小制造企业 深刻体会到了制造企业的焦虑 也深刻体会到了中小企业期望进行数字化转型 期望实现智能制造的迫切愿望 但是受限于人才 受限于知识 受限于经验 对实现企业信息化的过程中 过多受到乙方
  • Revit 2011二次开发之得到选择的对象

    start Transaction TransactionMode Manual Regeneration RegenerationOption Manual public class Document Selection IExterna
  • 微前端介绍

    提到微前端 稍微懂微前端的同学 可能会这样问 为什么不用iframe方案呢 其实 如果不考虑体验问题 iframe方案几乎是最完美的微前端解决方案 iframe最大的特性就是提供了浏览器原生的硬隔离方案 样式隔离 js隔离 但它最大的问题也
  • 韦东山嵌入式教程第四篇Linux驱动基础知识学习笔记(1)——Hello驱动程序

    驱动入门 Hello驱动程序 1 怎么写出一个驱动程序 确定主设备号 定义自己的file operations结构体 实现对应的open read write函数 填入file operations结构体 实现入口函数 安装驱动程序时 就会
  • 软件设计模式----访问者模式

    访问者模式 模式动机 对于系统中的某些对象 它们存储在同一个集合中 且具有不同的类型 而且对于该集合中的对象 可以接受一类称为访问者的对象来访问 而且不同的访问者其访问方式有所不同 访问者模式为解决这类问题而诞生 在实际使用时 对同一集合对