如何设计一个系统?

本文探讨了系统的基本概念,强调了系统由运行中的部分组成,具有特定结构和功能。系统与软件之间存在紧密关系,特别是在软件开发过程中。文章通过Tomcat服务器为例,详细介绍了系统设计的目的、特性要求和总体架构,强调了线程与任务解耦对于提高系统效率的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现在软件变得越来越复杂,于是我们用“系统”这个词来形容现代的软件。正如生态系统、人的循环系统等一样复杂。
当事物复杂到一定程度的时候,我们就不能用过去暴力的方法进行求解了。这时候我们就需要借助完善的、系统的方法来进行求解,这将大大降低开发的代价。

什么叫做系统?

=++




PS:

内部的特性 —> 决定了外部的特性展现。
外界的环境,影响系统特性的展现。

为何定义系统?

我们定义了一个概念指代某类事物,这样子我们才能在此基础上构建相关的衍生概念以及与此概念相关的方法、定理。

同样,我们定义了一个系统的概念,我们才能探索系统相关的设计方法等。

尽管我们经常把“系统”这个词挂在嘴边上,但是你真的知道系统较为准确的含义吗?
下面我们借鉴一下百度百科中的一些内容
系统是指将零散的东西进行有序的整理编排形成的具有整体性的整体。
运动着的若干部分【元素】,在相互联系、相互作用之中形成的具有某种确定功能的整体,谓之系统。(总结为如下关键成分

运行
实体
联系/相互作用

中国著名学者钱学森认为:
系统是由相互作用相互依赖的若干组成部分结合而成的,具有特定功能的有机整体,而且这个有机整体又是它从属的更大系统的组成部分。

钱学森的话,很符合数学公式中关于图的定义:G = (V,E)
(这就是数学抽象的魅力,具有通用性)

如何理解系统

我们可以从三个方面理解系统的概念:

1.系统是由若干要素(部分)组成的。这些要素可能是一些个体、元件、零件,也可能其本身就是一个系统(或称之为子系统)。如运算器、控制器、存储器、输入/输出设备组成了计算机的硬件系统,而硬件系统又是计算机系统的一个子系统。

2.系统有一定的结构。一个系统是其构成要素的集合,这些要素相互联系、相互制约。系统内部各要素之间相对稳定的联系方式、组织秩序及失控关系的内在表现形式,就是系统的结构。例如钟表是由齿轮、发条、指针等零部件按一定的方式装配而成的,但一堆齿轮、发条、指针随意放在一起却不能构成钟表;人体由各个器官组成,单个各器官简单拼凑在一起不能成其为一个有行为能力的人。

3.系统有一定的功能,或者说系统要有一定的目的性。系统的功能是指系统与外部环境相互联系和相互作用中表现出来的性质、能力、和功能。例如信息系统的功能是进行信息的收集、传递、储存、加工、维护和使用,辅助决策者进行决策,帮助企业实现目标。

简化的理解
与此同时,我们还要从以下几个方面对系统进行理解:系统由部件组成,部件处于运动之中;部件间存在着联系系统各主量和的贡献大于各主量贡献的和,即常说的1+1〉2;系统的状态是可以转换、可以控制的。

系统在实际应用中总是以特定系统出现,如消化系统、生物系统、教育系统等,其前面的修饰词描述了研究对象的物质特点,即“物性”, 而“系统”一词则表征所述对象的整体性。对某一具体对象的研究,既离不开对其物性的描述,也离不开对其系统性的描述。系统科学研究将所有实体作为整体对象的特征,如整体与部分、结构与功能、稳定与演化等等。
参考:【百度百科】系统

系统与软件的关系

前面说了半天关于系统的知识,但是这对于编程人员来说,似乎并没有什么卵用。这也是本小节想要解决的问题:理解一般(抽象)系统 与 软件/程序之间的关系

回顾软件开发的过程

过程中,我们能够发现很多有价值的东西。比如:方法论,逻辑的错误(比如,数学推理)。

接下来,我们将结合软件过程中的各个阶段,说明系统在软件中的体现:

需求分析
需求分析的目的在于确定用户的需求,一个比较直接的产物是用例图。需求分析从整体上确定了系统的功能,即系统的对外表现
当然,整体的系统也是可以划分成小的系统/用例的,所以可以将一个大的需求分解为小的需求,并且刻画出需求之间的关系。(需求上的系统就此形成)

概念设计】(TODO : 举个例子)
非编码,抽象的设计。确定必要的元素及元素间的关系以达到某种功能。

=()

详细设计
参考:《深入浅出面向对象分析与设计》
用编程范式(如,面向对象)来刻画概念设计中的逻辑
举个例子:
结构上的构建
对用户信息这一实体,建模成面向对象中类的形式,体现为User类。
User类除了有自身基本信息外,可能还与其他事物存在一些关系,比如说乐器,这时候需要在User中加入乐器类的引用。
流程/业务构建
User使用乐器的流程具体到某个函数中:擦拭乐器 ,奏乐,收起乐器。

这里写图片描述

系统设计案例

由于我们的目的是在系统层面探索,所以在了解一个系统之前,我们需要先了解该系统被设计的目的(服务于哪些需求)。然后再此基础上,自顶向下分析。

【服务器】

接受其他计算机(客户端)发来的请求数据并进行解析,完成相关的业务处理,随后把处理的结果作为响应返回到请求计算机(客户端)

案例 - Tomcat

设计目的

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器
一个Servlet和JSP容器

特性要求

上面的设计目的比较大,不能让人深刻理解。我们对于这些目的进一步细化。
所以作为一个应用服务器,需要考虑哪些方面呢

满足功能需求
高效
稳定性
可靠性
安全性
可扩展性
可管理性

总体架构

针对上面提到的对于各个方面的要求,Tomcat本身按照相应的原则进行了相应的分块,不同的模块往往负责不同的责任,比如说有的模块负责并发高效的性能,有的模块则负责安全。
分块,接口的定义本身就用抽象进行了隔离,有助于可扩展性
而为了进一步提升效率,Tomcat让Executor通过并发提升任务执行的效率

下图的简单说明:

可扩展性】分离请求监听 & 请求处理。 —> Connector & Container/Engine
管理者】Service管理多个Connector 和1个Container/Engine.
web应用】Context来表示,指代Tomcat中部署的其中一个web应用。
应用服务器是用来部署web应用的,是一个运行环境,而不是一个独立的业务处理系统web应用才是。)
一主机多域名:Host】为提供多域名服务,将每个域名视为一个虚拟主机,在每个虚拟主机下面包含多个Web应用。
(注意:Host在这里并非指代IP,而是指域名。)
并发】Executor负责。
这里写图片描述
图片来源:tomcat架构
参考:
Tomcat体系架构(介绍各模块职责)
Tomcat整体架构分析(较为细节)

