代理模式和动态代理模式_代理模式介绍

代理模式和动态代理模式

  • 代表:被选中或当选为他人投票或代理的人– Merriam-Webster 。
  • 委托模式:在软件工程中,委托模式是面向对象编程中的一种设计模式,其中,一个对象而不是执行其陈述的任务之一,而是将该任务委托给一个关联的辅助对象Wikipedia 。
  • 使事情尽可能简单,但不要简单- 爱因斯坦 Albert Einstein)释义

Spring Batch是Enterprise Java工具箱中的重要工具。 它提供了开箱即用的强大功能,尤其是从不同来源读取和写入数据时。 我们在此博客中提供了几篇介绍Spring Batch的文章。 如果您不熟悉Spring Batch和Reader,Processor,Writer Tasklet,请花点时间回顾一下。

我上面使用的措辞对我来说很重要。 我尝试做的一件事就是保持我提供的代码尽可能可维护。 我希望它能正常工作,但是我今天签入的代码将在以后某个日期由某些人维护。 保持代码尽可能简单是确保代码易于维护的一种方法。

那么,当您必须处理复杂的数据源时会发生什么呢?

我们发现,经常需要处理的输入文件并不像每行一个记录那么简单。 通常,文件中有多行描述一个记录。

例如:

HKaren Traviss
LAB00KW3VG2G
LI0345478274
LI0345511131
F00000003
HJim Butcher
LI0451457811
F00000001
HDave Duncan
LI0380791277
LI0345352912
F00000002
HRik Scarborough
LI9999999999
F00000001

在这里,我们有一个文件,其中包含十五行中的四个记录。 每条记录均以页眉行开头,包含一个或多个正文行,并以页脚结尾。 标头包含线型(标头为H)和名称。 该行还包含线型(L),查找类型(在此示例中为ISBN或Amazon代码)以及查找书本的键。 页脚再次包含线型和此块中的记录数。

使用标准的读取器,将读取每一行,然后传递给处理器,然后处理器必须确定处理的是哪种类型的行。 然后,处理器在处理每个正文行时必须保留每个标头中的信息,直到处理了页脚。 然后,编写者将必须知道处理器发送的每一行,以及是否应将其写入。 这在某种程度上很复杂,因为多个对象必须知道如何读取文件,而不是处理器只关心单个对象,而编写器只关心编写给定的对象。

相反,让我们将Delegate模式引入Reader并让其处理创建整个记录的过程。 由于我们具有来自多行的信息以及用于创建每条记录的页眉和页脚,因此我们将必须向处理者传递记录列表。 你们当中的观察者会注意到,每个记录都包含一个ISBN或Amazon图书符号,并且可以用来查找作者(也包含在标题中)。 在现实生活中,这种冗余可能也不会发生。

让我们将输出包装在另一个对象中,以使其更易于使用。

