基于组件设计原则剖析代码结构:框架代码结构与组件设计原则

基于组件设计原则剖析代码结构:框架代码结构与组件设计原则

本节关注于分析代码结构的第一个核心主题,即基于组件设计原则来剖析代码结构。组件设计原则很大程度上是一切软件系统设计的基本原则,需要通过实践加深对其的理解和应用。

为什么代码结构要这么设计?

在笔者与很多同学进行沟通和交流时,发现大家在学习开源框架的源码时普遍存在一个问题,即一不小心就扎进细节,没办法找到代码的整体结构。目前市面上能被大家所熟知而广泛应用的代码框架功能强大而完善,代码结构也相对复杂。如果我们没有很好的方法来把握代码的整体结构,在阅读源码时很容易产生一种挫败感。当我们拿到一个框架的源代码时,首先应该问如下一个问题:

为什么这个框架的代码结构要这么设计?

本节内容先从这一问题入手梳理分析代码结构的系统方法。让我们引入本课程中将要介绍的第一个框架:Dubbo。Dubbo 是 Alibaba 开源的一个分布式服务框架,在互联网行业应用和扩展仍然十分广泛。Dubbo 的核心功能为我们进行分布式系统设计提供了两大方案,即高性能和透明化的 RPC 实现方案和服务治理方案。

Dubbo 源代码可以从 https://github.com/alibaba/dubbo 下载(本课程使用的还是应用最广泛的 2.6.x 版本),代码组织结构下图 2-x 所示。我们看到 Dubbo 在代码结构上一共包含 common、remoting、rpc、cluster、registry、monitor、config 和 container 等 8 大核心包。这些包的详细介绍在本文以及后续的文章内容中会有详细介绍,这里暂时不做展开。

02.01

让我们再引入另一个非常主流的开源框架:Mybatis。MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO 为数据库中的记录。

Mybatis 的源码可以从 https://github.com/mybatis/mybatis-3 下载,本课程中使用的是 3.5.x 版本,其代码组织结构下图所示。我们看到 Mybatis 的结构比较复杂,包含了 session、mapping、binding 等 10 余个核心包。

02.02

针对 Mybatis 中各个核心包的详细介绍同样不是本篇文章的重点,在这里我们关注的是从整理结构上把握这些框架的包组织结构。在了解了 Dubbo 和 Mybatis 这两个框架的包结构之后,我们再从前文中“为什么这个框架的代码结构要这么设计?”这个问题出发可以延伸出以下问题:

  • 这些框架的开发人员是如何设计和规划这些代码结构的?
  • 这些代码结构的背后是否遵循了一定的原则?
  • 如何评价这些代码结构的优劣性?
  • 如何从这些框架的代码结构中获取经验从而可以学以致用?

源码阅读需要有突破点,本课程会引导大家逐步挖掘这些突破点。而对以上问题的发散和总结就是我们进行源码解读的一个突破点。想要理解代码结构,我们还是需要从一些基本原理入手,这就是接下去将要介绍组件设计原则。

组件设计原则

组件(Component)设计原则有时候也称为分包(Package)原则,可以用来设计和规划上一小节中提到的 Dubbo、Mybatis 等框架的代码结构。任何一个软件系统都可以看做是一系列组件的集合,良好的组件设计能够把系统分解为一些大小恰到好处的组件,从而使每个开发团队都可以只关注单个的组件而无需关心整个系统。但在我们刚开始阅读某个框架的源码时,为了避免过多的扎进细节关注而只关注某一个具体组件,同样可以使用这些原则来管理我们的学习预期。

对于组件而言,最核心的设计要点就是内聚(Cohesion)和耦合(Coupling),所谓内聚是指一个组件内各个元素彼此结合的紧密程度,而耦合指的是一个软件结构内不同组件之间互连程度的度量。基于这两个设计要点,组件设计原则也包括组件内聚原则(Component Cohesion Principle)和组件耦合原则(Component Coupling Principle)两大类。组件内聚原则用于指导把类划分到包中,而组件耦合原则用来处理包与包之间的关系。

