组件设计原则和度量方法

在日常开发过程中,Spring、Dubbo、Mybatis等都是我们常用的开源框架。当你在使用这些框架时,不可避免需要通过分析源码来理解内部的实现原理。那么,你在翻阅源代码时,有没有想过这些框架的代码结构为什么要这样进行设计和实现呢?背后是否有一些组件设计的原则呢?这就是今天我们要讨论的话题。

我们知道,组件(Component)是设计和规划软件系统代码结构的基本单元,而在代码结构设计上最重要的关注点就是耦合(Coupling)度,用来处理组件与组件之间的关系。这里,我们以Dubbo框架为例进行切入并给出它的组件关系图。Dubbo框架的源码比较复杂,从顶层的代码结构进行梳理,我们可以得到这样的包结构图。


我们看到Dubbo在代码结构上一共包含common、remoting、rpc、cluster、registry、monitor、config和container等8大核心包,它们之间相互依赖构成一个整体。但这里的依赖关系并不是随意设计的,而是使用到了经典的组件设计原则(Component Design Principle)。

什么是组件设计原则?

组件设计原则有时候也称为分包(Package)原则。针对耦合度,在组件设计上包含以下三条设计原则: 

  1. 无环依赖原则

无环依赖原则的英文全称是Acyclic Dependencies Principle,它的含义很明确,就是说组件与组件之间的关联关系中不应该存在环状结构,我们要避免形成循环依赖。

  1. 稳定抽象原则

稳定抽象原则,即Stable Abstractions Principle。该原则认为如果一个组件是稳定的,那么它就应该是抽象的。反之,如果一个不稳定的组件中就不应该包含很多抽象的内容。

  1. 稳定依赖原则

稳定依赖原则,即Stable Dependencies Principle,强调的也是稳定性,认为组件与组件之间的依赖关系应该是有方向的,也就说一个组件只应该依赖于比它更稳定的组件,反之就是不合理的。

从原则的命名上我们也不难看出,组件耦合原则实际上更多关注的是稳定性(Stablility)。那么什么是稳定性呢?在软件系统中,如果某一个包被许多其他的软件包所依赖,也就是具有很多输入依赖关系的包就是稳定的,例如下图中的这个X组件。


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


现实中的诸如Dubbo这种框架代码中的包结构通常比较复杂,可能很难找到这些一眼就能判断其稳定性的组件,这时候我们就需要借助一些量化标准来对包结构的稳定性进行衡量。让我们一起来看一下。

组件设计原则的度量方法

我们先来看看组件的稳定度如何进行度量,我们可以使用以下公式:

I = Ce / (Ca + Ce)

其中Ca代表向心耦合(Afferent Coupling),表示有多少个组件依赖与这个组件。而Ce代表离心耦合(Efferent Coupling),表示这个组件本身所依赖的组件数量。I代表Instability,即不稳定性,显然它的值处于[0,1]之间。

针对前面介绍的X和Y两个组件,我们可以使用该工作做一个简单计算。不难得出组件X的Ce=0,所以不稳定性I=0,说明它非常稳定。相反,组件Y的Ce=3,Ca=0,所以它的不稳定性I=1,说明它非常不稳定。

另一方面,组件的抽象度也同样存在类似的计算公式:

A = AC / CC

其中A代表抽象度(Abstractness)。AC(Abstract Class)表示组件中抽象类的数量,而CC(Concrete Class)表示组件中所有类的总和,这样通过对比AC和CC就能简单得出该组件的抽象度。

事实上,组件之间都存在一个依赖链,稳定性在该依赖链上具有传递性。下图展示的是一种更常见的场景,沿着依赖的方向,组件的不稳定性应该逐渐降低,稳定性应该逐渐升高。如果已经处于稳定状态的组件就不应该去依赖处于不稳定状态的组件。


另一方面,正如上图所展示的,大多数组件即具备一定的稳定性也表现出一定的抽象度。如果一个组件的稳定度和抽象度都是1,意味着该组件里面全是抽象类且没有任何组件依赖它,那么这个组件就没有任何用处。相反,如果一个组件稳定度和抽象度都是0,那么意味着这个组件不断在变化,不具备维护性,这也是我们不想设计的组件。所以,在稳定度和抽象度之间我们应该保持一种平衡,下图中中间的那个线就是平衡线。在有些资料中,这条平衡线有一个专业的名称,即主序列(Main Sequence),如下图所示。


我们用距离(Distance)的概念来量化这种平衡,距离的计算公式:

D = abs(1 - I - A) * sin(45)

距离的图形化表示参考下图。


通过这些度量方法,我们就可以全面分析组件的稳定度、抽象度以及与主序列之间的平衡关系。

讲到这里,你可能会问,如何能够有效计算出这些度量方法背后的具体量化数据呢?通过人工的方式进行计算显然不可行,这时候就需要引入一些专门的测量工具了。

