最近在看大数据和容器相关的东西,发现有一个模式被反复使用到,关键是被用的很恰当且优雅,并能在这些关键技术中都发挥着至关重要的核心作用。我想你已经猜到了,他就是Eminem——强大的rapper——哦,不对,是wrapper。接下来,这篇文章我就带大家看看,wrapper这玩意有什么特别之处,为啥这么有用?

1. 什么是wrapper

Wrapper就是包装的意思,广义上来说,Decorator和Adapter设计模式都属于Wrapper,只是二者的意图(intent)不一样。借用stackflow上一哥们的回答:

Decorator:

Allows objects to be composed/add capabilities by wrapping them with a class with the same interface.

Adapter:

Allows you to wrap an object without a known interface implementation so it adheres to an interface. The point is to "translate" one interface into another.

Wrapper:

Never heard of this as a design pattern, but I suppose it's just a common name for the above

大意是说Decorator是用组合的方式增强功能,而Adapter是为了做接口转换,二者都是采用wrapper的方式来做到这一点,形式是一样的,但意图不一样。

2. 流式计算中的wrapper

谈到wrapper(在本文中,你可以认为wrapper就是decorator),我认为使用最到位的,非流式计算莫属。从Java的IO流,到Java Stream,到Spark,Flink到处都能看到wrapper的身影。

2.1 Java IO流

我们先从Java IO流看起,假如现在我们需要从一个文件中读取信息打印到console,我们可以使用以下的方式:

Reader in = new BufferedReader(               // 3. 缓冲字符,提升效率new InputStreamReader(                    // 2. 将字节流转换成字符流new FileInputStream("path"), "GBK") // 1. 读取文件的字节流    )
)

其执行过程如下图所示,首先从文件中读取字节流,然后放入字节缓冲区,例如“中”字,他的GBK编码是两个字节——D6D0,那么在InputStreamReader中等到这个字符形成以后,再输送到BufferedReader的字符缓冲区,当缓冲区满了以后,再溢写到console。

IO流的这种做法就是一种典型的wrapper,也就是decorator装饰者设计模式的实现。通过层层包装,我们获得了更强功能的流处理能力。

2.2 Spark中的RDD

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。

Spark支持两个类型(算子)操作:Transformation和Action,主要做的是就是将一个已有的RDD生成另外一个RDD。Transformation具有lazy特性(延迟加载)。Transformation算子的代码不会真正被执行。只有当我们的程序里面遇到一个action算子的时候,代码才会真正的被执行。这种设计让Spark更加有效率地运行。

其中Transformation等价于Java Stream中的中间操作(Intermediate operations),而Action和Java Stream的结束操作(Terminal operations)表达的是一个意思,它们只是实现方式不一样,Java Stream是使用pipeline,而spark则采用了wrapper的方式来达到lazy的特性。以wordCount为例,它在spark中的scala实现如下:

//1. new HadoopRDD()
val lines: RDD[String] = sparkContext.textFile("path")//2. new MapPartitionsRDD(hadoopRdd) - flatMap
val words: RDD[String] = lines.flatMap(_.split(regex=" "))//3. new MapPartitionsRDD(mapPartitionsRdd) - map
val wordToOne = words.map(word=>(word, 1))//4. new ShuffledRDD(mapPartitionsRdd)
val wordToSum:RDD[(String, Int)] = wordToOne.reduceByKey(_+_)//5. collect
val array: Array[(String, Int)] = wordToSum.collect()

其在runtime被执行时,实际上,构建的是一个如下图所示的wrapper,也就是说,当flatmap被调用时,并不会真正执行flatmap这个动作,而是构建了一个MapPartitionsRDD包装了HadoopRDD,以此类推,直到collect方法被调用时,前序的转换指令(transformation)才会被真正执行。

同样的道理也适用于Flink,Flink中的各种transform算子也是通过这种wrapper的方式构建出来的。

3. wrapper的背后思想

通过上面的案例,可以看到wrapper这个模式的确很有用,再往下深挖一下,我们可以发现其背后隐藏着更深刻的设计道理——即分层和OCP。

3.1 分层

表面上是包装,其背后暗含的是一种分层结构。“复杂性常常以层次结构的形式存在。复杂的系统有一些相关的子系统组成,这些子系统又有自己的系统,如此下去,直到达到某种最低层次的基本组件。”

因此,wrapper这种构建类似洋葱圈层次结构的能力,正是构建复杂系统所需要的。也正因为有这样的层次结构,我们才得以能够理解、描述、实现这些复杂系统。话说回来,我们似乎也只能理解那些有层次结构的系统。这也是为什么分层架构是软件设计中最基础,也是最重要的架构之一。

3.2 OCP

"Open to extend,Close to change"可以说是我们工程师的梦想,没有人愿意修改原来已经work的东西,因为修改就意味着要重新测试,就以为着可能引入新的bug,就以为着头发变少...... 大部分的设计模式都是为了OCP这个目标在努力。

