引用的必要
一个文件实现一个复杂功能的时代已经过去了,甚至一个工程实现一个项目都逐渐被淘汰。
限于层级的划分、项目的管理、功能的扩展等因素,在程序的设计之初,就有了引用的概念。
使用既定的方式,去使用已经完成的功能或者对象。
部门间的流转、模块间的导入都是如此,仅仅关注于自身的逻辑管理,而其他部分只关注接口。
诸多的编程语言中,基本的共同点,就是包、模块的引入。
Go
语言中,方法间存在两种引用:值传递和引用传递。一个大对象,占据内存为
S
,如果是值传递,经过n
个操作方法,内存占用为。但是,如果使用地址传递,也就是地址引用,消耗内存为。
不论是功能还是其他方面,引用都是大有用处,也无处不在。
奇妙的对象
面向对象的模式的优缺点不用再讨论,我也没能力置喙,只是说说它和引用的奇妙关联。
面向对象的封装模式,更提高了内聚,但是引出一个本质问题:多层引用。
我们通过模块引入一个类,实例化类之后通过对象引用方法。
尤其重要的是,我们更依赖于对象的引用,这就把引用的本质,或者面向对象的本质给显露出来。
普遍的引用,通过方法的传递,或者模块的导入即可完成,但是面向对象中间还夹杂了实例化这个步骤。
总结一下,面向对象的几个繁琐点
- 个体依赖性
模块可以直接引用、方法可以直接调用,但是面向对象中依赖的并不是直接的外部接口,而是依赖的具体对象。
即使是同一个类,由于参数的不同,相同的方法之间的差异性也非常巨大,不存直接引用这种说法。
- 引入复杂性
由于前述的一点,当涉及多个对象组合的功能的时候,如何引入这些依赖的对象。
加上其他模块对于基础对象的相同的引入,不可能由某一个特定的使用类来具体的实例化。
同时,不管由构造器还是外部方法进行设置,都显得十分笨拙。
或者还有其他原因,但都大同小异,那就是:面向对象带来了引入的动态性质。
之前的引入,都是静态的,但是基于对象而非类的依赖,尤其是对象的封装特性,我们不可能做到统一的引入。
势必,要有那么一个东西,支持、简化、统一我们的对象的引用。
注入(DI)
模块和方法都是静态的,对其接口即可,对象确实动态的,必须指定个体。
而且由于可能的多方面的引用,无法交给任何一个调用类来进行实例化管理,必须存在一个中心的容器,去维护这些基础的素材。
最后,让对象,和其他语言中的模块一样,成为一种静态的,可无歧义的直接进行引用。
每种语言,支持的都是基础的静态引用,而java
中的办法,或者说spring
的方案是:使用一个对象容器,来进行维护管理。
然后通过注入
这种方式,来模仿导入的过程。也即是,你说你需要,我一个个给你们填充。
看看一个文件中高达十几个的类引用,想到一个个的实例化的恐惧,想到几十个类之间的交叉引用,庆幸。
几十个的get/set
,谁都会对代码丧失兴趣。
控制(IOC)
然后,接踵而来的更重要的问题,管理的对象哪里来。
如果说借助容器,我们自己去挨个注册,自己去实例化,不说多么无味和重复,只是,有必要么。
通过反射,或者其他什么办法,让容器自主的能够去创建、管理对象。
减少失误,提高开发进度,尤其死板的严格的管控条例,获得更准确的服务保障,这就是IOC
的极大优势。
切面(AOP)
不得不说,基于对象引用的繁杂操作让人厌烦,但是,其所带来的操作空间的确巨大。
对于一个基础的功能,如果我们想要稍微修饰一下,就会显得十分鸡肋
- 方法包装
可以写方法进行包装,但是却可能时常更换,浪费人力,切效用不大
- 操作包裹
操作时候去修饰,冲淡逻辑的紧密性,分散精力,冗余垃圾代码
不论采取什么方式都显得鸡肋----但是,java
中这种问题不复存在。
如上,鸡肋让我们不论从模块还是程序的角度,都无法保证纯净。
多出来的中间过程,让我们可以尽情的去定制所需要的即将引用的对象及其方法。
在每个阶段都能够保证自身的纯净、逻辑的聚合。
其中的从加载到容器的阶段,正是切面(定制)的活动空间。
由于
jvm
是基于class
的中间语言的平台,从java
到class
的步骤,也是这一步的领域。因此,切面在于已经加载对象的重新定制,还有
javaassist
等基于字节码的包装功能。不论是加载即包装,还是引用即包装,都是对象引用所提供的定制化的操作空间。
忽略具体细节,把class
字节码文件等同于java
的内容,就可以统一理解。
注意:
class
的直接定制,由于不存在更多的联结特性,效能优于基于对象的包装方案。
操作的空间
如果非直接的流程,在中间的传输过程中,就可能存在更多可能的操作,提供更多的使用空间。
ribbon
的负载均衡,正是由于注册中心的非直接调用,才得以实现。
微服务中的小模块,管理和调用,正如java
中的对象引用的管理,容器中心也就变成了服务中心。
简单的引用,其本质,天差地别。