从设计角度查看上述结构

刻画/设计 系统往往有不同的维度。比如说人的身体,我们可以从血液循环系统,消化系统等方面分别进行研究刻画。
同样软件设计也是,从不同的维度进行设计

比如:
只考虑完成功能维度时,我们设计了和业务相关的类,逻辑。
但当我们考虑效率维度时,我们则需要通过线程将任务处理并发化。

PS : 如果在软件开发时,同时考虑多个维度,那是比较混乱的,比较好的办法是,多次迭代,每次尝试去做一个维度的事情。

功能性需求的设计

在此步骤的设计过程(迭代)中,主要目标在于实现基本的业务目标。即,先实现做什么,而不是做的怎么样

扩展性

从某种角度来看,软件是动态变化的。而变化往往对应着开发人员的的时间、精力的消耗,所以降低变化过程中的消耗,也是十分必要的。(在这方面,好的设计应该尽量使变化发生时 ,代价最小化

当需要增加新的功能时应该如何做?需要某些修改的时候,能否将修改之后的影响最小化?这是本部分需要考虑的目标。

一个简单的例子

现实的业务】有两个实体,人 和 苹果,人可以吃苹果(两个实体的关系)。

计算机刻画】在计算机实现时,我们会建立两个类:Human类以及Apple类,然后在Human中引用Apple类的实例。这是最初版本的程序。

小的演化】但是正如前面所说,程序是会演化的,很快你发现了人也可以吃,然后我又在这Human里面加入了梨的引用。然后建立吃梨的函数。(这里我们姑且将代价量化为2,因为做了两次修改。这里暂且只考虑Human中的修改

代价线性增长】上面的方法似乎是可行的,但是接下来就呵呵了,大量的水果进入(葡萄,香蕉等等),我们设需要加入的水果的数量为n,那么代价是2n。
代价已经很明显了(这里已经进行了量化),那么我们应该如何消除这其中的代价呢?

降低代价】这里用到一个非常非常重要的手段:抽象。掌握了抽象,你会发现抽象能够将你解决方案提升一定的覆盖度(比如,水果的实例 远远 多于苹果的实例,而水果就是比苹果抽象程度更高的概念)。 经过观察发现,梨子、苹果、葡萄等都属于水果,存在着共性,接下来,建立Human 与 水果 之间的引用关系,并更改相应的函数。而更改之后可以处理大部分水果了,即新到来的水果类数目为 n , 造成的在Human中更改的代价 却为 0。

效果】对于Human中的变化,有n种变化时, 代价从 2n 变为了 0

耦合性】耦合性与更改的代价有天然的联系。有兴趣的读者可进一步耦合性是如何一步步降低代价的。

在这方面,Tomcat做了什么?

确定元素】该部分的主要作用,在于Tomcat为了实现其功能,需要包含哪些基本的元素(这些元素可以从不同的角度定义,比如功能,类等等)(这里暂不考虑Tomcat的非核心业务功能,比如日志等)
- 连接元素
- 处理元素
- …

确定元素间的关系】关系设计的好坏,一方面决定了你算法设计,另一个方面也决定了你更改时的代价。
连接元素 将处理请求转发到相应的 处理元素
在建立元素间关系时,Tomcat建立了抽象连接元素与处理元素间的关系

【高效性】 线程与任务解耦

线程池能够达到线程与任务解耦的目的,这意味着线程可以被复用,执行不同的任务。(一个是为了提升效率,一个是为了完成规定的任务)

Tomcat将线程池单独提取出来,放在最外层。(一个请求,让一个线程进行处理。也可以理解为线程执行相应的Servlet方法

为何采用多线程?】Tomcat是用作服务器的,而其承载的业务往往是多样的,外界不同的请求往往对应着后台不同的业务实体,有很多情况下,这些业务实体是相互独立(不相互影响的),或者是同一个页面只能被访问,而不是同时被修改,这样的一些场景是非常适合于并发处理的。

为何采用线程池?】任务的数量很多,如果每个任务都绑定一个线程,而任务结束则销毁相应的线程,分配以及销毁线程的代价是很大的,这将占去相当大的一部分时间,线程池的目的在于:省去过多的分配及销毁线程的时间

职责分明】线程池的目的在于执行任务,CPU调度。 而任务(Runnable)的目的在于完成相应的业务功能。

根据网络相关的实际结构(如,IP与域名),定制了层次结构。(Engine –> Host –> Context)
为何设置监听器?用来进行哪方面的通信?(方便注册,通知相关的组件(比如,Context监听着Host,所以Host收到相应的消息时,可以通知相应的Host))
负责集群的模块。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值