public class OrderReaderStep implements ItemReader<OrderList> {private static final Logger logger = LoggerFactory.getLogger(OrderReaderStep.class);private FlatFileItemReader
<FieldSet> delegate;private static final String FOOTER = "F*";private static final String BODY = "L*";private static final String HEADER = "H*";@BeforeSteppublic void beforeStep(StepExecution stepExecution) {delegate = new FlatFileItemReader<>();delegate.setResource(new ClassPathResource("orders.txt"));final DefaultLineMapper
<FieldSet> defaultLineMapper = new DefaultLineMapper<>();final PatternMatchingCompositeLineTokenizer orderFileTokenizer = new PatternMatchingCompositeLineTokenizer();final Map<String, LineTokenizer> tokenizers = new HashMap<>();tokenizers.put(HEADER, buildHeaderTokenizer());tokenizers.put(BODY, buildBodyTokenizer());tokenizers.put(FOOTER, buildFooterTokenizer());orderFileTokenizer.setTokenizers(tokenizers);defaultLineMapper.setLineTokenizer(orderFileTokenizer);defaultLineMapper.setFieldSetMapper(new PassThroughFieldSetMapper());delegate.setLineMapper(defaultLineMapper);delegate.open(stepExecution.getExecutionContext());}@AfterSteppublic void afterStep(StepExecution stepExecution) {delegate.close();}@Overridepublic OrderList read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {logger.info("start read");OrderList record = null;FieldSet line;List<Order> bodyList = new ArrayList<>();while ((line = delegate.read()) != null) {String prefix = line.readString("lineType");if (prefix.equals("H")) {record = new OrderList();record.setName(line.readString("name"));} else if (prefix.equals("L")) {Order order = new Order();order.setLookup(line.readString("lookupKey"));order.setLookupType(line.readString("keyType"));bodyList.add(order);} else if (prefix.equals("F")) {if (record != null) {if (line.readLong("count") != bodyList.size()) {throw new ValidationException("Size does not match file count");}record.setOrders(bodyList);}break;}}logger.info("end read");return record;}private LineTokenizer buildBodyTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 2), // keyTypenew Range(3, 12) // lookup key});tokenizer.setNames(new String[]{ //"lineType","keyType","lookupKey"}); //tokenizer.setStrict(false);return tokenizer;}private LineTokenizer buildFooterTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 9) // count});tokenizer.setNames(new String[]{ //"lineType","count"}); //tokenizer.setStrict(false);return tokenizer;}private LineTokenizer buildHeaderTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 20), // name});tokenizer.setNames(new String[]{ //"lineType","name"}); //tokenizer.setStrict(false);return tokenizer;}}

此Reader实现ItemReader接口。 这为我们提供了一个由作业调用的read方法,直到它返回null或发生错误时引发异常。 在我们的Reader中,我们声明另一个Reader,这是一个FlatFileItemReader。 这是我们的代表,即为我们执行功能所选择的对象。 我们的read方法将以委托的读取为循环,直到读取Footer。 然后它将整个记录捆绑到其包装器中,并将其传递给处理器。

必须先打开委托阅读器,然后才完成使用。 我必须在此处将它初始化并在此处进行设置,因此在BeforeStep中在此处打开它。 我也可以将包含的阅读器实现为ItemStreamReader,并使用Interface给我们的open,close以及update方法。

将简化的对象返回给Processor可以使我们大大简化Processor:

@Override
public List<BookList> process(OrderList orderList) throws Exception {logger.info("process");List<BookList> books = new ArrayList<>();for (Order order : orderList.getOrders()) {BookList bl = doProcessing(orderList.getName(), order);books.add(bl);}return books;
}

doProcessing方法可以包含此Job的业务逻辑,并且需要创建一个有效的BookList对象。 由于我们正在处理多个记录,因此该过程将创建多个可返回的BookList,并将其传递给Writer。 我将留给您填写该对象的其余部分,但这只是一个标准的ItemProcessor。 处理器不必在调用之间保留记录信息,因此程序员可以专注于业务逻辑。

我们的编写器实现ItemStreamWriter。 这给我们提供了比ItemWriter更多的方法,但是,如果您希望像使用Reader一样使用ItemWriter,请确保在BeforeStep中打开Delegate,在AfterStep中将其关闭。

在Writer中使用委托使我们能够遍历Writer从Reader和Process收到的List。

public class ListWriter implements ItemStreamWriter<List<BookList>> {private static final Logger logger = LoggerFactory.getLogger(ListWriter.class);private FlatFileItemWriter<BookList> delegate;@BeforeSteppublic void beforeStep(StepExecution stepExecution) {delegate = new FlatFileItemWriter<>();delegate.setResource(new FileSystemResource("booklist.csv"));delegate.setShouldDeleteIfEmpty(true);delegate.setAppendAllowed(true);DelimitedLineAggregator<BookList> dla = new DelimitedLineAggregator<>();dla.setDelimiter(",");BeanWrapperFieldExtractor<BookList> fieldExtractor = new BeanWrapperFieldExtractor<>();fieldExtractor.setNames(new String[]{"bookName", "author"});dla.setFieldExtractor(fieldExtractor);delegate.setLineAggregator(dla);}@Overridepublic void close() throws ItemStreamException {delegate.close();}@Overridepublic void open(ExecutionContext ec) throws ItemStreamException {delegate.open(ec);}@Overridepublic void update(ExecutionContext ec) throws ItemStreamException {delegate.update(ec);}@Overridepublic void write(List<? extends List<BookList>> list) throws Exception {logger.info("write");for (List<BookList> bookList : list) {delegate.write(bookList);}}}

