如何以访客模式访问_重新访问了访客模式

如何以访客模式访问

访客模式是面向对象设计中最被高估但又被低估的模式之一。 高估了它,因为它通常选择得太快了( 可能是由建筑宇航员选择的 ),然后以错误的方式添加后,使原本非常简单的设计肿了。 如果您不遵循教科书示例,那么它可能会非常强大,因此被低估了。 让我们详细看一下。

问题1:命名

它的最大缺陷(在我看来)是命名本身。 “访客”模式。 当我们用google搜索它时,我们很可能会在相关的Wikipedia文章中找到自己,显示类似这样的有趣图像:

维基百科访客模式示例

对。 对于我们98%的人在日常软件工程工作中对车轮,发动机和车身的思考而言,这是显而易见的,因为我们知道,机修工向我们收取几千美元的汽车维修费用后,我们将首先访问车轮,然后是发动机,然后最终访问我们的钱包并接受我们的现金。 如果我们很不幸,他也会在我们工作时拜访我们的妻子,但她永远不会接受那个忠实的灵魂。

但是,解决工作中其他问题的2%的人呢? 就像我们为电子银行系统,证券交易所客户,Intranet门户等编写复杂的数据结构时一样。为什么不将访客模式应用于真正的分层数据结构? 喜欢文件夹和文件? (好的,毕竟不是那么复杂)

好的,所以我们将“访问”文件夹,每个文件夹都将让其文件“接受”为“访客”,然后让访问者也“访问”这些文件。 什么?? 汽车让其零件接纳访客,然后让访客自我参观吗? 这些条款具有误导性。 它们是通用的,适合设计模式。 但是它们会杀死您的现实设计,因为没有人会考虑“接受”和“访问”,而实际上您是在读/写/删除/修改文件系统。

问题2:多态性

当应用于错误的情况时,这比命名引起的头痛甚至更大。 访客为什么在地球上认识其他所有人? 为什么访问者需要针对层次结构中每个涉及元素的方法? 多态和封装要求将实现隐藏在API的后面。 (我们数据结构的)API可能以某种方式实现了复合模式 ,即其部分继承自公共接口。 好吧,当然,车轮不是汽车,我妻子也不是机械师。 但是当我们采用文件夹/文件结构时,它们不是全部都是java.util.File对象吗?

了解问题

实际的问题不是访问代码的命名和可怕的API详细程度,而是对模式的误解。 这不是最适合访问带有许多不同类型对象的大型复杂数据结构的模式。 这种模式最适合于访问几种不同类型的简单数据结构,但要访问数百名访问者。 取文件和文件夹。 那是一个简单的数据结构。 您有两种类型。 一个可以包含另一个,两者共享一些属性。 各种访客可能是:

  • CalculateSizeVisitor
  • FindOldestFileVisitor
  • DeleteAllVisitor
  • FindFilesByContentVisitor
  • ScanForVirusesVisitor
  • …你给它起名字

我仍然不喜欢命名,但是这种模式在这种范例中可以完美地工作。

那么,访客模式何时“错误”?

我想以jOOQ QueryPart结构为例。 其中有很多,可以对各种SQL查询结构进行建模,从而使jOOQ可以构建和执行任意复杂度SQL查询。 让我们举几个例子:

  • 健康)状况
    • 组合条件
  • 领域
    • 表格栏位
  • 字段清单

还有更多。 它们中的每一个都必须能够执行两个操作:渲染SQL和绑定变量。 那将使两个访问者每个人都知道……40-50种类型……? 也许在遥远的未来,jOOQ查询将能够呈现JPQL或其他某种查询类型。 那将使3位访客面对40-50种类型。 显然,在这里,经典的访客模式是一个错误的选择。 但是我仍然想“访问” QueryPart,将渲染和绑定委托给较低的抽象级别。

那么如何实现呢?

很简单:坚持使用复合模式! 它允许您向每个人都必须实现的数据结构添加一些API元素。

因此,凭直觉,第一步就是

interface QueryPart {// Let the QueryPart return its SQLString getSQL();// Let the QueryPart bind variables to a prepared// statement, given the next bind index, returning// the last bind indexint bind(PreparedStatement statement, int nextIndex);
}

使用此API,我们可以轻松地抽象SQL查询并将职责委派给较低级别​​的工件。 例如,一个BetweenCondition。 它负责在[lower]和[upper]条件之间正确排序[field]的各个部分,语法正确地呈现SQL,并将部分任务委派给其child-QueryParts:

class BetweenCondition {Field field;Field lower;Field upper;public String getSQL() {return field.getSQL() + ' between ' +lower.getSQL() + ' and ' +upper.getSQL();}public int bind(PreparedStatement statement, int nextIndex) {int result = nextIndex;result = field.bind(statement, result);result = lower.bind(statement, result);result = upper.bind(statement, result);return result;}
}

另一方面,BindValue主要负责变量绑定

class BindValue {Object value;public String getSQL() {return '?';}public int bind(PreparedStatement statement, int nextIndex) {statement.setObject(nextIndex, value);return nextIndex + 1;}
}

