千家信息网

如何理解细数软件架构中的解耦

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容主要讲解"如何理解细数软件架构中的解耦",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解细数软件架构中的解耦"吧!1. 架构的定义架构是软件方
千家信息网最后更新 2026年11月03日如何理解细数软件架构中的解耦

本篇内容主要讲解"如何理解细数软件架构中的解耦",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解细数软件架构中的解耦"吧!

1. 架构的定义

架构是软件方法学的范畴,它解决的是软件组织的问题,不解决软件算法的问题。两者的区别可用下图的积木做个类比:

算法就像一个个的积木块,比如绿色的圆柱,蓝色的三角,红色的方块等。而架构则是把各种积木块,组装成一个城堡,一辆小火车。为搭建这个城堡或小火车,架构师脑子里得有张图纸,图纸里既要定义需要哪些形形色色的积木块,又要考虑如何将它们组装起来。这工作很像建筑师,英文也的确叫 architect。

这样类比,很容易让不太理解技术的企业家们陷入误区,会觉得架构师要比算法工程师更厉害?其实不然,这是两个细分领域的才能。不知道您注意到小火车车头上的烟囱没?它是一个像鸡腿菇一样的弧线造型,浇灌出这种造型的模子,要比三角形和方块形要难很多,它需要更深奥的几何学的支撑,这可以形象的看做是算法工程师解决的问题。

2. 架构的意义

架构解决软件组织的问题,它能给企业创造什么价值?换句话说,好的软件组织,跟差的软件组织,从商业价值创造的角度,有什么不同?笔者以为架构的价值体现在可用性和敏捷性两个角度,但今天要讲的是敏捷性。敏捷性指的是快速、低成本、高质量地应对扩张市场的差异化需求。企业在初创期积累了不少软件资产,这些资产在当初的市场环境下,已被论证取得了市场业绩。但是伴随着企业扩张,市场会更加精细化、场景化,这些都会给我们的软件提出新的需求,企业需要借助前些年在这个领域积累的先发优势,一方面快速占领细分的市场;另一方面复用曾经积累的资产,发挥资产的规模经济效应。

比如京东电商,从高价值、标准化的 3C 数码起家,建立起自营电商模式;紧接着开始扩品,做低价值、但高频次、依然标准化的日用百货圈用户粘性;再做相对非标的服装发展女性用户和生态模式等,直指行业竞争的关键区;除了扩品还伴随着场景扩张,诸如 2B 企业业务、下沉市场拼购业务、泰国印尼国际业务等。供给角度的品类扩张,需求角度的场景扩张,构成了京东矩阵式垂直业务线。它们正是复用了零售中台的软件基础设施,才在一定程度上做到了快速扩张。

3. 架构的灵魂

既然软件组织的价值如此重要,那么好的软件组织的标准是什么呢?又该如何做到呢?好坏的标准在解耦。解耦的对立面是耦合,耦合是指阻碍变化的依赖;解耦是要在依赖的基础上,做到应对可能的变化。依赖是必不可少的,依赖的本质是分工,正如亚当斯密的《国富论》论述的那样,分工有助于专业化、有助于提高效率。太抽象了!说了这么多,没讲清楚解耦是什么。的确,笔者也认为这样的解释只能让已经理解了的人再表示一次赞同,无法让原本不理解的人变得理解,这样毫无意义!我该如何诠释?事实上,很多真理是建立在归纳法基础上的。归纳法的好处是见得多了自然就会(归纳似乎是人脑的一种本能),比如诗词,只要熟读唐诗三百首,不会吟诗也会吟。不信你看,先来一篇叫"大漠孤烟直"的,没啥概念;再读一篇叫"空山新雨后"的,有点感觉了;最后"小桥流水人家"你自己就会了。如何写出点有意境的诗,你张口就来"床前明月光",还不是自己写的?如果你去到草原晚上触景生情,即兴来上一句"明月篝火烤肥羊",就能媲美"日照香炉生紫烟"了。所以笔者觉得,最好的方式就是细数那些软件架构中的解耦,让读者从铺陈式的实例中,自己找感觉。

