架构整洁之道中篇(组件构建原则软件架构)

news2024/9/20 14:45:57

目录

1.组件构建原则

1.1.组件

1.2.组件聚合

1.3.组件耦合

2.软件架构

2.1.什么是软件架构?

2.2.独立性

2.3.划分边界

2.4.策略与层次

2.5.业务逻辑

2.6.尖叫的软件架构

2.7.整洁架构

2.8.层次与边界

2.9.Main组件

2.10.测试边界

2.11.整洁的嵌入式架构

3. 总结


1.组件构建原则

1.1.组件

组件是软件的部署单元,是整个软件系统在部署过程中可以独立完成部署的最小实体。

对于Java来说,宏观来看,它的组件是jar文件;微观来看,可以是单个类。常见的组件有:JavaBean、Spring组件等。

1.2.组件聚合

组件聚合是指哪些类应该被组合成一个组件。遵循三大原则:

复用/发布等同原则(REP)

本质:组件的发布和复用应该是等同的。具体来讲:在设计和实现组件时,应该将组件的复用和发布视为同一过程。也就是说,组件的设计应该考虑到它可能会被其他应用程序使用,并且应该尽可能地设计成可复用的、独立的、可测试的和可扩展的。

共同闭包原则(CCP)

本质:将那些一起变化的元素放在同一个组件中。具体来讲:在设计和实现组件时,应该将那些具有相同原因发生变化的元素放在同一个组件中,而将那些具有不同原因发生变化的元素分别放在不同的组件中。这样可以使组件更加独立、可维护和可复用。共同闭包原则是单一职责原则在组件层面的再度阐述。

共同复用原则(CRP)

本质:那些被多个组件一起使用的元素,应该被封装在一个共同的组件中。具体来讲:在设计和实现组件时,应该将那些被多个组件一起使用的元素封装在一个共同的组件中,而不是在多个组件中分别实现。这样可以提高代码的可重用性和可维护性,避免代码的重复实现。不要强迫一个组件的用户依赖他们不需要的东西。

1.3.组件耦合

无依赖环原则:

组件依赖关系图中不应该出现环。

消除循环依赖:

所谓有向无环图(DAG),是指不管我们从该图中的哪个节点开始,都不能沿着这些代表了依赖关系的边最终走回到起始点。也就是说,这种结构中不存在环。

假设某个新需求使我们修改了Entities组件中的某个类,而这个类又依赖于Authorizer组件中的某个类,会形成循环依赖。如何去打破循环依赖呢?

1. 应用依赖反转原则:创建一个User类需要使用的接口,然后将这个接口放入Entities组件,并在Authorizer组件中继承它。

2. 创建一个新的组件,并让Entities与Authorize这两个组件都依赖于它。将现有的这两个组件中互相依赖的类全部放入新组件。

 典型的组件结构图

依赖关系反转 

 让Entities与Authorizer共同依赖于一个新组件

稳定依赖原则:

本质是指依赖关系应该指向稳定的方向。具体来讲:在设计和实现组件时,应该尽量使组件之间的依赖关系指向稳定的方向。也就是说,稳定的组件不应该依赖于不稳定的组件,而不稳定的组件可以依赖于稳定的组件。

自上而下的设计:

自上而下的设计是一种分而治之的方法,将一个大型问题分解为更小、更容易管理的子问题。

2.软件架构

2.1.什么是软件架构?

一个好的软件架构应当做到可靠、易维护和可扩展,使软件系统更容易进行开发、测试、部署和维护。

优秀的架构师会小心地将软件的高层策略与其底层实现隔离开,让高层策略与实现细节脱钩,使其策略部分完全不需要关心底层细节,当然也不会对这些细节有任何形式的依赖。

2.2.独立性

按水平分层和用例解耦一个系统有很多种方式。例如,我们可以在源码层次上解耦、二进制层次上解耦(部署),也可以在执行单元层次上解耦(服务)。

源码层次:我们可以控制源代码模块之间的依赖关系,以此来实现一个模块的变更不会导致其他模块也需要变更或重新编译,这种模式叫作单体结构;部署层次:我们可以控制部署单元(譬如jar文件、DLL、共享库等)之间的依赖关系,以此来实现一个模块的变更不会导致其他模块的重新构建和部署。服务层次:我们可以将组件间的依赖关系降低到数据结构级别,然后仅通过网络数据包来进行通信。

按层解耦:架构师应该知道整个系统的基本设计意图。一个系统可以被解耦成若干个水平分层——UI界面、应用独有的业务逻辑、领域普适的业务逻辑、数据库等。一个系统所适用的解耦模式可能会随着时间而变化。