这为我们提供了以下输出:

Going Grey,Karen Traviss
Hard Contact,Karen Traviss
501st,Karen Traviss
Storm Front,Jim Butcher
Lord of the Fire Lands,Dave Duncan
The Reluctant Swordsman,Dave Duncan
Wolfbrander Series Unpublished,Rik Scarborough

那么,如果稍微复杂一点并且输入文件不包含页脚,会发生什么呢?

逻辑记录仍然从标题行开始,但在下一个标题之前的行结束。 在我们之前的示例中,系统必须先读取下一行,然后才能知道该行已完成,然后具有一些复杂的逻辑来保留该信息以用于下一轮。

HKaren Traviss
LAB00KW3VG2G
LI0345478274
LI0345511131
HJim Butcher
LI0451457811
HDave Duncan
LI0380791277
LI0345352912
HRik Scarborough
LI9999999999

让我们当前的作者提前阅读并在下一次通话时保留该记录是不必要的复杂操作,这会导致维护麻烦。 但是,我们可以使用PeekableItemReader简化此过程:

class OrderReaderStep2 implements ItemStreamReader<OrderList> {private static final String BODY = "L*";private static final String HEADER = "H*";private static final Logger logger = LoggerFactory.getLogger(OrderReaderStep2.class);private SingleItemPeekableItemReader
<FieldSet> delegate;@BeforeSteppublic void beforeStep(StepExecution stepExecution) {FlatFileItemReader fileReader = new FlatFileItemReader<>();fileReader.setResource(new ClassPathResource("orders2.txt"));final DefaultLineMapper
<FieldSet> defaultLineMapper = new DefaultLineMapper<>();final PatternMatchingCompositeLineTokenizer orderFileTokenizer = new PatternMatchingCompositeLineTokenizer();final Map<String, LineTokenizer> tokenizers = new HashMap<>();tokenizers.put(HEADER, buildHeaderTokenizer());tokenizers.put(BODY, buildBodyTokenizer());orderFileTokenizer.setTokenizers(tokenizers);defaultLineMapper.setLineTokenizer(orderFileTokenizer);defaultLineMapper.setFieldSetMapper(new PassThroughFieldSetMapper());fileReader.setLineMapper(defaultLineMapper);delegate = new SingleItemPeekableItemReader<>();delegate.setDelegate(fileReader);}@Overridepublic void close() throws ItemStreamException {delegate.close();}@Overridepublic void open(ExecutionContext ec) throws ItemStreamException {delegate.open(ec);}@Overridepublic OrderList read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {logger.info("start read");OrderList record = null;FieldSet line;List<Order> bodyList = new ArrayList<>();while ((line = delegate.read()) != null) {String prefix = line.readString("lineType");if (prefix.equals("H")) {record = new OrderList();record.setName(line.readString("name"));} else if (prefix.equals("L")) {Order order = new Order();order.setLookup(line.readString("lookupKey"));order.setLookupType(line.readString("keyType"));bodyList.add(order);}FieldSet nextLine = delegate.peek();if (nextLine == null || nextLine.readString("lineType").equals("H")) {record.setOrders(bodyList);break;}}logger.info("end read");return record;}@Overridepublic void update(ExecutionContext ec) throws ItemStreamException {delegate.update(ec);}private LineTokenizer buildBodyTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 2), // keyTypenew Range(3, 12) // lookup key});tokenizer.setNames(new String[]{ //"lineType","keyType","lookupKey"}); //tokenizer.setStrict(false);return tokenizer;}private LineTokenizer buildHeaderTokenizer() {FixedLengthTokenizer tokenizer = new FixedLengthTokenizer();tokenizer.setColumns(new Range[]{ //new Range(1, 1), // lineTypenew Range(2, 20), // name});tokenizer.setNames(new String[]{ //"lineType","name"}); //tokenizer.setStrict(false);return tokenizer;}}

