《编程匠艺》读书笔记之十二
第十四章 软件体系结构——奠定软件设计的基础
- 我们已经建造出很多的建筑物,其历史要远远超过我们编写软件的时间,我们现在仍然要学习掌握制作优秀的软件体系结构的要素。
- 体系结构视图的缺失对于软件造成的影响是很大的。这样的系统很难处理,也很难理解,其功能随机的散步在各个模块之间,它的状态糟糕到你唯一能做的事就是把它扔掉。
- 软件体系结构是一种顶级定义,是一种对系统的全面概览,其中刻意避免了过多的细节,它是宏观层面的,而不是微观的。
体系结构视图的作用如下: - 确定关键的软件模块。
- 确定各组件之间是如何相互通信的。
- 有助于鉴别和确定系统中所有重要接口的特性,阐明各个子系统的正确的角色和职责。
- 体系结构不仅描绘了系统是如何构成的,而且也说明了系统以后应该如何扩展,它是影响软件系统设计和未来成长的最大因素,因此,在开发的早期阶段保持体系结构的正确性非常关键。
- 作为一种预先进行的活动,体系结构使我们的第一个将问题域映射到解决域的机会。
在设计体系结构的过程中,我们会制作不同的软件视图,主要包括: - 概念视图。显示系统的主要部分以及它们之间的交互关系。
- 实现视图。从真正的实现模块看问题。
- 进程视图。使用任务、进程和通信来显示动态的结构。
- 部署视图。显示任务在分布式系统中的不同物理节点上如何分布。
- 在概念设计阶段,检查体系结构是否支持你认为应该支持的内容,而不仅仅是架构师们认为应该支持的内容。
- 体系结构被记录在一种高层次文档中,这种文档有时被称为“体系结构规范”。
- 将体系结构放在一个大家都知道的位置上,即一个所有相关人员都可以访问的文档中。
- 体系结构是最初的系统设计,因此,它实在就需求达成一致意见之后的第一个开发步骤。
- 体系结构工作是一种设计形式,但是它独立于模块设计阶段,而且与低层次的代码设计截然不同,体系结构是由很少几个人来确定的。
体系结构完成后,它有以下作用: - 验证。体系结构是我们的第一个对要构建的东西进行验证的机会,高层次设计中难以解决的瑕疵,只会导致在较低层次的设计上有更加危险的故障。
- 沟通。我们使用体系结构规范来将设计传达给所有相关的人员,这是了解系统的主要途径,也是应该在作出更改时及时更新的重要文档。它应该明确的指出了未来如何进行扩展,这有助于保持系统在“概念上的统一性”。
- 判断优劣。我们使用体系结构来帮助我们做出决策,应该确保我们的软件体系具有足够的弹性,它不应该遇到一阵小风或多一些负荷就会倾倒。我们需要用这种系统层名的视角来作出恰当的决策,以确保设计符合所要求的特性。
- 体系结构最关注的是组件和连接,它决定了组件和连接的数量和类型。
- 系统的每一个组件都应该是一个清晰和负荷逻辑的单元,体系结构将定义组件的可见性:它可以看到什么,不可以看到什么,以及什么可以看到它,什么不可以看到它。
- 体系结构将确定所有内部组件的连接情况,并描述连接的属性。
- 良好的体系结构的关键是简洁,它可以只用一个段落来描述,也可以只用一个精致的图标来总结。
- 在一个设计良好的系统中,组件的数目不应该太少,也不应该太多。太少的组件意味着每个模块都将承担过多的工作,这会使结构模糊不清,难以维护,并且难以扩展;过多的小粒度组件会使体系结构混乱,而且难以处理。
- 体系结构将确定系统的关键组件,以及这些组件之间如何相互联系,它不会定义组件的工作方式。
- 一个良好的体系结构不需要详细说明其他可选策略,但是应该证明所选的体系结构是恰当的,并证明选择它经过了慎重的考虑。它会为灵活性留出余地,允许你改变注意。
- 体系结构必须是清晰明确,最好使用已经存在并广为人知的体系结构风格或者框架,体系结构必须是易于理解和应用。
- 熟悉主要的体系结构风格,并了解其优点和缺点,这将有助于你和谐的处理现有的软件,并执行恰当的系统设计。
每种体系结构都具有不同的特征: - 更改数据表示法、算法和所需功能的适应能力。
- 模块分割和连接的方法。
- 全面性。
- 满足性能要求的能力。
- 组件重用方面的考虑。
我们常见的体系结构风格包括: - 没有体系结构。即没有进行规划的体系结构,一般是在项目开始之前就可以肯定是死路一条。
- 分层的体系结构。它使用砌砖的方法,将系统描述为一个分层的层次结构,在任何一点,你都可以去掉所有较低层次,并换成这些层的新实现;较高的层次会使用紧邻的下层的公共接口,它们是否可以使用更低层次的公共接口,则要取决于你对分层的定义。
- 管道和过滤器体系结构。它塑造了整个系统的数据逻辑流,被实现成一串有序的模块,每个模块都会读取一些数据,处理这些数据,然后再将这些数据吐出;它要求良好的定义各个过滤器之间的数据结构;这种体系结构使得添加功能变得十分简单,只要在管道中插入一个新的过滤器就可以了;它的主要缺陷是错误处理,我们可以使用一个单独的错误通道,但是错误信息很容易会乱成一团。
- 客户端/服务器体系结构。这是一种典型的基于网络的体系结构,它将功能分割成两大块:客户端和服务器端。两者之间的通信方式多种多样——最简单的方式就是使用标准的网络协议,不过你也有使用远程过程调用(RPC)、远程SQL数据库查询或者使用某种专用的应用程序协议的情况;有时我们会看到对两层设计的扩展,再引入一层,这样结构被明确的设计为前台显示、逻辑处理和数据存储3部分,也就是我们经常说的MVC。
- 基于组件的体系结构。这种结构分散了控制权,将其分配到许多相互协作的独立组件中,从而不再是一种单一的结构。它是一种面向对象的方法,但是不一定要求使用面向对象的语言实现;这种设计源于使用预制的组件快速的组装好应用程序,这样就可以得到即插即用式的解决方案;这种设计的核心是通信的基础结构或者中间件,它们使得组件可以即插即用,传播它们的存在,并宣称它们所提供的服务;从根本上讲,一个组件就是一个实现单元,它实现了一个特定的公共接口,这个接口就是组件的客户端与组件进行交互的方式。
- 框架。框架是一种可扩展的代码库,它为特定的问题域提供了可重用的设计解决方案;在框架中调用组件是结构与控制流的责任,只有在必要时,它才会调用为你提供的代码。
我们在进行体系结构的设计时,经常使用接口的机制来实现可替换的特性,我们通常使用的接口类型包括: - API。为了替换一个实现特定API的组件,你只需重新实现所有的函数,然后重新链接代码即可。
- 类层次结构。在面向对象的世界中,采取“Interface”的机制。
- 组件技术。通常接口都是以抽象的接口定义语言(IDL)定义的。
- 数据格式。(?????)
- 一个良好的体系结构,会为满足可以预见的需求而在较高的层次上做一些适当的折中。
- 良好的体系结构设计需要很多经验——从真正的软件系统中学习,不断练习设计和优化,经验只能从真正的实践中来,而不仅仅通过旁观别人的工作。对于那些只设计过一个软件版本就称自己是架构师的人,一定要保持警惕。
- 对于软件中的共通模块和整个软件体系结构设计之间的关系:体系结构对共通有深远的影响,或者更准确的说,共通对体系结构有深刻的影响。
- 过度设计的体系结构与设计不充分的体系结构一样危险,如果体系结构所支持的功能太多,将会使产品过于复杂、笨重和缓慢,让人无法接受。过度设计通常意味着即使是进行最简单的修改,都需要涉及许多个组件。过度设计,通常会使组件之间的连接异常复杂。
- 优秀的程序员:1. 理解他们的软件体系结构,并在其中编写新的代码;2. 能够为每个设计情景应用恰当的体系结构;3. 创建简洁的体系结构,既美观又优雅——他们十分看重软件设计的美感;4. 从不断更新的文档中了解系统的体系结构;5. 将与结构有关的问题返回给系统架构师,以期改善设计。
- 糟糕的程序员:1. 编写代码时,完全不理会整体的体系结构,结果造成了许多思虑不周的缺陷和不完善的组件;2. 在编写代码之前,不执行高层次的设计,无视任何可选的体系结构方案;3. 将体系结构信息锁在脑海深处,或放在极有可能过时的规范中;4. 愿意忍受不恰当的体系结构,宁可添加更多设计糟糕的代码,也不去解决底层的问题——他们不想费力的去解决那些复杂的问题。
作者:李潘
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。