2.3.划分边界

通过划清边界,我们可以推迟和延后一些细节性的决策,这最终会为我们节省大量的时间、避免大量的问题。

那么应在何时、何处画这些线?边界线应该画在那些不相关的事情中间。GUI与业务逻辑无关,所以两者之间应该有一条边界线。数据库与GUI无关,这两者之间也应该有一条边界线。数据库又与业务逻辑无关,所以两者之间也应该有一条边界线。

插件式架构:软件开发技术发展的历史就是一个如何想方设法方便地增加插件,从而构建一个可扩展、可维护的系统架构的故事。系统的核心业务逻辑必须和其他组件隔离,保持独立,而这些其他组件要么是可以去掉的,要么是有多种实现的。

为了在软件架构中画边界线,我们需要先将系统分割成组件,其中一部分是系统的核心业务逻辑组件,而另一部分则是与核心业务逻辑无关但负责提供必要功能的插件。然后通过对源代码的修改,让这些非核心组件依赖于系统的核心业务逻辑组件。

 插件式结构

2.4.策略与层次

“层次”是严格按照“输入与输出之间的距离”来定义的。也就是说,一条策略距离系统的输入/输出越远,它所属的层次就越高。而直接管理输入/输出的策略在系统中的层次是最低的。

在一个设计良好的架构中,依赖关系的方向通常取决于它们所关联的组件层次。一般来说,低层组件被设计为依赖于高层组件。通过将策略隔离,并让源码中的依赖方向都统一调整为指向高层策略,我们可以大幅度降低系统变更所带来的影响。主要是:单一职责原则(SRP)、开闭原则(OCP)、共同闭包原则(CCP)、依赖反转原则(DIP)、稳定依赖原则(SDP)以及稳定抽象原则(SAP)的运用。

2.5.业务逻辑

业务逻辑是指软件系统中实现特定业务需求的程序逻辑,也就是软件系统中实现的业务规则和流程。

业务实体:关键业务逻辑和关键业务数据是紧密相关的,所以它们很适合被放在同一个对象中处理。我们将这种对象称为“业务实体(Entity)”。

用例:本质上就是关于如何操作一个自动化系统的描述,它定义了用户需要提供的输入数据、用户应该得到的输出信息以及产生输出所应该采取的处理步骤。

 用例示范

为什么说业务实体属于高层概念,而用例属于低层概念呢?因为用例描述的是一个特定的应用情景,这样一来,用例必然会更靠近系统的输入和输出。而业务实体是一个可以适用于多个应用情景的一般化概念,相对地离系统的输入和输出更远。所以,用例依赖于业务实体,而业务实体并不依赖于用例。

2.6.尖叫的软件架构

良好的架构设计应该只关注用例,并能将它们与其他的周边因素隔离。一个系统的架构应该着重于展示系统本身的设计,而并非该系统所使用的框架。

比如,一个住宅建筑设计的首要目标应该是满足住宅的使用需求,而不是确保一定要用砖来构建这个房子。

2.7.整洁架构

所谓整洁架构,具备以下特点:独立于框架(框架是工具);可被测试(业务逻辑);独立于UI(系统UI易变更);独立于数据库(数据库可替换);独立于任何外部机构。

源码中的依赖关系必须只指向同心圆的内层,即由低层机制指向高层策略。任何属于内层圆中的代码都不应该牵涉外层圆中的代码,尤其是内层圆中的代码不应该引用外层圆中代码所声明的名字。

 以基于Web的、使用数据库的Java系统为例,其UML图如下:

 

2.8.层次与边界

 以Hunt The Wumpus游戏UML图为例,所有的箭头都是朝上的,GameRules组件就被放在顶层的位置,是最高层策略组件。

我们来考虑一下信息流的方向。首先,所有来自用户的信息都会通过左下角的TextDelievery组件传入。当这些信息被上传到Language组件时,就会转换为具体的命令输入给GameRules组件。然后,GameRules组件会负责处理用户的输入,并将数据发送给右下角的DataStorage组件。接下来,GameRules会将输出向下传递到Language组件,将其转成合适的语言并通过TextDelievery将该语言传递给用户。

这种设计方式将数据流分成两路。左侧的数据流关注如何与用户通信,而右侧的数据流关注的是数据持久化。两条数据流在顶部的GameRules汇聚。GameRules组件是所有数据的最终处理者。

2.9.Main组件