笔者分 3 类 6 组(每类分进程内的应用层和进程间的架构层)给大家举例:

外加中间的 Naming 解析与 Proxy 代理融合的 CNAME 别名,总共 7 个案例。

中间层映射

中间层映射的设计理念是当 A 对 B 有依赖时,A 不要直接依赖 B,而是抽象一个中间层,让 A 依赖中间层,再由中间层映射到 B,从而当 B 变成 C 时,不用修改 A,只用调整中间层的映射关系。中间层映射,在应用层表现为面向接口动态绑定,在架构层表现为 Naming 解析动态绑定。

应用层 - 面向接口动态绑定

面向接口编程的核心思想是"先想清楚做什么,再想让谁来做"。什么叫想清楚了做什么?就是用接口的形式,描述输入什么,输出什么;但接口更多描述的是语法层面,语义层面的刻画还需配合单元测试及其断言(技术上叫 Test Driven),还有文档。这跟企业家们常读的《高效能人士的 7 个习惯》里面讲的"以终为始",思想上如出一辙。让谁来做?就涉及到运行时动态绑定。比如下图:

在 Java 面向对象的语言里,使用方通过 Provider 接口 Response doService(Request r) 来对外刻画它的招标文件。然后三个供应方,LocalProvider、RemoteProvider 和 AsyncProvider 来应标。使用方只使用 Provider 接口,至于它跟哪个具体的 Provider 绑定,完全可以在"采购"时刻动态替换。

面向接口动态绑定的解耦,体现在使用方把依赖的服务抽象为一个接口,依赖这个抽象的接口,而不依赖于具体的服务提供者,以便应对服务提供者变化的可能性。

架构层 -Naming 解析动态绑定

上图是域名服务 DNS 的示意流程。客户端并不直接通过 IP 地址来访问 Provider#A 或 B,而是先询问 Naming 服务,并依据返回的服务列表,再访问 Provider#A 或 B。如果某个 Provider 故障了,可以替换转移到其他的 Provider。出于性能考虑,也可以在客户端把 Naming 的结果缓存起来,并配个缓存更新机制。

基于 ZooKeeper 的应用层名字服务,思想上类似 DNS。不同的是,它基于 TCP 长链接来实现 Server Push,可及时刷新服务列表。

Naming 解析动态绑定的解耦,体现在使用方把依赖的对象或网络进程,抽象为一个名字,名字代表的具体服务提供者则通过 Lookup 机制返回,进而做到如果提供者有变化,只要改变 Lookup 的结果,无需改变使用方代码。

前后节植入

前后节植入的设计理念是服务器是流程的集合,流程是环节的序列。改变一个流程的行为,可以通过在其前后植入一个新环节来实现。前后节植入,在应用层表现为 Chain 拦截模式,在架构层表现为 Proxy 代理模式。

应用层 -Chain 拦截模式

上图是 Strtus2 的架构,每个 Action 的执行,都会被包裹在一系列 Interceptor 里面,形成一条处理链 Chain,每个 Interceptor 会进行 PreHandler 和 PostHandler 处理。这里的 Interceptor 可以增加、删除或替换,以此实现可拓展性。比如可以在 Interceptor 里做鉴权、日志、性能统计、限流等。

Chain 插拔的动态绑定,通过增删替 Interceptor,把过去 URL 与 Action 的 1:1 的处理关系,转变成了 M:N 的处理链。一类请求(某个 URL),可以被多个 Interceptor 处理;一个 Interceptor 也可以处理多类请求。

顺便说一下,Strtus2 这里说的"动态绑定",是配置相对硬编码而言的。严格意义上,这里的绑定是编译期的,不是运行期的,是静态的绑定。类似的架构还有 Spring AOP 和 Servlet Filters 机制。