这次,我确实将包含的Reader实现为ItemStreamReader,以向您展示它们之间的区别。 可以像上一个一样将其实现为ItemReader。

PeekableItemReader允许我们向前查看下一条记录,以查看是否到达记录的末尾或文件的末尾。 然后可以使用相同的Processor和Writer来产生与以前相同的输出。

最后的想法

乍一看,委托模式似乎不像使用单个读取器或写入器那么简单。 这两个对象都有更多的配置。 但是我最喜欢的释义是说要尽可能简单,而且再简单不过。 稍微复杂一点的Reader和Writer将使您的Processor更加简单,并有助于进行后续维护。

代码很好,我的朋友。

翻译自: https://www.javacodegeeks.com/2016/03/introducing-delegate-pattern.html

代理模式和动态代理模式

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

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

相关文章

【WebRTC---入门篇】(六)JavaScript基础

变量与类型 var与let的区别&#xff0c;var局部变量&#xff1b;let全局变量 函数

javaio流层次结构_流的多层次分组

javaio流层次结构1.简介 使用Java 8流&#xff0c;可以很容易地根据不同的标准对对象集合进行分组。 在这篇文章中&#xff0c;我们将看到如何从简单的单级分组到更复杂的&#xff0c;涉及多个级分组的分组。 我们将使用两个类来表示我们要分组的对象&#xff1a;人和宠物。 …

【开源项目----Android OPenGLES渲染YUV视频文件】

【开源项目----Android OPenGLES渲染YUV视频文件】 OpenGLES对YUV渲染相关文章参考

大型程序是如何开发的_大型小程序如何研发提效

作者:王梦君微信公众号:滴滴顺风车技术出处:https://mp.weixin.qq.com/s/M1VArJ_ORY-eXSKzD6ysQw导读&#xff1a;自2016年小程序诞生以来&#xff0c;小程序以其“用完即走”的设计理念&#xff0c;以及简单易上手的开发模式&#xff0c;吸引了大批的小程序使用者以及开发者&a…

c 语言栈,C语言栈

C语言实现简单的栈结构今天看到一个问题是要交换两个变量的值并且不能使用中间变量&#xff0c;首先想到的方法就是用数学的方法&#xff1a;int a 10,b 12;a a b; // 求和b a - b; // 和减去b得到的是a的值a a - b; // 和减去a(此时的b是最初a的值)得到b 的值这种方法很…

【WebRTC---入门篇】(七)MediaStream

MediaStream方法 MediaStream.addTrack( ) 功能&#xff1a;向流媒体中加入不同的轨 MediaStream.removeTrack( ) 功能&#xff1a;从流媒体中移除不同的轨 MediaStream.getVideoTracks( ) 功能&#xff1a;从流媒体中取出所有的视频轨 MediaStream.getAudioTracks( …

jvm 宕机 打印jvm_通过入侵JVM打印阵列

jvm 宕机 打印jvm总览 Java中最常见的陷阱之一就是知道如何打印数组。 如果有关如何打印阵列的答案获得了超过1000票赞成票&#xff0c;那么您必须怀疑是否有更简单的方法。 几乎所有其他流行语言都具有这种更简单的方法&#xff0c;所以我不清楚为什么Java仍会这样做。 与其他…

python数码时钟代码_Python+Pyqt实现简单GUI电子时钟

本文实例为大家分享了PythonPyqt实现简单GUI电子时钟的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 突发奇想想用GUI做一个简单的电子时钟界面&#xff0c;利用pyqt模块也很方便&#xff0c;代码如下&#xff1a; from PyQt5.QtGui import * from PyQt5.QtCore im…

两个人投票的c语言程序,设计网页投票器(二)《精通Unix下C语言编程与项目实践》之十...

本处设计一个网页投票器&#xff0c;它访问“www.zhiliaowang.com”上的服务程序“/toupiaoceshi.asp”&#xff0c;并通过“name”参数向选手投票。如代码15-16所示&#xff1a;代码15-16网页投票器(节自/code/chapter15/http1.c)#include char buf2[]/*组装HTTP协议GET请求报…

【WebRTC---入门篇】(八)WebRTC核心之RTP Medio 媒体控制与数据统计

