程序员必知的23种设计模式之访问者模式

2023-11-02

1. 模式引出-测评系统需求

在歌手评选的系统中,有歌手和观众,观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等)

要求:

  1. 每一种评价都需要一个类

  2. 各种观众也都都各需要一个类

  3. 观众类依赖评价类做出评价,不同种类的观众评价内容不同

  4. 评价的种类要求易扩展

2. 传统方案
  1. 可以得知,不同的观众类的评价方法不会有区别,所以可以复用评价的方法,构造一个Person父类,设子类观众Man类和Woman类。定义一个Action标记接口,实现子类为Fail和Success评价,本例为评价类为空类

在这里插入图片描述

在这里插入图片描述

  1. 在父类中定义accept方法,判读传入评价类的类型,并依照判断的结果做出不同的输出。
2.1 传统方式代码

Action.java

// 一个评价类的标记接口
public interface Action {
}

Fail.java

// 具体评价类,本例中不需要内容实现
public class Fail implements Action{
}

Success.java

// 具体评价类,本例中不需要内容实现
public class Success implements Action{
}

Person.java

// 观众类的父类
public abstract class Person {
	public abstract void accept(Action a);
}

Man.java

// 具体观众类
public class Man extends Person {

	@Override
	public void accept(Action a) {
		if (a instanceof Success) {
			System.out.println("man:success");
		} else {
			System.out.println("man:fail");
		}

	}

}

Woman.java

// 具体观众类
public class Woman extends Person {

	@Override
	public void accept(Action a) {
		if (a instanceof Success) {
			System.out.println("woman:success");
		} else {
			System.out.println("woman:fail");
		}

	}

}

测试Main.java

public class Main {
	public static void main(String[] args) {
		Action fail = new Fail();
		Person man = new Man();
		man.accept(fail);
		
	}
}

输出结果

在这里插入图片描述

传统方式的问题分析

  1. 如果系统比较小,还是ok的,但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了ocp原则, 不利于维护

  2. 扩展性不好,如果评价类型很多,需要增加或删除了某个评价的类型(需要修改if-else,很麻烦),或者管理方法,都不好做

  3. 引出我们会使用新的设计模式 – 访问者模式

3. 访问者模式基本介绍
  1. 访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

  2. 主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题

  3. 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口

  4. 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决

3.1 UML原理类图

在这里插入图片描述