组件设计原则的测量工具

这里介绍一款组件关系分析的利器:JDepend。JDepend是用来评价Java代码是否遵循组件设计原则的便捷工具,可以给出代码工程中包与包之间的依赖关系,并分析出每个包的稳定和抽象程度以及是否存在循环依赖等。这些指标与前面中介绍的组件设计量化标准保持一致。

使用JDepend时,我们一般加载它为Eclipse提供的插件。安装完JDepend插件之后,在Eclipse中会出现一个“Run JDepend Analysis”菜单。


直接执行命令,就可以得到JDepend的分析结果了。接下来,我们还是以Dubbo为例,来看一下该框架中位于整个依赖关系中心位置的dubbo.rpc,可以看到如下图所示的分析结果。


JDepend给出了四个子页面,分别是所选中的对象、存在循环依赖关系的包、多依赖的包和被依赖的包。从图中,我们看到具体类(CC)、抽象类(AC)、向心耦合(Ca)、离心耦合(Ec)、不稳定性(I)、抽象度(A)和距离(D)等组价设计原则中所介绍的指标数量,同时还使用“Cycle!”用来标识是否包结构是否存在循环依赖。当然,针对这些可视化界面,JDepend还提供了完整的文本结果描述。

同时,JDepend还为我们自动生成了主序列图以及各个包在该图中的分布情况。对于com.alibaba.dubbo.rpc包而言,得到的效果如下图。


在上图中,我们点击某个点,可以看到该点所代表的包结构中的不稳定性、抽象度和主序列之间的距离值。而图中所分布点分为三种颜色,绿色集中主序列线附件,代表在不稳定性和抽象度之间达成了比较好的一种平衡。黑色点位则相对差一下,如果红色点位,则表示设计上出现了问题,需要引起我们的注意。

可以说,任何组件的设计在耦合度上都不可能是完美的,对于复杂的系统而言尤其如此。因此,更多的时候,我们追求的是一种平衡性。组件设计原则为我们追求这种平衡性提供了很好的理论依据和量化标准,我们可以通过工具将这些理论依据和量化标准转化为工程实践,从而更好地指导我们的日常开发工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/789481.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

爬虫-豆瓣读书排行榜