架构层 -Proxy 代理模式

上图是一个 Proxy 架构模式,这个应用极其广泛。比如 HTTP 的 Nginx,SQL 的 Apache Calcite,memcached 和 redis 的 twitter/twemproxy。为什么?因为 Proxy 对于 Backend 而言就是流量入口,是中间人,能扮演架构层面的 AOP 机制,可拓展性非常强。

当一个请求过来后,刚开始 Proxy 转发给 Backend#A。但是业务发展了,Proxy 也可以转发给 Backend#B 以实现负载均衡,更重要的是 A 和 B 还可以不同的版本,以实现灰度发布。还可以植入 PrePlugin 和 PostPlugin:

在 PrePlugin 里可以做权限控制、流量控制、请求改写、缓存加速、恶意流量拦截、PV 统计、性能 Profile、ChaosMonkey 混沌事件植入等等。

在 PostPlugin 里,还可以做响应报文改写,安全加密(后端不用考虑数据安全,对外时统一加密处理)、压缩加速等等。

两者融合的实例 -CNAME 别名

上图是一种混合模式:既有 Naming 解析,又有 Proxy 代理。而且 Naming 服务,为了支持可拓展,还引入了父子层级,末端的 Naming 服务,完全可以委托给上一层级的 Naming 服务。

在 DNS 里面,我们经常会看到 www.example.org 的域名解析,CNAME 别名到 www.example.org.cdnprovider.com (它是 cdnprovider.com 的子域名),这样客户端不用修改,依然访问的是 www.example.org ,但是对应的后端服务,却不再是直接访问 Provider#A 或 B,而是中间植入了 CNAME Proxy,再由 Proxy 依据 Plugin 的决策,是否转发给问 Provider#A 或 B。

这个设计太棒了!它使得商业公司 cdnprovider.com 给 www.example.org 提供 CDN 服务时,完全是零侵入,不需要修改任何一段代码,只需要在域名服务商那修改 www.example.org 的域名解析,这个操作代表 www.example.org 同意 cdnprovider.com 为他们提供 CDN 服务,代表授权。这一切,都源于基于 Naming 解析的动态绑定实现的解耦。同样的,除了 CDN,我们的恶意流量清洗、灰度发布、性能分析等都可以采用这种方式,实现零侵入插拔。

4. 事件流订阅

事件流订阅的设计理念是将瞬间的过程化调用转变成可回放的指令,对指令的响应可以不用再预定义。事件流订阅,在应用层表现为 Mediator 中介模式,在架构层表现为 Broker 消息模式。

应用层 -Mediator 中介模式

A 直接调用 B,意味着 A 对 B 产生了强依赖。当然我们可以通过面向接口编程,把这个依赖降低,降低到只依赖接口,不依赖实现。简单说,我们只依赖对事情的处理结果,不依赖于如何实现这个处理结果。

但是这还不够,因为我们还依赖了接口,接口意味着对处理语义的刻画。现实中有些情况,连语义的描述都要发生变化,也就是接口都要发生变化,如何进一步解耦呢?如下图:

A 不直接调用 B,而通过中介 Mediator,解耦两步:

  • 先由 A 调用 Mediator: A 持有 Mediator 的引用,执行 Mediator 的方法,即 mediator.publish(e)。

  • 再由 Mediator 调用 B: 为了解耦 Mediator 对外界的依赖,我们用面向接口 EventHandler 来实现依赖反转。让 B 来实现 EventHandler,当然如果 B 已经存在,或更有话语权,依然应该遵循依赖反转的原则,只不过 Mediator 模式的推进方可以再实现一个 Adaptor,来帮助既有的 B 适配到 EventHandler。

