访客模式 无痕模式 区别_旧访客设计模式的新生活

访客模式 无痕模式 区别

介绍

访客 [1、2]是众所周知的经典设计模式。 有很多资源对其进行了详细说明。 在不深入研究实现的情况下,我将简要提醒一下该模式的概念,解释其优点和缺点,并提出一些可以使用Java编程语言轻松应用于其的改进。

古典访客

[Visitor] 允许在运行时将一个或多个操作应用于一组对象,从而将操作与对象结构分离。

(四人帮)

该模式基于通常称为的接口。 Visitable具有由模型类和一组来实现Visitors实现为每个相关的模型类方法(算法)。

 public interface Visitable { public void accept(Visitor visitor);  }  public class Book implements Visitable { ....... @Override public void accept(Visitor visitor) {visitor.visit( this )}; .......  }  public class Cd implements Visitable { ....... @Override public void accept(Visitor visitor) {visitor.visit( this )}; .......  }  Visitor { interface Visitor { public void visit(Book book); public void visit(Magazine magazine); public void visit(Cd cd);  } 

现在我们可以实现各种visitors ,例如

  • PrintVisitor是提供打印Visitable
  • DbVisitor其存储在数据库中的DbVisitor
  • 将其添加到购物车的ShoppingCart

等等

访客模式的缺点

  1. visit()方法的返回类型必须在设计时定义。 实际上,在大多数情况下,这些方法都是void
  2. accept()方法的实现在所有类中都是相同的。 显然,我们更喜欢避免代码重复。
  3. 每次添加新的模型类时,每个visitor必须更新,因此维护变得很困难。
  4. 对于某些visitor ,某些模型类不可能有可选的实现。 例如,可以通过电子邮件将软件发送给买方,而不能发送牛奶。 但是,两者都可以使用传统的邮寄方式递送。 因此, EmailSendingVisitor不能实现方法visit(Milk)但可以实现visit(Software) 。 可能的解决方案是引发UnsupportedOperationException但调用者无法提前知道在调用该方法之前将引发此异常。

改进经典访客模式

返回值

首先,让我们将返回值添加到Visitor接口。 通用定义可以使用泛型来完成。

 public interface Visitable { public <R> R accept(Visitor<R> visitor);  }  interface Visitor<R> { public R visit(Book book); public R visit(Magazine magazine); public R visit(Cd cd);  } 

好吧,这很容易。 现在,我们可以将任何能带来价值的Visitor应用于我们的图书。 例如, DbVisitor可能返回DB(整数)中已更改记录的数量,而ToJson访问者可能会将我们对象的JSON表示形式返回为String。 (该示例可能不太有机,在现实生活中,我们通常使用其他技术将对象序列化为JSON,但就其在理论上可以使用Visitor模式而言,已经足够了)。

默认实现

接下来,让我们感谢Java 8在接口内保留默认实现的能力:

 public interface Visitable<R> { default R accept(Visitor<R> visitor) { return visitor.visit( this ); }  } 

现在,实现Visitable类本身不必实现>visit() :在大多数情况下,默认实现就足够了。

上面建议的改进解决了缺点#1和#2。

单访客

让我们尝试应用进一步的改进。 首先,让我们定义接口MonoVisitor如下:

 public interface MonoVisitor<T, R> { R visit(T t);  } 

名称Visitor被更改为MonoVisitor以避免名称冲突和可能的混淆。 通过这本书, visitor定义了许多重载方法visit() 。 他们每个人都为每个Visitable接受不同类型的参数。 因此,根据定义, Visitor不能是通用的。 必须在项目级别上定义和维护它。 MonoVisitor仅定义一种方法。 泛型保证了类型安全。 即使使用不同的通用参数,单个类也无法多次实现相同的接口。 这意味着即使将MonoVisitor多个实现分组为一个类,我们也必须保留它们。

功能参考,而不是访客

由于MonoVisitor只有一种业务方法,因此我们必须为每个模型类创建实现。 但是,我们不想创建单独的顶级类,而是希望将它们分组为一个类。 这个新的visitor在各种Visitable类与java.util.Function实现之间持有Map,并将visit()方法的调用分派给特定的实现。

因此,让我们看一下MapVisitor。

 public class MapVisitor<R> implements Function<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> { private final Map<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> visitors; MapVisitor(Map<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> visitors) { this .visitors = visitors; } @Override public MonoVisitor apply(Class clazz) { return visitors.get(clazz); }  } 

MapVisitor

  • 实现Function

    为了检索特定的实现(为便于阅读,此处省略了完整的泛型;有关详细定义,请查看代码段)

  • 接收映射中类和实现之间的映射
  • 检索适合给定类的特定实现

MapVisitor具有程序包专用的构造函数。 使用特殊构建器完成的MapVisitor初始化非常简单且灵活:

 MapVisitor<Void> printVisitor = MapVisitor.builder(Void. class ) .with(Book. class , book -> {System.out.println(book.getTitle()); return null ;}) .with(Magazine. class , magazine -> {System.out.println(magazine.getName()); return null ;}) .build(); 

MapVisitor的用法类似于传统的Visitor

 someBook.accept(printVisitor);  someMagazine.accept(printVisitor); 

我们的MapVisitor还有一个好处。 必须实现在传统访问者的接口中声明的所有方法。 但是,通常无法实现某些方法。

例如,我们想要实现演示动物可以执行的各种动作的应用程序。 用户可以选择一种动物,然后通过从菜单中选择特定的动作来使它做某事。

这是动物名单: Duck, Penguin, Wale, Ostrich 这是动作列表: Walk, Fly, Swim. 我们决定按操作设置访问者: WalkVisitor, FlyVisitor, SwimVisitor 。 鸭子可以做所有三个动作,企鹅不能飞,瓦尔只能游泳, 鸵鸟只能行走。 因此,如果用户试图使Wale走路或Ostrich飞行,我们决定抛出异常。 但是这种行为不是用户友好的。 确实,用户只有在按下操作按钮时才会收到错误消息。 我们可能更希望禁用不相关的按钮。 MapVisitor允许这样做而无需其他数据结构或代码重复。 我们甚至不必定义new或扩展任何其他接口。 相反,我们更喜欢使用标准接口java.util.Predicate

 public class MapVisitor<R> implements Function<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>>, Predicate<Class<? extends Visitable>> { private final Map<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> visitors; ............... @Override public boolean test(Class<? extends Visitable> clazz) { return visitors.containsKey(clazz); }  } 

现在我们可以调用函数test()来定义是否必须启用或显示选定动物的操作按钮。

github上提供了此处使用的示例的完整源代码。

结论

本文演示了一些改进,这些改进使旧的Visitor模式变得更加灵活和强大。 建议的实现方式避免了实现经典的Vistor模式所需的某些样板代码。 以下是上述改进的简要清单。

  1. 这里描述的Visitor visit()方法可以返回值,因此可以实现为纯函数[3],该函数有助于将Visitor模式与功能编程范例结合起来。
  2. 将整体Visitor接口分成单独的块可以使其更加灵活,并简化代码维护。
  3. 可以在运行时使用构建器来配置MapVisitor ,因此它可能会更改其行为,具体取决于仅在运行时已知且在开发过程中不可用的信息。
  4. 具有不同返回类型的访问者可以应用于相同的Visitable类。
  5. 在接口中完成的方法的默认实现将删除通常用于典型的Visitor实现的许多样板代码。

参考资料

  1. 维基百科
  2. 区域
  3. 纯函数的定义 。

翻译自: https://www.javacodegeeks.com/2019/03/new-life-old-visitor-design-pattern.html

访客模式 无痕模式 区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/332797.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML的图片标签img的图片地址

使用相对路径&#xff0c;例如&#xff0c;当前目录中的子目录 image 中的图片 1.jpg&#xff0c;可以写成 ./image/1.jpg&#xff0c;其中 ./ 表示当前目录&#xff0c;可以省略不写。 <img src"image/1.jpg"/>实际上HTML文档数据写入到 Response 中时&#…

java swing 左上角图标_科学网—Matlab: 学习GUI(修改窗口左上角图标而不warning) - 刘磊的博文...

网上常用的方法&#xff1a;if ~isdeployednewIconjavax.swing.ImageIcon(.piciap.jpg);elsenewIconjavax.swing.ImageIcon(iap.jpg);endjFrame get(hObject,javaframe);jFrame.setFigureIcon(newIcon);运行后warning&#xff1a;Warning: figure JavaFrame property will be …

pbfunc外部扩展函数_从外部CorDapp扩展和覆盖流

pbfunc外部扩展函数Corda 4于上周&#xff08;2月21日&#xff09;发布&#xff0c;带来了大量的新功能&#xff0c;使Corda更加令人愉快。 老实说&#xff0c;我有点假设有很多新功能。 我快速浏览了变更日志&#xff0c;主要是看到我的贡献被引用&#xff0c;但是我记得看到很…

表单项标签的input标签的单选框(radio)

<input type"radio" name"gender" value"male"> 男 <input type"radio" name"gender" value"female"> 女1.单选框需要注意的是&#xff0c;如果是属于一组的选项&#xff0c;那么 name 属性的值必须相…

java 6 update 3_Java(TM) 6 Update(java运行环境) V 6.0.450.6 官方版

Java(TM) 6 Update是个JAVA辅助软件&#xff0c;它具备高度的安全性以及跨平台的特性&#xff0c;能让你的电脑或手机运行java程序&#xff0c;用户可使用Java(TM) 6 Update来搭建甚至运行整个ava程序&#xff0c;注意&#xff1a;卸载后JAVA环境的程序将无法运行。过去很可能会…

jep290涉及jdk版本_JDK 9 / JEP 280:字符串串联永远不会相同

jep290涉及jdk版本JEP 280 &#xff08;“ Indify String Concatenation”&#xff09;是与JDK 9一起实现的&#xff0c;根据其“摘要”部分&#xff0c;“更改了javac生成的静态String concatenation字节码序列&#xff0c;以使用对JDK库函数的invokedynamic调用。 ” 通过查看…

label标签/标记

label 标签用于指定表单项的文字描述信息&#xff0c;如下所示&#xff1a; <label for"username">用户名称&#xff1a;</label> <input id"username" name"username">label 标签 的 for 属性指定的值与 input 标签的 id 属…

矩阵累积相乘 java_累积:轻松自定义Java收集器

矩阵累积相乘 javaAccumulative是针对Collector<T, A, R>的中间累积类型A提出的接口Collector<T, A, R>以使定义自定义Java Collector更加容易。 介绍 如果您曾经使用过Java Stream &#xff0c;那么很可能会使用了一些Collector &#xff0c;例如&#xff1a; C…

java socket 传输压缩文件_java基于socket传输zip文件功能示例

本文实例讲述了java基于socket传输zip文件的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;服务器端程序&#xff1a;import java.io.*;import java.net.*;import java.io.BufferedInputStream;public class SocketServer {ServerSocket ssnull;Socket snull;Dat…

css的学习

文章目录内联样式内部样式外部样式CSS语法格式选择器基本选择器ID 选择器元素选择器类选择器不带标签名带标签名通用选择器并集选择器选择器的优先级扩展选择器组合选择器属性选择器伪类选择器伪元素选择器css 是 Cascading Style Sheets&#xff0c;层叠样式表。 层叠含义是多…

java throws catch_java异常处理throws throw try-catch实例

java异常处理throws throw try-catch实例。throw用于方法中&#xff0c;我们可以预见的错误。比如&#xff1a;if(age<0){Exception e new Exception();//创建异常对象throw e;//抛出异常}在java代码中如果发生异常的话&#xff0c;jvm会抛出异常对象&#xff0c;导致程序代…

java 开发人员工具_每个Java开发人员都应该知道的10个基本工具

java 开发人员工具大家好&#xff0c;我们已经到了2019年的第二个月&#xff0c;我相信你们所有人都已经制定了关于2019年学习以及如何实现这些目标的目标。 我一直在撰写一系列文章&#xff0c;为您提供一些知识&#xff0c;使您可以学习和改进以成为2019年更好的全方位开发人…

SpringBoot2.x+mybatis plus3.x集成Activit7版本

文/朱季谦 在Activiti6版本当中&#xff0c;若要集成到Springboot里&#xff0c;需要写一些额外的配置类&#xff0c;我曾经在Activiti工作流框架学习笔记&#xff08;二&#xff09;之springboot2.0整合工作流Activiti6.0一文当中总结过相关配置过程&#xff0c;感兴趣的同学…

camel apache_Apache Camel 3的工作终于开始了

camel apache我们正在开始Apache Camel 3的工作。 我们正在多方面努力改善骆驼并引入新功能。 Guillaume Nodet实际上是在10月初开始了第一项工作&#xff0c;他通过清理代码库&#xff0c;删除不推荐使用的代码和组件&#xff0c;改进了路由引擎和核心中的其他内部组件开始了…

(前端开发)表格中的行全选、全不选、反选以及数据行背景色变换的示例代码

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>表格数据行全选/全不选/反选的示例</title><style>table {border: 1px solid;width: 500px;margin-left: 30%;}th, td {text-align: cente…

future java 多线程_Java多线程之Future与FutureTask

一&#xff1a;Future在使用实现Callable创建线程时&#xff0c;call()方法是有返回值的。那么&#xff0c;我们在编程时用什么来代表这个 线程执行后才能返回的未来结果 呢&#xff1f;那就是 Future类型。顾名思义&#xff0c;Future——未来值&#xff0c;我们用这个未来值来…

switch字符串jdk_从JDK 12删除原始字符串文字

switch字符串jdk已经提出从JDK 12中删除原始字符串文字&#xff08;预览&#xff09; &#xff08;它将在12月13日进入Rampdown第一阶段 &#xff09;。 Brian Goetz撰写了删除此预览功能的动机的详细说明 &#xff08; JEP 326 &#xff09;。 在Java subreddit上也对此进行了…

win7旗舰版安装不了python_怎样在Win7 64位旗舰版安装Python+Eclipse开发环境

自从上周抛弃了WinXP转而安装了Win7&#xff0c;64位后&#xff0c;尝试安装PythonEclipse遇到了一点小问题。现在已经解决&#xff0c;将安装顺序记录如下&#xff0c;供参考。Setp1&#xff0c;到ORACLE网站下载64位的JDK。http://www.oracle.com/technetwork/java/javase/do…

MacOS下的取色器/拾色器推荐

文章目录ColorSnapper2ColorSlurp 不错&#xff0c;App Store 可以下载SipChromaColor PickerPixeur&#xff08;推荐&#xff09;操作说明ColorSnapper2 ColorSlurp 不错&#xff0c;App Store 可以下载 Sip Chroma Color Picker Pixeur&#xff08;推荐&#xff09; 体…

查看oracle会话和进程_带有Oracle Digital Assistant和Fn Project的会话式UI

查看oracle会话和进程在这里和那里&#xff0c;我们看到了无数的预测&#xff0c;很快聊天机器人将在用户与其系统之间的通信中扮演关键角色。 我没有水晶球&#xff0c;也不想等待这个“很快”&#xff0c;所以我决定现在就使这些预言成真&#xff0c;看看它是什么样。 我正在…