获取数据 requests库 获取数据环节需要用到requests库。安装方式也简单 pip install requests 爬取页面豆瓣读书 Top 250 用requests库来访问 import requests res requests.get(https://book.douban.com/top250/) 解析: 导入requests库调用了requests库中的…

昇思14天

ResNet50图像分类 1. ResNet50图像分类概述 ResNet50是一种用于图像分类的深度卷积神经网络。图像分类是计算机视觉的基本应用,属于有监督学习范畴。ResNet50通过引入残差结构,解决了深层网络中的退化问题,使得可以训练非常深的网络。 2. …

看到指针就头疼?这篇文章让你对指针有更全面的了解!

文章目录 1.什么是指针2.指针和指针类型2.1 指针-整数2.2 指针的解引用 3.野指针3.1为什么会有野指针3.2 如何规避野指针 4.指针运算4.1 指针-整数4.2 指针减指针4.3 指针的关系运算 5.指针与数组6.二级指针7.指针数组 1.什么是指针 指针的两个要点 1.指针是内存中的一个最小单…

Apache中使用CGI

Apache24 使用Visual Studio 2022 // CGI2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <stdio.h> #include <stdlib.h>#include <stdio.h>void main() {//设置HTML语言printf("Content-type:text/html\n\n&q…

Ansys Zemax|什么是点扩散函数( PSF )

附件下载 联系工作人员获取附件 概览 这篇文章讲述了&#xff1a; 什么是点扩散函数&#xff1f; 点列图 快速傅里叶变换计算的点扩散函数&#xff08;FFT PSF&#xff09; 惠更斯算法计算的点扩散函数&#xff08;Huygens PSF&#xff09; 如何使用非序列模式下的透镜和…

地下水环评(一级)实践技术及Modflow地下水数值模拟

主要围绕的环评导则&#xff0c;结合不同行业类别&#xff0c;实例讲解地下水环境影响评价的原则、内容、工作程序、方法。包括数据处理分析、数值模型构建以及环评报告编写等。涉及地下水流场绘制软件&#xff08;Surfer&#xff09;的操作流程及数据处理、地下水数值模拟软件…

JVM:类的生命周期

文章目录 一、介绍二、加载阶段三、连接阶段1、验证阶段2、准备阶段3、解析阶段 四、初始化阶段 一、介绍 类的生命周期描述了一个类加载、连接&#xff08;验证、准备和解析&#xff09;、初始化、使用、卸载的整个过程。 二、加载阶段 加载&#xff08;Loading&#xff09…

【论文速读】| JADE:用于大语言模型的基于语言学的安全评估平台

本次分享论文&#xff1a;JADE : A Linguistics-based Safety Evaluation Platform for Large Language Models 基本信息 原文作者&#xff1a;Mi Zhang, Xudong Pan, Min Yang 作者单位&#xff1a;Whitzard-AI, System Software and Security Lab Fudan University 关键…

JavaWeb__正则表达式

目录 1. 正则表达式简介2. 正则表达式体验2.1 验证2.2 匹配2.3 替换2.4 全文查找2.5 忽略大小写2.6 元字符使用2.7 字符集合的使用2.8 常用正则表达式 1. 正则表达式简介 正则表达式是描述字符模式的对象。正则表达式用于对字符串模式匹配及检索替换&#xff0c;是对字符串执行…

用SurfaceView实现落花动画效果

上篇文章 Android子线程真的不能刷新UI吗&#xff1f;(一&#xff09;复现异常 中可以看出子线程更新main线程创建的View&#xff0c;会抛出异常。SurfaceView不依赖main线程&#xff0c;可以直接使用自己的线程控制绘制逻辑。具体代码怎么实现了&#xff1f; 这篇文章用Surfa…

vue 中 使用腾讯地图 (动态引用腾讯地图及使用签名验证)

在设置定位的时候使用 腾讯地图 选择地址 在 mounted中引入腾讯地图&#xff1a; this.website.mapKey 为地图的 key // 异步加载腾讯地图APIconst script document.createElement(script);script.type text/javascript;script.src https://map.qq.com/api/js?v2.exp&…

C++11中重要的新特性之 lambda表达式 Part two

序言 在上一篇文章中&#xff0c;我们主要介绍了 C11 中的新增的关键词&#xff0c;以及 范围for循环 这类语法糖的使用和背后的逻辑。在这篇文章中我们会继续介绍一个特别重要的新特性分别是 lambda表达式 。 1. lambda表达式 1.1 lambda的定义 C11 中的 lambda表达式 是一种…

APB总线协议

一、APB总线介绍 关于总线的一些概念&#xff1a; 总线&#xff1a;计算机内部和计算机之间传输数据的共用通道。 总线位宽&#xff1a;总线能够一次性传送的二进制数据位数&#xff0c;例如8bit、16bit、32bit、64bit等。 总线工作频率&#xff1a;即时钟频率&#xff08;时…

PHP实现用户认证与权限管理的全面指南

目录 引言 1. 数据库设计 1.1 用户表&#xff08;users&#xff09; 1.2 角色表&#xff08;roles&#xff09; 1.3 权限表&#xff08;permissions&#xff09; 1.4 用户角色关联表&#xff08;user_roles&#xff09; 1.5 角色权限关联表&#xff08;role_permissions…

【内网渗透】内网渗透学习之域渗透常规方法

域渗透常规方法和思路 1、域内信息收集1.1、获取当前用户信息1.1.1、获取当前用户与域 SID1.1.2、查询指定用户的详细信息 1.2、判断是否存在域1.2、查询域内所有计算机1.3、查询域内所有用户组列表1.4、查询所有域成员计算机列表1.5、获取域密码信息1.6、获取域信任信息1.7、查…

最短路径算法:Dijkstra算法探险记

想象一下,你是一只小蚂蚁,名字叫小明。你住在一个大大的花园里,这个花园有很多小路,小路之间还有交叉点,就像是一个迷宫一样。现在,你接到了一个任务:找到从你家到花园里一个特定地方(比如一块超级大的糖果)的最短路径! 第一步:画出地图 首先,我们需要一张地图来…

YOLOv8改进 | 注意力机制 | 增强模型在图像分类和目标检测BAM注意力【小白必备 + 附完整代码】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

【模块化与包管理】:解锁【Python】编程的高效之道

目录 1.什么是模块&#xff1f; 2. 模块的导入过程 3. 理解命名空间 4. import语句的多种形式 5. 模块的执行与重新导入 6. 包&#xff08;Package&#xff09; 7. sys模块和os模块 sys模块 常用属性 示例&#xff1a;使用sys模块 os模块 常用功能 示例&#xff1…

前端埋点数据收集和数据上报

原文地址 什么是埋点 学名叫时间追踪(Event Tracking), 主要针对用户行为或者业务过程进行捕获&#xff0c;处理和发送相关技术及实施过程. 埋点是数据领域的一个专业术语&#xff0c;也是互联网领域的俗称&#xff0c;是互联网领域的俗称 埋点是产品数据分析的基础&#xf…

【AIGC】一、本地docker启动私有大模型

本地docker启动私有大模型 一、最终效果中英文对话生成代码 二、资源配置三、搭建步骤启动docker容器登录页面首次登录请注册登录后的效果 配置模型尝试使用选择模型选项下载模型选择适合的模型开始下载 试用效果返回首页选择模型中英文对话生成代码 四、附录资源监控 五、参考…