项目设计中的解耦合

解耦,通俗理解就是:方便将代码分开写,不同程序员可以完全负责不同的模块代码,不需要关心别人怎么写,我只需要知道调用你的模块时需要什么参数和返回什么结果就行。或者说:方便扩展,方便换新的。

从代码层面来看,除了简单的封装(面向对象编程的基本要素),解耦主要还有两大思路:依赖注入(DI) 面向切面编程(AOP)【解耦还有另外两种思路:消息队列微服务,后者达到Http的API级别解耦】。基本的概念这里就不重复写(抄)了,写写自己的思考。注意,本文只阐述简单的思路,这两大思路的实现原理暂时不深入讲解,下次写两篇博客来分别剖析DI和AOP的实现原理。

1 依赖注入

依赖注入,Dependency Injection,又称为IoC(控制反转)【依赖注入是控制反转IoC的一种实现方式,也就是将依赖管理交给IoC容器,Spring本身就实现了IoC容器的功能】。23种设计模式中,很多设计模式(behavior pattern)都使用了依赖注入的思想,用于管理类与类之间的关系,如State Pattern、Strategy Pattern、Command Pattern等。包括MVC之所以能解耦合也是通过依赖注入的技巧。具体可简单描述如下。

问题演化:

耦合模式中:B需要使用A,则在B内部创建一个A的实例(比如B有个类型A的属性),由于A可能是可替代的,而new的代码使用的是A的类名,因此替换成其他类时需要修改B的代码。

依赖注入模式中:外部生成一个实例A,然后传给B(B依赖于A),此时B内部只需要有个抽象类(A的父类AA,或A实现的接口AA)接住A即可,若需要替换成A2(A和A2都继承自AA,或实现了AA接口),则无需更改任何代码。

当然,在某些版本中,会写一个Injector来创建A实例,并注入给B,示例:Dependency injection

 

依赖注入的编写方式的一个示例:工厂式自给自足

若B面临的环境非常接近人类自然世界,比如传给B的不是A的实例 而是一段字符串(如:命令行的指令,网页的URL等),此时可以这样处理:写一个预处理器,根据字符串生成相应的类的实例,然后传给B。容易知道,这个预处理器其实就是一个特殊的Factory。也可以将这个预处理器写在B内部,那么B就“自给自足”了。

那么这个预处理器应该如何写呢?通过反射 reflection 实现。(1) 若字符串和类名一致,则直接反射得到类名,并使用类名创建实例(newInstance)。(2) 若字符串和类名不一致,则可通过Annotation来进行匹配。Annotation本身只是做了简单的“标记”,可以使得这个类能查询到“被某种Annotation标记了以及使用的value是多少”。那么预处理器就可以通过反射,拿到各个类的“类名与标记value的对应情况”,并将这种对应保存到HashMap中。那么B拿到字符串后(字符串应和Annotation的value一致),B就可以通过HashMap查询到相应的类,然后创建newInstance即可。

可以通过这个来自己手写服务器的路由 router。

依赖注入常借助工厂模式进行编写,具体编写细节根据需要而定。比如可以参考Spring中的IoC的实现源码。

Spring中大量使用了 IoC,并且进一步简化了 IoC的使用,即 使用某些annotation实现自动组装(new创建实例,并注入),最经典的是@Autowired这个注解配合Bean的使用。

 

2 面向切面编程

面向切面编程,AOP,是进一步解耦合的一种技巧。Spring中大量使用了AOP。而Python中的decoration其实也是AOP的思想。另外,设计模式中的 Proxy Pattern 其实就是实现AOP的理论基础,AOP使用代理模式的动态代理进行编写【动态代理是一个不小的话题】。

使用场景在于:业务逻辑存在交叉时,比如“检测权限”的业务逻辑 与 “访问页面”的业务逻辑出现交叉时。

其关键在于,对一个 behavior进行拦截,然后进行外围的修饰,并调用behavior。

耦合模式中:业务逻辑在每一次交叉时都要手写个判断(比如:每次“访问页面”前都需要调用“检测权限”的method来进行判定)。一旦,“检测权限”的method被替换(换了新的高级检测方法,调用需要使用新的method名)或者被遗弃(完全开放权限),则需要找到“曾经写判断的地方”进行修改。

AOP模式中:可以将某些次要业务逻辑写在Aspect类中,指定需要拦截的behavior、拦截的时机、拦截了要干些啥。比如将“检测权限”这一业务逻辑写在 AuthAspect 中,并且指定要在哪些behavior触发时需要进行拦截,然后检查权限,再放行。当然,拦截的时机还可以是behavior介绍或者抛出异常时等等。

AOP/decoration 实现了真的“插件”思想:我若想换一个修饰,直接拔了换一个新的插件就行。AOP说白了就是告诉我们如何写插件,写插件的人无需知道“主件”怎么写的,写“主件”的人无需知道插件怎么写的,要用的时候在插件配置的修饰列表里添加下“需要插件的behavior”就行(Python的decoration就只需要改改注解就行)。

Spring中的Aspec类需要使用@Aspect进行注解。

 

注意到,Aspect的切点设置中,不仅可以根据behavior进行设置(即execute method),还可还可根据annotation进行设置(即annotation sampleAnnotation),也就是可以拦截具备某种annotation的behavior。

那么,写一个带有AOP的annotation就很容易实现了,很容易写出一个类似于python装饰器的东西出来。

 

AOP的实现原理基于动态代理,包括Jdk动态代理和CGLIB两种方式(若委托类没有实现接口则必须使用CGLIB)。所谓动态代理,就是Java程序运行时动态临时生存代理类,比较灵活,足以解决AOP的需求。动态代理的两种实现方法,本文暂时不深入,下次写一篇博客专门进行总结。

 

 

你可能感兴趣的:(后端开发,软件工程,Java)