现在我们拿到了 Dubbo、Mybatis 这样的框架源码,也看到了这些框架内容有很多包结构。请注意,我们还没到要弄清楚为什么要将某些类放到同一个包中的时候(避免扎入细节)。从梳理代码结构的角度出发,我们首先应该关注的是组件之间的关系,即应用组件耦合原则来分析代码结构。组件耦合原则也包含以下三条设计原则:

  • 无环依赖原则

无环依赖原则(Acyclic Dependencies Principle,ADP)认为在组件之间不应该存在循环依赖关系。通过将系统划分为不同的可发布组件,对某一个组件的修改所产生的影响不应该必须扩展到其他组件。

  • 稳定抽象原则

稳定抽象原则(Stable Abstractions Principle,SAP)认为组件的抽象程度应该与其稳定程度保持一致。即一个稳定的组件应该也是抽象的,这样该组件的稳定性就不会无法扩展。另一方面,一个不稳定的组件应该是具体的,因为他的不稳定性使其内部代码更易于修改。

  • 稳定依赖原则

稳定依赖原则(Stable Dependencies Principle,SDP)认为被依赖者应该比依赖者更稳定。一个好的设计中的 组件之间的依赖应该朝着稳定的方向进行。一个组件只应该依赖那些比自己更稳定的组件。

从原则的命名上我们也不难看出,组件耦合原则实际上关注的是系统的稳定性(Stablility)。那么什么是系统的稳定性?现实生活中,如果某一个事物不容易被移动,就认为它是稳定的。而在软件开发过程汇总,如果某一个包被许多其他的软件包所依赖,也就是具有很多输入依赖关系的包就是稳定的,因为它的变化可能需要其他依赖它的包做相应的修改,而这种修改显然需要非常大的工作量。

我们来看几个具体的例子。在下图中我们看到存在一个 X 组件被三个其他组件所依赖,我们认为组件 X 是稳定的,因为 X 被很多其他组件依赖。

而在下图中存在一个 Y 组件,但我们认为组件 Y 是不稳定的,因为 Y 没有被其他的组件所依赖,但 Y 自身依赖很多别的组件。

02.04

面试题分析

判断一个框架好坏有什么方法?

  • 考点分析

这个一个比较大的话题,也是考验面试者综合能力的一个常见方法。一般而言,面试官对这种问题本身也不一定有标准答案,更多的是考察面试者的知识面已经对问题的抽象能力。

  • 解题思路

针对该问题,我们可以从很多方面进行切入,如架构的扩展性和维护性等架构设计的方法、设计模式的合理应用等。通过今天内容的学习,我们明白组件设计原则也是判断一个框架好坏的一个重要方面。面试时通过介绍组织设计原则的基本概念,结合具体开源框架中的应用,相信是针对这一问题的一种不大常见但又非常重要的回答方案。

  • 建议回答与本文内容

本文介绍的组件耦合原则部分内容是这个问题的建议答案,同时本文中关于 Dubbo 和 Mybatis 框架的对比也可以作为答案的一部分。

组件在设计耦合都上有什么通用的原则和方法?

  • 考点分析

相较上一个问题,这个问题更加有针对性,直接指向了组件设计原则这一特定话题,需要面试人具备这部分内容有一定的了解。

  • 解题思路

组件设计上核心就是高内聚和低耦合,在设计原则上也包括了组件耦合原则和组件内聚原则这两大组成部分。本文中对其中的组件耦合三大原则进行了介绍,分别是稳定抽象原则、稳定依赖原则和无环依赖原则。这些原则看起来比较抽象,但实际上能够指导我们日常的开发工作。回答这个问题时,先明确这些原则的概念和设计思想,然后最好能列举一两个日常开发中的真实案例。当然,本课程中介绍的关于开源框架的分析也是很好的面试内容。

  • 建议回答与本文内容

本文给出了组件设计原则的总的描述,以及关于组件耦合三大原则的介绍,可以直接作为问题的答案。

小结与预告

本节基于框架代码结构的讨论引出了组件设计原则,并对其中的三条组件耦合原则进行了展开。现实开源框架代码中的包结构通常比较复杂,可能很难找到这些一眼就能判断其稳定性的组件,这时候我们就需要借助一些量化标准来对包结构的稳定性进行衡量。幸运的是,业界已经存在了这样的量化标准以及对应的衡量工作。让我们先来看看具体的量化标准,以及测量这些标准的工具,这就是下一篇内容。

上一篇
下一篇