结合起来,我们现在可以轻松创建这种形式的条件: 在之间? 和?。 当实现更多QueryPart时,我们还可以想象像MY_TABLE.MY_FIELD BETWEEN吗? 与(SELECT?FROM DUAL),在适当的字段实现可用时。 这就是使复合模式如此强大,通用的API和许多封装行为的组件,从而将行为的一部分委派给子组件的原因。

第2步负责API的演变

到目前为止,我们已经看到的复合模式非常直观,但是功能非常强大。 但是迟早,我们将需要更多的参数,因为我们发现要将状态从父级QueryPart传递给子级QueryParts。 例如,我们希望能够内联某些子句的绑定值。 也许某些SQL方言不允许BETWEEN子句中的绑定值。 如何使用当前的API处理该问题? 扩展它,添加一个“布尔内联”参数? 没有! 这就是发明访客模式的原因之一。 为了使复合结构元素的API保持简单(只需实现“接受”)。 但是在这种情况下,用“上下文”替换参数比实现真正的访客模式好得多:

interface QueryPart {// The QueryPart now renders its SQL to the contextvoid toSQL(RenderContext context);// The QueryPart now binds its variables to the contextvoid bind(BindContext context);
}

上面的上下文将包含这样的属性(setter和render方法返回上下文本身,以允许方法链接):

interface RenderContext {// Whether we're inlining bind variablesboolean inline();RenderContext inline(boolean inline);// Whether fields should be rendered as a field declaration// (as opposed to a field reference). This is used for aliased fieldsboolean declareFields();RenderContext declareFields(boolean declare);// Whether tables should be rendered as a table declaration// (as opposed to a table reference). This is used for aliased tablesboolean declareTables();RenderContext declareTables(boolean declare);// Whether we should cast bind variablesboolean cast();// Render methodsRenderContext sql(String sql);RenderContext sql(char sql);RenderContext keyword(String keyword);RenderContext literal(String literal);// The context's 'visit' methodRenderContext sql(QueryPart sql);
}

BindContext也是如此。 如您所见,该API相当可扩展,可以添加新属性,还可以添加其他常见的呈现SQL的方法。 但是BetweenCondition不必放弃有关如何呈现其SQL以及是否允许绑定变量的封装知识。 它会将这些知识保留给自己:

class BetweenCondition {Field field;Field lower;Field upper;// The QueryPart now renders its SQL to the contextpublic void toSQL(RenderContext context) {context.sql(field).keyword(' between ').sql(lower).keyword(' and ').sql(upper);}// The QueryPart now binds its variables to the contextpublic void bind(BindContext context) {context.bind(field).bind(lower).bind(upper);}
}

另一方面,BindValue主要负责变量绑定

class BindValue {Object value;public void toSQL(RenderContext context) {context.sql('?');}public void bind(BindContext context) {context.statement().setObject(context.nextIndex(), value);}
}

结论:将其命名为“上下文模式”,而不是“访客模式”

快速跳到访客模式时要小心。 在许多情况下,您将使设计变得肿,从而使其完全难以阅读且难以调试。 这里是要记住的规则,总结如下:

  1. 如果您有许多访问者并且数据结构相对简单(几种类型),那么访问者模式可能就可以了。
  2. 如果您有很多类型,并且访问者组相对较少(很少有行为),则访问者模式是过大的,请坚持使用复合模式
  3. 为了简化API的演变,请将您的复合对象设计为具有采用单个上下文参数的方法。
  4. 突然之间,您将再次发现自己处于“几乎访问者”模式,其中context = visitor,“ visit”和“ accept” =“您专有的方法名称”

同时,“上下文模式”与“复合模式”一样直观,而与“访问者模式”一样强大,结合了两个方面的优势。

参考: “访问者模式”是我们的JCG合作伙伴 Lukas Eder在JAVA,SQL和JOOQ博客上再次访问的 。


翻译自: https://www.javacodegeeks.com/2012/05/visitor-pattern-re-visited.html

如何以访客模式访问

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

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

相关文章

【数据挖掘导论】——数据质量

数据质量数据挖掘使用的数据一般是为其它用途收集或者收集的时候还没有明白目的。因此数据经常不能在数据的源头控制质量。为了避免数据质量的问题,所以数据挖掘着眼于两个方面:数据质量问题的检測和纠正(数据清理);使…

Spark学习笔记(7)---Spark SQL学习笔记

Spark SQL学习笔记 Spark SQL学习笔记设计到很多代码操作,所以就放在github, https://github.com/yangtong123/RoadOfStudySpark/blob/master/src/com/spark/sql/Readme.md其中包括了对Spark2.0的新特性的介绍,包括SparkSession, DataSet等转载于:https:…

性能实战(一) --- clock_gettime造成系统整体cpu过高定位过程