RTCPeerConnection底层是通过RTP Medio实现的,处理真正数据传输,编码流量各种控制的实现。 Receiver 通过 getReceivers 可以获取一组RTCRtpReceiver对象,用于接收数据 Sender 通过getSenders 可以获取一组RTCRtpSender对象,用于发送数据,每个对象对应一个媒体轨 RTCR…

事件驱动程序设计_简单的事件驱动设计

事件驱动程序设计总览 开发人员经常询问系统或其代码的性能或效率。 这到底是什么意思&#xff1f; 我的代码是如此高效&#xff0c;只有编码神能理解它的工作原理。 我的代码确实很聪明&#xff0c;但是却难以维护。 下一位开发人员将继续重写它。 我的代码对机器确实很有效…

python语言中的单行注释语句_Python 1基础语法一(注释、行与缩进、多行语句、空行和代码组)...

#第一个注释 print ("Hello, Python!") #第二个注释 输出结果为&#xff1a;RESTART: E:/python/change.py Hello, Python! 多行注释可以用多个 # 号&#xff0c;还有 和 """&#xff1a;实例如下&#xff1a; #第一个注释#第二个注释 第三注释 第四…

c语言文件的读写通讯录,学C三个月了,学了文件,用C语言写了个通讯录程序

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include #include #include #include struct QQ //结构体&#xff0c;有6个成员{char name[20];char age[5];char tel[15];char qq[15];char birth[12];char address[80];}user;void style()//窗口风格函数{system("mode con…

【WebRTC---入门篇】(九)WebRTC网络基础:P2P/STUN/TURN/ICE

NAT 作用:内网地址转公网地址 STUN 作用:中介将公网信息彼此交换 TURN 作用:P2P不成功的话在云端架设服务器,双方进入同一房间。可以在一起进行数据交互 ICE 作用:打包以上取最优,先尝试P2P。如果P2P不通再选择TURN。ICE尝试所有可能性 Relay是TURN Server,大多数情况…

antlr4例子_ANTLR和网络:一个简单的例子

antlr4例子网络上的ANTLR&#xff1a;为什么&#xff1f; 我开始在MS-DOS上编写我的第一个程序。 因此&#xff0c;我非常习惯在自己的机器上安装工具。 但是在2016年&#xff0c;网络无处不在&#xff0c;因此那里也可能需要我们的语言。 可能的情况&#xff1a; ANTLR 也在…

python 形参_python中参数总结

一、形参和实参 函数参数的作用是传递数据给函数使用。 在使用的过程中&#xff0c;参数有两种形式&#xff1a;形式参数和实际参数 形参&#xff1a;定义函数时的参数 实参&#xff1a;调用函数时的参数 根据实际参数类型不同&#xff0c;将实际参数传递给形参的方式有两种&am…

【WebRTC---入门篇】(十)NAT

NAT主要用于内网穿透 NAT产生原因 NAT的种类 NAT穿越原理 完全锥型:内网主机先向外发送一个请求形成IP和port

linux安装mq报5724,小白提问:linux安装MQ出现的错误

在安装MQ中遇到以下问题&#xff0c;求大神帮助# ./mqlicense.sh -acceptLicensed Materials - Property of IBM 5724-H72 (C) Copyright IBM Corporation 1994, 2009 All rights reserved.US Gover...显示全部在安装MQ中遇到以下问题&#xff0c;求大神帮助…

maven java1.7_本周Java技巧#7 – Maven慢吗?

maven java1.7本周Java本周技巧是有关Maven的上一期的后续内容。 第一个视频介绍了与Maven依赖关系有关的方面。 该视频将介绍一些加快Maven构建速度的技术。 Maven慢吗&#xff1f; 从Maven 3开始&#xff0c;您可以并行运行构建。 根据构建机器和项目结构的不同&#xff0c;…

vector 删除指定元素_std::vector简介

引入使用vector&#xff0c;需添加头文件#include<vector>&#xff0c;要使用sort或find&#xff0c;则需要添加头文件#include<algorithm>。简介vector是表示可以改变大小的数组的序列容器。就像数组一样&#xff0c;vector使用连续存储空间存储元素&#xff0c;这…