我们平时提倡的immutable(不可变性)也是这个道理,immutable的String让我们可以放心使用,而不用考虑参数传递问题;immutable的函数式编程不用担心线程安全问题;immutable的docker image使得分层构建和复用成为可能;immutable infrastructure(不可变基础实施)提升了运维效率,是云原生的必要条件。

wrapper通过包装的形式,在保存原有功能的同时,通过扩展实现新增能力,从而保证了原有能力的immutable,完美的实现了OCP。此外,wrapper还可以通过基本组件的组合,可以实现非常复杂的业务逻辑。比如Spark通过RDD之间的组合来完成从简单到非常复杂的数据处理业务逻辑。

强大的wrapper相关推荐

  1. HttpSender OkHttp+RxJava超好用、功能超级强大的Http请求框架

    HttpSender HttpSender 是对OkHttp二次封装,并与RxJava做到了无缝连接,支持任意Http请求方式,如:Get.Post.Head.Put等:也支持任意数据解析方法,如:J ...

  2. 强大的表格控件handsometable,结合vue

    handsontable handsontable是目前在前端界最接近excel的插件,可以执行编辑,复制粘贴,插入删除行列,排序等复杂操作.jQuery.react.ng和vue版本,功能强大,是复 ...

  3. 计算机视觉,图像处理 经典代码paper整理(很全很强大!)

    转自:Jia-Bin Huang 同学收集了很多计算机视觉方面的代码 ,链接如下: https://netfiles.uiuc.edu/jbhuang1/www/resources/vision/in ...

  4. Linux下的TCP Wrapper机制

    inetd程序在系统中是作为一个服务进程出现,它监听许多端口并且在得到客户请求时启动这个端口的服务程序. 早期系统中使用的inetd被称作超级服务器,其实现控制对主机网络连接.当一个请求到达由inet ...

  5. html中剪切图片所用插件,简单功能强大的jQuery图片剪裁插件Image Cropper

    插件描述:相信很多朋友都在大型的网站,如新浪微博.QQ微博上看到过头像裁图工具,感觉很高大尚吧,今天朋友们有福了,今天就来说一说一款这么高大尚的插件cropper,cropper是一款使用简单且功能强 ...

  6. Jquery_JQuery之DataTables强大的表格解决方案

    1.DataTables的默认配置 $(document).ready(function() { $('#example').dataTable(); } ); 示例:http://www.guoxk ...

  7. 灵活强大的构建系统Gradle

    前言 构建,软件生命周期中重要的一环,在现代软件开发过程中,起着越来越重要的作用.过去在Java或类Java的世界里,Ant.Maven再熟悉不过了,Maven凭借其强大的依赖配置战胜Ant,基本上成 ...

  8. MP条件构造器Wrapper

    5.1 概述 我们在实际操作数据库的时候会涉及到很多的条件.所以MP为我们提供了一个功能强大的条件构造器 Wrapper .使用它可以让我们非常方便的构造条件. ​ 其继承体系如下: ​ 在其子类Ab ...

  9. 强大的图片预览组件Viewer.js

    ​ 1. Viewer.js简介 Viewer.js 是一款强大的图片查看器.我们通过Viewer.js 在页面上添加强大的图片查看功能,同时,这款优秀的插件配置操作起来也非常的方便. Viewer. ...

最新文章

  1. Map-Reduce编程模型gif图片解释
  2. linux部署jar项目报错_如何在Linux服务器上部署jar包
  3. Android高级控件(二)——SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现...
  4. Docker: vmware企业级docker镜像私服--Harbor的搭建
  5. 美团大脑:知识图谱的建模方法及其应用
  6. 因为知道了30+款在线工具,我的工作效率提升500%!
  7. Hibernate 学习-1
  8. go语言暂停命令_go test命令(Go语言测试命令)完全攻略
  9. 关系模型的完整性约束
  10. 北京国企软件开发公司有哪些?哪家比较好呢
  11. 数据科学和人工智能技术笔记 十九、数据整理(下)
  12. Java递归下降分析器_递归下降语法分析器
  13. 即席查询—— Kylin使用
  14. Win系统 - 开启 WIN10 隐藏的卓越性能模式
  15. 涂涂乐的详细实现之一--画笔核心功能
  16. 【计算机毕业设计】541鲜花商城系统
  17. 冒烟测试回归测试UATSIT
  18. Chomp game博弈游戏
  19. 取代房子,这是未来5年最好的投资!
  20. IBM Installation Manager安装的几种安装方式

热门文章

  1. B2B2C分销商城系统开发解析-首篇
  2. httpd实现https
  3. 【ffmpeg】windows上用命令行批量将.flac格式转换为.wav等格式
  4. 网站站内优化三大标签
  5. 国密算法SM4 的JAVA实现(基于BC实现)
  6. linux U盘插拔检测
  7. 互联网产品发掘种子用户和意见领袖的方法
  8. 正达信通丨【功能介绍】ZedaIoT物联网平台的视频监控设置说明
  9. 计算机局域网安拓扑结构进行分类,计算机一级试题7
  10. C++ strcmp函数的使用