写点什么

架构师训练营第二周作业

用户头像
Jerry Tse
关注
发布于: 2020 年 06 月 17 日

1.什么是依赖倒置原则,为什么有时候依赖倒置原则又被称为好莱坞原则?

1.1 依赖倒置原则(DIP)

  • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象

  • 抽象不应该依赖于细节,细节应该依赖于抽象

1.2 先从高层、底层的说起

系统由模型+关系构成,分层是将功能相似模型进行有序的分组,从逻辑上归为不同的类别。方便将模型间多对多的网状关系抽象为层与层之间一对一的层状关系,有效降低系统间模型关系复杂度。我们熟知的MVC模式就是一种分层思路的具体实现。



有了分层就有了高层和低层的概念。顾名思义,在业务逻辑上游的层叫做高层,处于业务逻辑下游的层叫做低层。高层、底层只是相对概念,一个系统可以划分为很多层而不是只有两层。

1.3 再聊聊依赖

我们简化一下,假设上层模块直接调用下层模块方法。



(图一)

如图一所示,这样有什么问题:

  • 下层模块方法没有约束,可以随意更改,且调整后上层调用方也要被动更改。

  • 上层模块无法复用,新增一类下层业务,上层模块也只能引入更改才能适配。



这就是所谓的正向依赖,也是大多数人代码体现的模块与模块之间的依赖方式。

1.4 最后说说倒置

正向依赖有问题,我们把它“倒置”一下解决问题。



(图二)

图二就是倒置后的样子:

  • 由上层定义出一个抽象接口(策略模式)

  • 上层业务不再调用底层实现,二是调用上层接口中的方式实现自己的逻辑

  • 底层必须实现上层接口



我们倒置了什么?

  • 倒置了上下层的依赖关系

上层模块不在依赖于低层具体实现,两者都依赖与上层抽象接口。

  • 倒置了接口所有权和定义权

是上层根据具体的业务定义接口,低层根据业务实现接口定义。而不是低层现有能力抽象为接口供低层使用。从业务驱动的角度这种方式更加合理。



1.5 好莱坞原则:"Don't call me,I'll call you."



好莱坞原则可以很好描述依赖倒置原则:
  • 经纪人和候选人都依赖通信服务抽象(高层不依赖低层,他们都依赖抽象),候选人必须实现通信服务所规定的电话联系的方法。

  • 经纪人不关心候选人如何实现电话联系,你可以使用手机、座机、卫星电话、网络电话。

  • 经纪人决定通信服务的通信方式,不是候选人决定(接口定义权由高层规定)。

  • 经纪人规定的联系方法就是电话,不是邮件,也不是登门拜访

好莱坞原则也可以描述框架的交互行为:

一个框架不是暴露了一些接口供业务代码调用,二是实现了高层策略逻辑,并且调用具体业务完成业务流程。不要去试图调用架构方法,架构会在何时的时机调用你的方法。



2.Struts2如何实现依赖反转原则

2.1 Struts2框架定义:

Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。

2.2 核心业务逻辑:

FilterDispatcher就是Struts2控制器逻辑的核心实现,主要负责职责:

  • 接收用户请求

  • 请求地址的解析、映射

  • 请求参数的解析、封装

  • servelet上线文环境组装

  • 调用相应的用户自定义的逻辑处理用户请求

  • 封装处理调用返回数据

  • 将结果返回给用户请求

(以上内容只是示意,阅读过Struts2源码的大神不要较真)

2.3 小白版架构

按照职能描述,FilterDispatcher和用户自定义逻辑间有一个明显的依赖关系,但是如果我们直接按照依赖关系设计出如图三的类图架构:就违反了依赖倒置原则,高层模块依赖底层架构



(图三)

带来的问题:

  • FilterDispatcher被动变更,不同的用户请求对应不同的用户逻辑中的方法,因为FilterDispatcher依赖XxxAction并调用xxx方法,一旦某个Action中有改动,都会迫使FilterDispatcher做出调整.

  • FilterDispatcher无法复用且缺乏扩展性,想想一下增加一个新的业务需求:比如新增BillAction来处理用户账单逻辑,我们还要在FilterDispatcher添加代码支持。

架构思考:

FilterDispatcher作为一个Struts2框架的核心逻辑,包含了高层业务规则,应该独立于用户业务逻辑。现在反而被具体的用户逻辑影响。想象一下你在实际的工作中每次新增或修改一段业务逻辑都要更改Struts2架构的核心FilterDispatcher类的源码,修改之后再重新打包引入工程,岂不是非常可笑。

2.4 现实架构

有了以上了思考,我们看一下Struts2的框架结构。图四为一个简单的示意图,用来说明FilterDispatcher和用户逻辑的关系。



(图四)

如何实现依赖倒置原则:

  • 高层模块不依赖低层模块,二者都依赖于抽象

架构层的FilterDispatcher不在依赖于用户层的具体业务,两者都依赖于Action接口抽象。

  • 抽象不依赖于细节,细节应该依赖于抽象

Action抽象不由某一个具体的用户业务决定,相反所有的用户业务必须实现Action抽象。FilterDispatcher类中也没有任何具体用户类的引用(依赖),只引用(依赖)了Action抽象类。

架构思考:

FilterDispatcher不再依赖于具体的业务实现,而依赖一个Struts2架构自己定义的Action抽象接口,FilterDispatcher终于不在关系具体的业务变化,只要是实现的Action的类都可以被FilterDispatcher无感调用,而且无论是以后用户逻辑的变化或者新增用户逻辑,只要依然满足Action契约(必须满足),FilterDispatcher不用做出任何改变



进一步思考:

FilterDispatcher中用户逻辑的类是如何被实例化并赋值个Action接口的?

这里就要使用Java一个非常重要机制——“反射”,在用户首次请求的时候,FilterDispatcher通过配置文件找到请求地址所对应的类名,根据类名通过反射机制将类实例化为对象,也是通过反射将实例化后的对象赋值给Action接口的变量。Spring等框架都是使用这个机制实现的依赖倒置功能。所以说没有反射我们无法践行依赖倒置原则。



3. 作业三:

请用接口隔离原则优化 Cache 类的设计,画出优化后的类图。

改造后类图如下:



Cache实现CacheOpertation和CacheConfig两个接口,两个接口有各自的接口方法。使用时依赖CacheOperation接口就不可见Cache中reBuild的方法。



发布于: 2020 年 06 月 17 日阅读数: 60
用户头像

Jerry Tse

关注

还未添加个人签名 2018.11.02 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第二周作业