强大的wrapper

最近在看大数据和容器相关的东西,发现有一个模式被反复使用到,关键是被用的很恰当且优雅,并能在这些关键技术中都发挥着至关重要的核心作用。我想你已经猜到了,他就是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。

e2d71b5cb57d349ab2b615de2598413c.png

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)才会被真正执行。

2642908c3ea9d127de58d1c52b2079e9.png

同样的道理也适用于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之间的组合来完成从简单到非常复杂的数据处理业务逻辑。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Wrapper Object(包装对象)是一种将基本数据类型或其他对象包装在内部的对象。它提供了一种方便的方式来处理和操作这些数据类型。 在编程Wrapper Object通常用于以下几个方面: 1. 类型转换:Wrapper Object可以将基本数据类型转换为对象,从而可以在对象上调用方法和执行其他操作。例如,将int类型的数据包装在Integer对象,就可以使用Integer类提供的方法进行操作。 2. 泛型支持:在某些情况下,需要将基本数据类型作为泛型参数传递给方法或类。由于泛型只能接受对象类型,因此需要使用Wrapper Object将基本数据类型包装起来。 3. 集合框架:在集合框架,只能存储对象类型,无法直接存储基本数据类型。因此,可以使用Wrapper Object将基本数据类型包装起来,然后将其添加到集合。 4. null值处理:基本数据类型不能为null,但是Wrapper Object可以为null。这在某些情况下非常有用,例如在数据库查询,如果某个字段的值为空,可以使用Wrapper Object表示。 常见的Wrapper Object包括: - Integer:包装int类型 - Long:包装long类型 - Float:包装float类型 - Double:包装double类型 - Boolean:包装boolean类型 - Character:包装char类型 需要注意的是,由于Wrapper Object是对象,因此在使用时需要注意自动拆箱和装箱的性能开销。同时,Wrapper Object也提供了一些方法来获取基本数据类型的值,例如intValue()、doubleValue()等。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值