有了上述的设计模式后,具体的执行分三步:

  • 订阅:通过 mediator.subscribe(b) 把未来的事件处理提前注册到 Mediator。

  • 发布:A 向 Mediator 发布自己的事件。注意这个理念特别重要,A 仅仅发布发生了什么事情,A 并没有直接调用 B 声明对事情的处理。也就是 A 连对 B 的接口都不再依赖了!举个例子,比如新员工入职,刚开始要为员工办理磁条卡,只是办理磁条卡的供应商可能是甲,也可能是乙。这叫面向接口编程,但这还不够,因为随着公司的发展,现在新员工入职,有人脸识别了,不用再办磁条卡了,而是要登记人脸识别,另外员工福利更好了,对异地公干的新员工入职还会发放一笔安家费,这些都是之前的"接口"没有描述的。

  • 执行:当 mediator 收到 A 的事件后(A 调用了 mediator.publish(e)),mediator 会通过 EventHandler 来回调预先通过 mediator.subscribe(b) 注册的处理类。

上述 Mediator,有些局限性,对所有的 Event,只能有一种 EventHandler。如果我们把 Mediator 升级为一种通用的处理机制,一种平台,自然会有各种各样的 Event,自然会我们会对 Event 做个分类或分组。我们把 Event 的分类或分组,叫做 Topic;而把 Event 理解为 Topic 这个类里面的具体实例。并在 Mediator 里面维护,从 Topic 到 EventHandler 的一组处理器。如下图所示:

可以看到上述架构通过 Map resolver 来维护从 Topic 到 EventHandler 的一组处理。为什么是 List,而不是 EventHandler 呢?为了更加灵活,比如上文提到的「现在新员工入职,有人脸识别了,不用再办磁条卡了,而是要登记人脸识别,另外员工福利更好了,对异地公干的新员工入职还会发放一笔安家费」。

架构层 -Broker 消息模式

上图的 Broker 模式,跟 Mediator 模式其实没有本质的不同,只不过 Broker 更加突出了借助消息中间件 MQ 实现异步。客户端提交一个委托,Broker 持久化完成,并回复 ACK,表示委托已收到。接着委托的消费处理,可以是离线的。通常需要支持 Group 机制:Group 内部多个 Instance 是负载均衡的,它们共同瓜分委托消息的处理;而 Group 间是冗余复制的,它们各自消费各自的,相互之间隔离,有助于实现业务可拓展性。

比如一个新员工入职,它产生一个"新人入职"事件,然后行政部门会为其准备工卡、财务部门会为其准备工资卡、HR 部门会为其缴纳社保。当然,随着公司业务发展,可能还会增加,比如业务部门的业务培训,风控部门的合规性培训等。

跟前面说的 Proxy 模式,相同点在于它们都是在架构层面实现可拓展性。不同点是,Proxy 模式支持的是 PreHandler 和 PostHandler;而 Broker 模式支持的是 MidHandler。

到此,相信大家对"如何理解细数软件架构中的解耦"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

架构 接口 模式 处理 服务 软件 解耦 动态 业务 员工 应用 事件 应用层 中间层 企业 不用 价值 市场 机制 变化 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广东猪兼强互联网科技有限公司 科技互联网企业 美国互联网保险保险科技 互联网最新的科技有限公司 科技制造互联网公司 互联网金融与金融科技 电子科技大学互联网 盛创互联网科技有限公司 滨湖区互联网智能科技产品供应 中国最先进互联网科技公司 阿里巴巴算科技企业还是互联网 阿里互联网科技公司 互联网企业和科技型企业 洛阳共同富裕互联网科技有限公司 群盈汇互联网科技有限公司 南京一网一红互联网科技有限公司 广东鹅鹅鹅互联网科技有限公司 福建互联网展示科技馆设计 互联网科技机房 互联网时代高科技布局 南京欣网互联网络科技有限公司 云信互联网科技有限公司 河南真二互联网科技有限公司 互联网科技媒体 中国的互联网科技企业的崛起 特约风豹互联网科技为什么扣费 互联网科技公司营业执照怎么办理 互联网科技包装股份有限公司上市 云鲸互联网络科技有限公司 互联网科技博主张洲
0