问题背景 有一台linux服务器测试环境cpu经常到达80%,造成系统卡顿,部分功能不可用. 分析步骤 1.使用perf制作cpu火焰图 通过制作cpu火焰图,发现很多进程都存在大量的clock_gettime系统调用. 2. 使用bcc工具funclatency`进一步查看clock_gettime的调用次数 # /usr/share/bc…

玩透个人所得税

每次拿着工资条的时候,总有个代扣个税这么一项,不知道你们有没有想过这到底是怎样计算得出来的。下面我就给你们普及一下这个知识。 个人所得税 个人所得税是对个人(自然人)取得的各项所得征收的一种所得税。个人所得税…

哔哩哔哩swot分析_哔哩哔哩2020校园招聘游戏运营笔试真题

在前不久《英雄联盟》S9世界大赛总决赛上,中国FPX战队以3:0横扫欧洲G2最终夺得S9世界冠军,国内玩家万分激动。总决赛刚结束不久,B站以8亿元价格拍得《英雄联盟》全球总决赛中国地区三年独家直播版权。作为主打年轻人群体起家的产品…

P2280 [HNOI2003]激光炸弹

P2280 [HNOI2003]激光炸弹 题目描述 输入输出格式 输入格式:输入文件名为input.txt 输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示 xi,yi ,vi 。 输出格式:输出文件名为output.…

在Spring Boot中使用配置元数据来配置您的配置

Spring Boot 1.3.0中发布了许多更新,但是其中一个对我很突出,因为我以前并不了解此更新,它的状态使其成为一项真正有用的功能(不幸的是,撰写本文时仅在Spring Boot中可用)这个)。 我正在谈论配置…

jsp实现数据禁用和只读

jsp实现数据禁用和只读

java事务不生效场景_讲一下,我最近帮忙面试的几个“高级”JAVA,真的心累

最近看了很多简历,很多候选人年限不小,但是想着也不能通过简历就直接否定一个人,何况现在大环境越来 越难,大家找工作也不容易,于是就打算见一见。在沟通中发现,由于年限不小,他们的定位基本都是…

antlr 教程_ANTLR教程– Hello Word

antlr 教程Antlr代表另一种语言识别工具。 该工具能够为任何计算机语言生成编译器或解释器。 除了明显的用途(例如需要解析一种真正的“大型”编程语言,例如Java,PHP或SQL)外,它还可以帮助执行更小,更常见的…

Java中使用ArrayList的10个示例–教程

Java中的ArrayList是HashMap之后最常用的集合类。 Java ArrayList表示一个可自动调整大小的数组,并用于代替数组。 由于创建数组后我们无法修改数组的大小,因此我们更喜欢在Java中使用ArrayList,一旦数组满了,它就会自动调整大小。…

[SoapUI] How to create a random UUID in each Request's Headers

${java.util.UUID.randomUUID()} is OK 转载于:https://www.cnblogs.com/MasterMonkInTemple/p/7159589.html

设置Tomcat管理员用户名和密码

http://dove19900520.iteye.com/blog/1774980 今天tomcat出点问题,然后我就想进入tomcat manager看看,结果怎么输入密码都不行,后来网上查了查才找到配置管理员用户名和密码的正确方式,在此记录一下: 要想配置管理员用…

怎么把两个div一左一右放

怎么把两个div一左一右放1.代码 <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>Title</title> </head> <body> <div style"width:150px;height:50px;margin:0;…

为Java + STANDARD值引入Cucumber

作为软件开发人员&#xff0c;我们都有最喜欢的工具来使我们成功。 许多人在上手时就很适合这份工作&#xff0c;但很快就不见了。 其他人则需要太多的设置和培训才能“将脚趾浸入水中”&#xff0c;只是为了找出自己是否是正确的工具即可。 Cucumber JVM是一个测试框架&#…

一个逼格很低的appium自动化测试框架

Github地址: https://github.com/wuranxu 使用说明 1. 安装配置Mongo数据库 下载地址 mongo是用来存放元素定位的&#xff0c;截图如下: 通过case_id区分每个case的元素定位 里面提供了value, method和text字段&#xff0c;分别作用是定位的值&#xff0c;定位的方法和要输入的…

Port already in use: 1099;

Port already in use: 1099;1.使用命令:netstat -aon|findstr 1099 找出占用1099端口的进程; 2.关闭占用该端口的进程:taskkill -f -pid 9336;

一款好用且免费的语句分析工具Plan Explorer

在调优过程中的查询语句优化阶段&#xff0c;分析语句的执行计划是必经之路&#xff0c;一款好的执行计划分析工具确实可以帮助我们事半功倍 小贴士&#xff1a;Plan Explorer是将Plan Explorer 专业版与免费版整合在一起发布的全新、完全免费版。微软的数据库专家和开发人员也…

前端如何发ModelAndView的请求

前端如何发ModelAndView的请求1.在Web.xml指定用作工具的跳转页面

带有Gluon Ignite和Dagger的JavaFX中的依赖注入

依赖注入抽象框架Gluon Ignite在几个流行的依赖注入框架&#xff08;例如Spring&#xff0c;Dagger和Guice&#xff09;上创建了一个通用抽象。 目前&#xff0c;Gluon 页面仅包含一个示例&#xff0c;该示例使用Gluon Ignite和Google Guice作为依赖注入框架&#xff0c;我想尝…