3.2 对原理类图的说明- 即(访问者模式的角色及职责)
  1. Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作(相当于上例评价类的父类)

  2. ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是对每个元素操作实现的部分.(具体评价类

  3. ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素(被增加方法的稳定数据结构,其中有各种不同的元素,用来管理被访问者

  4. Element 定义一个accept 方法,接收一个访问者对象(观众父类

  5. ConcreteElement 为具体元素,实现了accept 方法(具体观众

4. 方案修改

观众类的数据结构稳定的,而评价类是容易变化的。

访问者是评价类,接受访问的是观众类,二者的访问和接受访问的方法都需要在父类或接口中定义。

评价类的接口必须定义每个观众类的访问方法。

观众类接收评价类对象时只需要接受对象的访问方法即可。(注意类型要对应

修改后的UML类图

在这里插入图片描述

Action.java

// 评价类接口:定义每个观众的访问方法
public interface Action {
	public void visitMan(Man man);
	public void visitWoman(Woman woman);
}

Fail.java

// 具体评价类:具体访问者
public class Fail implements Action{

	@Override
	public void visitMan(Man man) {
		System.out.println("man:fail");
	}

	@Override
	public void visitWoman(Woman woman) {
		System.out.println("woman:fail");
	}

}

Success.java

// 具体评价类:具体访问者
public class Success implements Action{

	@Override
	public void visitMan(Man man) {
		System.out.println("man:success");
	}

	@Override
	public void visitWoman(Woman woman) {
		System.out.println("woman:success");
	}

}

Man.java

// 具体观众类
public class Man extends Person {

	@Override
	public void accept(Action a) {
		a.visitMan(this);
	}

}

Woman.java

// 具体观众类
//说明
//1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递Woman中(第一次分派)
//2. 然后Woman 类调用作为参数的 "具体方法" 中方法visitWoman, 同时将自己(this)作为参数
// 传入,完成第二次的分派
public class Woman extends Person {

	@Override
	public void accept(Action a) {
		a.visitWoman(this);
	}

}

ObjectStruture.java

// 具体观众集合类,可以做统一操作
public class ObjectStruture {
	private List<Person> list = new ArrayList<Person>();

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

	public void startAction(Action a) {
		for (Person p : list) {
			p.accept(a);
		}
	}

}

测试Main.java

public class Main {
	public static void main(String[] args) {
		ObjectStruture objs = new ObjectStruture();
		objs.addPerson(new Man());
		objs.addPerson(new Man());
		objs.addPerson(new Woman());
		objs.startAction(new Success());
		
		
	}
}

输出结果

在这里插入图片描述

5. 双分派

所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行,双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型

以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码。

6. 访问者模式的注意事项和细节
6.1 优点
  1. 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高

  2. 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统

6.2 缺点
  1. 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难

  2. 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素

  3. 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

程序员必知的23种设计模式之访问者模式 的相关文章

随机推荐

  • Ubuntu18.04下安装配置SSH服务

    安装ssh工具 1 打开终端键入如下命令 apt get update apt get install openssh server 2 选择Y继续执行 启动SSH服务 1 键入如下命令 etc init d ssh start 注 重启命
  • 机器学习面试题汇总(1~50题)

    机器学习面试题汇总 1 50题 1 深度神经网络预防过拟合的方法 2 SMOTE算法 过采样算法 3 为什么LR 逻辑回归 用sigmoid函数 4 LR损失函数 5 几种神经网络梯度下降方法 6 克莱姆法则 7 各种排序的时间复杂度 8
  • 【JDK】:Java容器框架——同步容器与并发容器

    前面的文章中详细介绍了Java的容器框架 在此基础上 本文对Java中的同步容器与并发容器做一些介绍 fail fast机制 快速报错机制 fail fast 能够防止多个进程同时修改同一个容器的内容 如果在你迭代遍历某个容器的过程中 另一
  • 云服务器维护工作,服务器日常维护工作

    服务器日常维护工作 内容精选 换一换 以昇腾 AI 处理器的PCIe的工作模式进行区分 如果PCIe工作在主模式 可以扩展外设 则称为RC模式 如果PCIe工作在从模式 则称为EP模式 昇腾 AI 处理器的工作模式如下 昇腾310 AI处理
  • Vuex学习总结

    Vuex学习总结 1 概述 说说我的理解 不一定对 由于前端的组件化开发常常难免涉及到组件之间的通信 对于一个小型项目来说 通过父子组件通信就够用了 但是 可能随着项目扩大 存在兄弟组件或者祖孙组件通信的通信 其解决方法通常有两种 中央bu
  • QML VideoOutput填充模式引发的问题

    QML Camera显示不全问题 BUG记录 QML使用Camera组件获取摄像头中的视频 使用VideoOutput显示 显示没有问题 但在Capture时 生成的图片明显比显示的东西多 真实生成的图片 在VideoOutput显示的部分
  • JMJS系统总结系列----JMJS中接口使用的技术(六)

    三同时接口 调用外部的webService A 添加WebService引用 using JMJS BusinessLogic IsSupervisionsServiceReference B 具体调用 public class IsSup
  • MySQL表操作:提高数据处理效率的秘诀(进阶)(1)

    生命不在于相信奇迹 而在于创造奇迹 朱学恒 作者 不能再留遗憾了 专栏 MySQL学习 本文章主要内容 MySQL对表操作进阶 数据库约束 表的设计 新增 后续会更新进阶表的查询 文章目录 前言 1 数据库约束 NULL约束 UNIQUE
  • VTK 测量类Widget的应用 与 vtkDistanceWidget 3D测试 问题

    vtkDistanceWidget 用于在二维平面上测量两点之间的距离 vtkAngleWidget 用于在平面的角度测量 AngleWidget 感觉这都是 2D控件 include
  • nvm use 无法切换npm的解决办法

    nvm正常的安装目录结构 没有正常切换时对应的文件结构 3 解决方法 nvm use 无效 即 nvm use 后执行nvm ls 当前使用的版本前正常是会有 标注 可是nvm use 所有版本前都没有 我当前使用的版本是 6 10 0 我
  • 编写一个类实现银行账户Account的概念

    编写一个类实现银行账户Account的概念 包含的属性有 帐号 密码 存款余额 利率rate 最小余额 定义封装这些属性的方法 账号要自动生成 编写主类 使用银行账户类 输入 输出3个储户的上述信息 考虑 哪些属性可以设计成static属性
  • 【APP自动化测试必知必会】Appium之微信小程序自动化测试

    本节大纲 H5 与小程序介绍 混合 App 元素定位环境部署 混合 App 元素操作 Airtest 测试 App 01 H5与小程序介绍 H5概述 H5 是指第 5 代 HTML 也指用 H5 语言制作的一切数字产品 所谓 HTML 是
  • springboot2整合redis超简单

    依赖
  • [开发

    同时支持中国手机和固定电话号码的正则表达式 const pattern 1 3 9 d 9 0 d 2 3 d 7 8 d 1 5 说明 表示字符串的开始 1 3 9 d 9 0 d 2 3 d 7 8 d 1 5 使用逻辑或 将两个正则表
  • 分布式任务调度框架xxljob2.2.0详细安装使用教程

    分布式任务调度框架xxljob2 2 0详细安装使用教程 简介 概述 特性 总体设计 源码目录介绍 调度数据库 配置 架构设计 设计思想 系统组成 架构图 安装 环境 创建目录 下载 解压 初始化调度数据库 调度数据库表说明 配置部署 调度
  • 安装Visio2010 64bit时提示不能安装32位版本的Office 2010 ,因为您当前已经安装了64位Office产品的解决方法(亲测可行)

    电脑是64位的 从网上找了64位的Visio2010但是安装的时候却提示不能安装32位版本的Office 2010 因为您当前已经安装了64位Office产品 如下 然后就找到了一个解决办法 链接 https pan baidu com s
  • 网页防篡改把服务器顶满,网页防篡改技术

    网页防篡改技术 网页防篡改技术包括时间轮询技术 核心内嵌技术 事件触发技术 文件过滤驱动技术等 时间轮询技术利用网页检测程序 以轮询方式读出要监控的网页 与真实网页相比较 来判断网页内容的完整性 对于被篡改的网页进行报警和恢复 但是由于目前
  • java多态练习_Java课堂练习——多态

    一 ATM机 import java util Scanner abstract class drawmoney public abstract double getMoney double a double n class drawmon
  • dbeaver设置mysql驱动

    dbeaver 默认是没有配置mysql驱动的 方式1 可访问外网 无办公防火墙拦截 直接联网更新驱动即可 方式2 有防火墙拦截 无法更新驱动 驱动属性 编辑驱动 选择本地的mysql驱动文件 可选择maven仓库中的mysql驱动jar
  • 程序员必知的23种设计模式之访问者模式

    文章目录 1 模式引出 测评系统需求 2 传统方案 2 1 传统方式代码 3 访问者模式基本介绍 3 1 UML原理类图 3 2 对原理类图的说明 即 访问者模式的角色及职责 4 方案修改 5 双分派 6 访问者模式的注意事项和细节 6 1