不论在天上,在自然界,在精神中,不论在哪个地方,没有什么东西不是同时包含着直接性和间接性的。------黑格尔
代码既是一种艺术创作,也是一种工艺制造,其中也包含诸多中介。
以下是一个简单认识中介的例子:
中介活动 :中介活动系指中介人居间帮助
委托方、委托方合作者(下简称合作者)双方达成某项协议/契约/合同的活动。
嗯,读到这儿好像还能理解,以买房为例,大概就如下图所示:
但还得再加上这么一句话,但在中介过程中,牵涉到中介人与委托方(甲、乙方或双方)
签约、发布、寻找委托方合作者、协调甲、乙方签约、帮助完成签约和追索并获得报酬等活动。
相比上一张图,这张图更能体现出中介具体做了哪些工作
- 积极收集符合委托方条件的合作者;
- 和合作者讨价还价;
- ······
当然,中介并不是白干这些活,他最终也是需要中介费的,这也就是委托方的成本。
看完只想说,啊啊啊啊可恶要长脑子了,不过没有关系,在代码意识中,成本的耗费相比模块和架构意识少。
代码意识
言归正传,我们直入主题,看以下两个案例:
-
有代码块 A、B、C... 想使用变量 "str",我们往往会通过静态常数描述它,
如:
static String CONSTANT = "str"
-
委托方:代码块 A、B、C...
-
中介人:CONSTANT
-
合作者:"str"
-
有类 A、B、C... 想使用类 X,在 Spring 框架中,我们常常会通过注入的方式让 X 被其他类依赖.
-
委托方:类 A、B、C...
-
中介人:Spring 容器
-
合作者:类 X
上述只是两个简单的中介行为案例,现实生活中委托方和合作者往往都会向中介人提供自己的需求,
而中介人则需要根据需求推荐委托方或合作者。
衍生出下一个案例:
- 在一个方法中,入参为 code,而这个方法动作则需要根据不同的 code 执行不同的逻辑。
如:
java
void performLogic(String code) {
}
如果 code 的变化是固定的,例如像英文字母,无论如何都是 26 个,那我们穷举出来,其实也不耽误代码的扩张性,
只是过于冗长有点丑陋,但这种情况在实际中偏少。
java
void performLogic(String code) {
if (code == "A") {
} else if(code == "B") {
} ... {
} else if (code == "Y") {
} else {
}
}
实际开发过程中,code 的变化往往是动态的,考虑维护成本和扩张性的功能,所以在方法performLogic()
中
显然不能因为 code 的动态变化而变化。
那可不可以找一个中介,让其提前知晓 code 对应的逻辑,每当 code 投来,我们让它把对应的逻辑给到委托者,
这样,委托者只需要关心自己在什么场景下传递什么 code,而不需要关心具体的逻辑怎么做。
这种情景 Map
结构再合适不过,因此可以这么写:
java
@Value // get
private final Map<String, Logic> knownLogics;
void performLogic(String code) {
Logic logic = knownLogics.getOrDefault(code, defaultLogic);
logic.performed();
}
那以上的身份可以确定如下:
- 委托者:执行
performLogic()
的业务 - 中介者:knownLogics
- 合作者:对应的逻辑
中介活动也显然易见:
- 中介提前知晓委托者(上层业务)的需求(code)对应哪些合作者(逻辑)
- 委托者将需求给中介(knownLogics)
- 中介将对应的合作者告知委托者
- 委托者与合作者完成合作
中介者的身份有效地将委托者与合作者进行了解藕,彼此各尽其职。
相反 ,如果在此处采取 "字母"的做法,那么每当出现新的 code ,那么都需要在方法 performLogic()
中修改。
综上,使用中介思想可以让委托者和合作者在遵循单一职责和开闭原则的同时,还能保证委托者与合作者合作的代码不变,
是符合面向对象设计原则的。因此,使用中介思想可以促进开发人员理解面向对象设计原则和灵活使用设计模式,进而提高代码质量。
模块意识
以规则引擎在众安无界山理赔中心的应用为例:
在理赔业务中,主要有报案、立案、定损、理算和核赔五大流程,每一个流程进行至下一步时都需要进行规则校验,
例如有黑名单客户校验、反洗钱校验等校验规则。
如果将这些规则嵌套在每一步流程的代码中,那么一旦面对规则逻辑需要修改时,就不得不在原有代码上进行修改,
这导致规则与代码逻辑强耦合,并且每一次修改规则时,都需要重新编译代码。
我们可以通过中介意识将这个问题解决,我们将每个被校验的对象当作变量 x
,
在经过一个函数:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> F n ( x 1 , x 2 , . . . , x n ) ∈ { 0 , 1 } F_n(x_1,x_2,..., x_n) \in \begin{Bmatrix} 0, 1 \end{Bmatrix} </math>Fn(x1,x2,...,xn)∈{0,1}
后得到通过或不通过。
到这一步,我们也只是知道了委托方(业务代码)、委托方的需求(变量 X)及合作者(函数)
那中介是谁呢?没错,这个中介就是要新增的模块
言归正传,我们回到规则引擎在无界山理赔中心的应用中,那么我们可以确定以下身份:
- 委托方:业务代码
- 中介:规则引擎
- 合作者:规则组(一簇规则;函数)
中介活动如下:
- 委托方(业务代码)提供需求(被校验的对象)给中介(规则引擎)
- 中介寻找合作者(规则组)
- 合作者按照委托方的需求签订协议(校验结果)
有了如上意识后,我们可以将规则引擎单独做一个模块去开发,然后使业务模块依赖,最后通过"创造(配置)"合作者(规则)。
这样,所有要使用到规则校验的业务代码都只需要通过规则引擎的入口,传递指定的需求和规则组 code 即可完成校验,如下图:
事实上,从模块意识开始,成本的问题就略有呈现了,例如:
- 创建出中介;
- 编写中介找到合作者逻辑;
- 合作者创造出来,应该存储在何处?又如何管理?
通常来讲,都是存储到数据库,又通过接口调用进行增删改查,虽然与业务代码进行了解耦,但需要另取资源存储和管理,
那这样是否是拆东墙补西墙呢?
回答这个问题之前,反过来问一个问题,如果保持原来的做法,没有中介,那又会怎么样呢?因此这就成了对比,需要在权衡之下做选择。
显然,有了中介能够拆更少的东墙补更多的西墙。
架构意识
以前车马很慢,书信很远,一生只够爱一个人。
为了能让信封能够抵达心上人的手上,往往会将信封塞到信箱中,或是托信使帮忙托送,尔后忙于其他。
架构亦是如此,服务与服务之间难免存在沟通的情况,例如:
- 如果服务 A 需要且满足某接口,那么通常会让服务 A 寻找实现了该接口的服务;
- 如果服务 A 只是需要某服务的处理,并不关心处理的细节,那么通常会让 A 传递给信使,信使再告诉能帮助 A 的服务 X。
从中介思想的角度看上述两个案例,需要解决两个问题:
- 案例 1 中 A 是如何找到实现了该接口的服务?
- 案例 2 中的信使是谁?又如何找到他?
问题显而易见:
- 案例 1 中 A 肯定是通过中介才找到实现了该接口的服务;
- 案例 2 中 A 肯定也是通过中介才找到信使;
- 案例 2 中 信使自己本身也是一个中介人,负责存储 A 的需求和寻找帮助 A 的服务 X。
事实上,上述的案例就是现在的远程过程调用(Remote Procedure Call, 下简称 RPC)
和消息队列(Message Queue, 下简称 MQ)。
案例 1 (以 Dubbo 框架作为 RPC 框架为例)的身份确定如下:
- 委托方:服务 A
- 中介:注册中心
- 合作者:实现了该接口的服务
案例 1 的中介活动如下:
- 委托方(服务 A)已知合作者(实现了该接口的服务)的要求(接口信息)
- 委托方按要求提供信息给中介
- 中介根据委托方提供的信息寻找合适(时间、天气等外部因素)的合作者
- 委托方得到合作者的答复(响应结果)
案例 2 的身份确定如下:
- 委托方:服务 A
- 中介 C:配置中心
- 合作者 M 兼中介 M:消息队列
- 合作者 X:帮助服务 A 的服务 X
案例 2 的中介活动如下:
- 委托方(服务 A)从中介 C (配置中心)得知有中介 M (消息队列)可以帮助他
- 委托方找到中介 M 拖信(消息体)
- 中介 M 将信传给委托方指定的合作者 X (帮助服务 A 的服务)
中介意识在当前分布式架构中非常常用,除了 RPC 和 MQ 用于服务之间的交流之外,
还诞生了许多中介人用于不同的场景,例如:
- 充当中介的事务协调者,用于分布式事务场景;
- 充当中介的分布式锁,用于多服务对共享资源的访问;
- 充当中介的负载均衡器,用于多服务时的负载均衡;
- 充当中介的服务监控,用于监视多服务沟通链路;
- ······
到这儿,作为造物主的你此时会发现,除了在完成功能的服务之外,令你头疼的不仅仅是功能逻辑,
还涉及到了如何让功能在各种场景下运行,因此你做了不少非原有功能的事情。
这就是成本,它不再是"多写几行代码"这种简单的成本,此时的它显然变得不可忽视。
那是不是就可以随意妄为呢?什么中介,这些成本才不想考虑。
那更有趣,如果我们的生活中没有像招标、房屋、拍卖和招聘这种中介,似乎好像也没啥影响,无疑是变得不方便。
但中介有没有可能是物,是思想呢?
比如一个人想让另一个人消失,是什么在约束他呢......
总结
艺术来源于生活,代码也如此,程序员也是一种艺术家,如今在现实生活中已有很多中介案例,只需要模仿或照搬。
特别地是,程序员有着与生俱来的逻辑感,他们热衷于为什么,于是在代码世界中将那些曾经的工匠精神复燃,
在那里,重现了将动力和转矩传递到所需处的齿轮、解决远距离沟通的电话乃至能量转换的发动机的发明。
参考文献
[1] Dubbo 官方文档
[2] 黑格尔. 逻辑学[M]. 北京: 商务印书馆, 1976.