体系结构的应用场景
首先需要承认的是,在软件设计的不同阶段需要不同的设计方法。面对不同粒度,不同复杂度的系统,你需要使用不同的思考框架。
体系结构设计与算法设计就需要不同的思考框架。
在算法设计中,动态规划、贪心算法等这些算法思想是非常重要的思考方式/思考框架,他们在一定程度上能够保证快速得到计算的结果。它们面临的场景更多类似于“快速在某个字符串中快速寻找某个子串”,这样一些小问题。
算法虽然能够攻克粒度较小的问题,但是在构建系统的时候,往往面临的问题是:软件系统规模大且复杂性高。这时对系统的全局结构设计和规划更加重要。比如说,给你一个需求,设计一个图书管理系统,这时候动态规划这些思考框架就显得有些力不从心了。这时候,体系结构设计就应运而生了,体系结构设计是一种构建复杂系统时的思考框架,注意它服务的对象是粗粒度的复杂系统,所以你也不要奢望它能对你的算法设计有太大的帮助。(正如你不要期待国家的高层,用它的思考方式去解决 如何耕地的细节问题)
理解软件体系结构
软件体系结构(Software Architecture)包括构成系统的设计元素的描述、 设计元素
之间的交互、 设计元素的组合模式以及在这些模式中的约束。
理解上述抽象概念并不是一件容易的事情,需要一定的工程经验。这里简单谈谈自己的理解。
理解的角度1:(集合角度)
在计算机中,集合是我们一个有利的工具,我们定义事物,理解事物都可以从中获得启发。在集合中,你需要规定基本的元素(这里指构件这些要素),同时还需要制定这些元素的联系方式(这里是指元素的组合模式,及模式中的约束),适当的时候你还需要对具有某些特殊性质的元素进行分类(这里根据元素的作用,将元素分为了两大类:构件 & 连接件)
接下来,你可以从利用常规的集合理论理解该思考框架了。
理解的角度2:(实际意义)
我们人类解决复杂问题的方式,基本都是采用分解的方式,即分而治之。所以给定一个系统的时候,我们尝试将其分解成不同的部分,按照一定的划分标准(比如,功能作用),我们可将其分出实际进行处理的模块(构件、连接件),以及负责连接(负责通信)的要素(分布,约束)等。
举个简单的例子,以 “人体”这个系统为例,根据人体各部分的功能,我们可将人体分为不同的构件:头、手、中间部位身体、腿。但是只具备这些要素还是不够的,他们必须连起来,相互配合才能形成真正的人体。
简而言之,软件体系结构 = 构件 + 连接件 + 约束 。
接下来,进一步说明其他概念。
构件
构件是具有某种功能的可复用的软件结构单元,表示系统中主要的计算元素 和 数据存储。
构件是一个抽象的概念,在程序中可以指程序函数、模块、对象、类等。
连接件
连接是构件间建立和维护行为关联与信息传递的途径。连接包含下面两种要素:
其中,机制指的实际中的消息传递方式。
而协议则决定了 消息的语义理解。
连接件表示构件之间的交互并实现构件之间的连接。
软件体系结构目标
为了更好理解后面的软件体系结构涉及的原则和体系结构风格,请牢记这些目标,时不时的对照后面的内容回顾这些目标。
所有的设计原则等理论基本上都可以映射到下面一个或几个目标上。
体系结构的发展
现在软件的复杂性及多变性,导致了软件粒度越来越粗,越来越开放。
软件设计的原则
设计原则是系统分解和模块设计的基本标准,应用这些原则可以使代码更加灵活、 易于维护和扩展。这里提到的设计原则包括以下几点:
需要指出的是,这些原则有一些交叉的部分,并非完全独立没有交集的,理解这一点,你将能更清楚细微的区别。比如:
通过封装来实现模块化
通过将抽象程度相同的模块放在同一层次,形成层次化的结构。
另外,层次化可以理解为模块化的特例
接下来分别介绍上述原则:
抽象
抽象是关注事物中与问题相关部分而忽略其他无关部分的一种思考方法。
待抽像的例子:
抽象后:
封装
封装和信息隐藏是指每个软件单元对其他所有单元都隐藏自己的设计决策,各个单元的特性通过其外部可见的接口来描述。
【要求】:应将单元接口设计得尽可能简单,并将单元对于环境的假设和要求降至最低。
举例:
模块化
模块化是在逻辑和物理上将整个系统分解成多个更小的部分,其实质是“分而治之” ,即将一个复杂问题分解成若干个简单问题,然后逐个解决。
(一般是先进行逻辑模块化,随后进行物理分配)
举例:
下面的模块化,主要是指逻辑上的模块化,并为涉及到如何将这些模块如何部署到物理机上的过程。
(在物理模块化的时候,可能将模块1和模块3放在同一台机器上,而模块2放在另一台机器上)
系统分解
模块化,不可避免的一个问题是,对于系统如何进行分解。在大量实践中,人们总结出了两个衡量的原则:高内聚、 低耦合。
举个简单的例子:
在一个软件系统中,有三个子系统A、 B、 C都要访问一个关系数据库。
设计方案1:(高耦合方案)
设计方案2:(增加隔离层)
说说这种方案的好处。
设计1中,如果数据库结构发生变化,那么你需要分别在A,B,C中分别进行更改,这样分散的修改,特别容易造成错误。而在设计2中,只需要修改Storage层就可以了。
另外,在A,B,C系统中可能有一些共同的读数据库操作,比如三个系统中都有ReadAnimal()这个操作,设计1中需要分别在A,B,C中进行修改,而设计2中将该共用方法提到Storage模块中,从而修改时,只需要进行一次修改。
PS:
这里加入Storage,是因为假设3个系统中有一些共同的部分。但是如果子系统没有任何共享的数据库数据(每个表被一个系统所独占),那么去掉Storage层也没有关系,因为数据库某个表的变化只会影响到一个子系统。
层次化
先给出层次化的表现形式:
个人感觉,层次化是模块化的一种特例。在模块化的基础上,加入了一些限制。在层次化结构中,layer可以作为一种特殊的模块(包含特定的子系统集合),在这类模块(layer)的联系中,规定了layer模块只能被设计成类似于双向链表的顺序结构,而不能出现树状结构或者图状结构。
层次化的一些说明:
举例:Android操作系统
复用
复用(Reuse)是利用某些已开发的、 对建立新系统有用的软件元素来生成新的软件系统,其好处在于提高生产效率,提高软件质量。
源代码复用:对构件库中的源代码构件进行复用
• 软件体系结构复用:对已有的软件体系结构进行复用
• 框架复用:对特定领域中存在的一个公共体系结构及其构件进行复用
• 设计模式:通过为对象协作提供思想和范例来强调方法的复用