Main组件是系统中最细节化的部分——也就是底层的策略,它是整个系统的初始点。Main组件也可以被视为应用程序的一个插件——这个插件负责设置起始状态、配置信息、加载外部资源,最后将控制权转交给应用程序的其他高层组件。

2.10.测试边界

软件设计的第一条原则——不管是为了可测试性还是其他什么东西——是不变的,就是不要依赖于多变的东西。譬如,GUI往往是多变的,因此通过GUI来验证系统的测试一定是脆弱的。因此,我们在系统设计与测试设计时,应该让业务逻辑不通过GUI也可以被测试。

2.11.整洁的嵌入式架构

软件构建过程中的三个阶段:

1.“先让代码工作起来”——如果代码不能工作,就不能产生价值。

2.“然后再试图将它变好”——通过对代码进行重构,让我们自己和其他人更好地理解代码,并能按照需求不断地修改代码。

3.“最后再试着让它运行得更快”——按照性能提升的“需求”来重构代码。

3. 总结

本文介绍了组件构建原则和软件架构。

组件是是整个软件系统在部署过程中可以独立完成部署的最小实体。组件聚合可以通过三大原则:复用/发布等同原则、共同闭包原则(CCP)和共同复用原则(CRP);共同复用原则(CRP)。组件依赖关系图中不应该出现环。那么如何消除循环依赖?依赖反转原则或者创建一个新的组件。

软件架构要做到可靠、易维护和可扩展。按水平分层和用例解耦以维持独立性;边界线应该画在那些不相关的事情中间,从而构建一个可扩展、可维护的插件式架构;距离系统的输入/输出越远,它所属的层次就越高,一般来说,低层组件被设计为依赖于高层组件;业务实体是指关键业务逻辑和关键业务数据,适合被放到一个对象;用例定义了输入数据、输出信息以及处理步骤;良好的架构设计应该只关注用例,并能将它们与其他的周边因素隔离;所谓整洁架构,具备以下特点:独立于框架(框架是工具),可被测试(业务逻辑),独立于UI(系统UI易变更),独立于数据库(数据库可替换),独立于任何外部机构。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Edgedetect2

边缘检测,检查数据变化,用异或实现 对于 8 位矢量中的每个位,检测输入信号何时从一个时钟周期变为下一个时钟周期(检测任何边沿)。输出位应在发生 0 到 1 转换后设置周期。 以下是一些示例。为清楚起见,in…

HNU-电路与电子学-小班4

第四次小班讨论 一、题目 1、书 3-41、3-62 2、书 4-23、4-26 3、设计一个时序电路。该电路仅在连续三个或三个以上时钟期间,且两个输入信号 X1 和 X2 相同时,输出信号 Z 为 1,其余情况 Z 为 0。试做出该电路的 Mealy 机和 Moore 机状态…

Windows:设置右键用RStudio打开文件和文件夹

0. 前言 在使用RStudio写R脚本的时候总是要先打开它,再通过它打开脚本和文件夹,感觉不是很方便。由于VSCode以及其他软件都可以整合到右键菜单中打开文件或文件夹,因此就折腾了一下怎么在右键中使用RStudio打开文件,下面是效果展…

简析java JNI技术

前言 认识JNI(Java Native Interface)技术,了解Java调用本地C/C库的简单原理以及一些基本的知识点;自己编写一个自定义的JNI接口。 一、简介 JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代…

在vue3中如何使用百度地图API(详细步骤+demo示例)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、注册账号、申请成为开发者二、申请密钥AK三、在vue3.0中使用百度地图API 提示:以下是本篇文章正文内容,下面案例可供参考 一、注册账号…

htb Mailroom里容器(Debian 11)图形界面显示在本机kali上,socat,unix转发,容器里不安装xrdp

在攻击机kali(ip:10.10.14.18)上运行chisel服务端: chisel server -v -p 60080 --socks5 在靶机的虚拟机(ssh root10.10.11.209)上,执行docker exec containers_sites_1 /bin/bash,进入容器里 进入容器后,先下载kali上的socat和chisel: curl -o /bin/chisel http://10.10.14…

使用JMeter+Grafana+Influxdb搭建可视化性能测试监控平台

【背景说明】 使用jmeter进行性能测试时,工具自带的查看结果方式往往不够直观和明了,所以我们需要搭建一个可视化监控平台来完成结果监控,这里我们采用三种JMeterGrafanaInfluxdb的方法来完成平台搭建 【实现原理】 通过influxdb数据库存储…

初学用于华为鸿蒙系统(HarmonyOS)的编程开发工具HUAWEI DevEco Studio:你好,鴻蒙~

本文是6月6日博文“初学用于华为鸿蒙系统(HarmonyOS)的编程开发工具HUAWEI DevEco Studio”的续篇。 成功通过华为开发者联盟的实名认证审核后,使用远程模拟器(Remote Emulator)运行程序。 步骤如下: 菜单Tools - Device Manager: 点击设备…

Vue列表渲染

1,回顾HTML列表? 答:列表分为顺序列表ol,无序列表ul,用于在网页上以表格的形式进行数据展示,数据放在单元格之中,可以用于布局或者展示某个具体对象的信息。li表示列表的每一项。自定义列表为dl…

C++多态详解(虚函数重写、接口继承、虚函数表详解)

目录 1. 多态概念 2. 多态的定义及实现 2.1 多态的构成条件 2.2 虚函数重写 2.3 C11 override和final 2.4 重载、覆盖(重写)、隐藏(重定义)的对比 3. 抽象类 3.1 概念 3.2 接口继承和实现继承 4. 多态的原理 4.1 虚函数表 4.2…

ESP32设备驱动-VCNL4010光传感器驱动

VCNL4010光传感器驱动 文章目录 VCNL4010光传感器驱动1、VCNL4010介绍2、硬件准备3、软件准备4、驱动实现1、VCNL4010介绍 VCNL4010 专为更短的距离而设计,不超过 200 毫米(约 7.5" ) 并且根据我们的实验,我们发现它在大约 10-150 毫米的距离内效果最佳。这对于检测手…

水表远程监控系统有什么功能吗?

水表远程监控系统是通过远程传输水表数据,实现对水表的远程监控和管理的一种智能化系统。它主要具备以下功能: 1.远程抄表功能:通过远程传输技术,实现对水表的远程抄表和监控,无需人工上门抄表,节省人力成本…

ChatGPT超详细教程-提问、使用、技巧~

huChatGPT已经面世很长时间了,很多人已经开始依赖ChatGPT了,逐渐应用到自己的生活工作学习中去了,然而还有很多人嚷嚷着不好用,真的不好用的话为什么广受好评,还有很多人一直在用? 当然也有可能真的对你没…

uni-app基础

1、基本语言和开发规范 uni-app代码编写,基本语言包括js、vue、css。以及ts、scss等css预编译器。 在app端,还支持原生渲染的nvue,以及可以编译为kotlin和swift的uts。 但是,DCloud提供了使用js编写服务器代码的uniCloud云引擎…

Unity Lighting Mode

在Light中Mode设置为Mixed时,Lighting Mode(在Window->Rendering->Light->Scene)有三种选项如下图: Baked Indirect 烘焙间接光,效果最好性能最耗 混合光源照亮的动态游戏对象将接收: 实时直接光照。烘焙间接…

【iOS_锁】

文章目录 前言锁线程安全锁🔒的作用锁的种类互斥锁 自旋锁加锁原理缺点对比自旋锁的缺点互斥锁的缺点 各种锁OSSpinLock使用OSSpinLockOSSpinLock存在缺陷 互斥锁分为两种: 递归锁、非递归锁 os_unfair_lock 【非递归互斥锁】锁的修饰使用 自旋锁的优先级…

数据结构初阶——堆排序

思维导图: 目录 一,堆排序的概念 二,堆排序的实现 2.1将数组变成堆 2.2堆有序化 二,全部代码 一,堆排序的概念 百度百科的解释如下:堆排序(英语:Heapsort)是指利用堆这种数据结构所设计…

Python——第7章 pandas数据分析实战

7.1pandas常用数据类型 7.1.1一维数组与常用操作 import pandas as pd import matplotlib.pyplot as plt#设置输出结果对齐方式 pd.set_option(display.unicode.ambiguous_as_wide,True) pd.set_option(display.unicode.east_asian_width,True)#自动创建从0开始的非负整数索引…

优化器| SGD/Adam/

前言:最近准备复习一下深度学习的基础知识,开个专栏记录自己的学习笔记 各种SGD和Adam优化器整理 基本概念 优化:最大化或最小化目标函数,具体指最小化代价函数或损失函数 损失函数 J(θ)f(hθ(x),y),h…

RK3568平台开发系列讲解(项目篇)TensorFlow图像分类

🚀返回专栏总目录 文章目录 一、安装tensorflow环境二、图像分类2.1、准备数据集2.2、构建和训练模型2.3、测试模型2.4、TensorFlow Lite模型2.5、模型转换和模拟测试三、部署推理测试沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 TensorFlow 是一个基于数据流编程…