这篇吸引人的练习探究了体系结构和设计方面的关系,并着重提出了它们可能对应用程序的编码和性能产生影响的方式。 每个人都同意好的体系结构是最基本的,但是并不是在日常的工作中都明白它的含意。本文用了一个例子来说明在一个小规模下的体系结构和设计的关系-- 但是即便在最大的组件解决方案也要用到相同的思想。
我把它作为一个练习组织起来,其后是我的解答和讨论。先做练习吧 !
练习
下面,您将寻找对于一个图书馆管理系统的需求摘要及部分的类模型。仔细的阅读它们,确认您理解了借阅的规则。然后回答以下问题
问题 1
为什么既要有 LibraryBook 类又要有 LibraryBookCopy 类?
问题 2
LibrarySystem 对象的目的是什么?当然我们寻找的类就是这个系统!
问题 3
画出借一本书的顺序图(从图书馆找出书)-- 开始于 LibrarySystem.checkOut(aLibraryBookCopy, aLibraryUser)
图书馆需求
我们为正在设计一个图书馆借书系统。图书馆系统的一个需求是它允许人们在图书馆进行注册以后借阅一本书籍,但必须满足以下系列三个条件:
1. 用户不能同时借阅超过最大数量的书籍。最大数量依据用户类型:一个成年用户每次可以最多借 7 本书籍,而未成年人最多是 4 本。
2. 用户不能同时借阅两本相同的书籍。
书籍必须可用于出借:书籍不能被预定也不能再同时借给其它用户。
如果这些条件能被满足,那么书籍就可以被借阅一段固定的时间。
图书馆类模型
图 1 - 图书馆系统的部分类模型
解答
A1 - 既然很多信息会在 LibraryBook 中共享,我们把书名(LibraryBook)和它的所有复本(LibraryBookCopy)区分开来。举个例子,要预定书 - 系统就可以决定分配哪一本复本,亦或买本新的。 注意到这并不是由在此所提出的需求摘要所暗示的。
A2 - LibrarySystem 类可能叫 Library 的名字更好一点,但就象你将在下面看到的它当然需要有一定的责任。
A3 - 这里并没有确定的答案 - 两个可能的答案展示在图 2 和图 3 - 它们将引起我们的讨论。
图 2 - 对于向用户借出图书馆书籍的设计 #1
图 3 - 对于向用户借出图书馆书籍的设计 #2
讨论
图 2 和图 3 说明了两种可能的完成借书给某一个用户这一需求所做的面向对象的设计。两种设计都具有三个相同的对象:LibrarySystem、LibraryUser(代表借书用户的对象)和 LibraryBookCopy (代表书籍本身的对象)。两种方案的差别在于责任在对象之间的分布方式和对象之间的合作本质。
** 在设计 #1,LibrarySystem 负责借书给用户。LibrarySystem 通过询问 LibraryUser 和 LibraryBookCopy,确信上面列出的条件都被满足了,然后 LibrarySystem 告诉 LibraryBookCopy 它目前正在被借阅,同时告诉LibraryUser 它正在借这本书。
** 在设计 #2,LibrarySystem 仅负责把借书给用户的责任委托给 LibraryBookCopy 对象 - LibraryBookCopy 有效地把自己借出。为了做到这一点,LibraryBookCopy 对象首先确定条件 3 是被满足的,接着告诉 LibraryUser 来借它。LibraryUser 对象负责确认条件 1 和条件 2 是被满足的,然后告诉自己正在借书。控制权返回给 LibraryBookCopy,LibraryBookCopy 告诉自己目前正在被借阅。
两个设计在功能上都是相同的:它们都要检查所有三个条件是否被满足,然后确定 LibraryUser 知道它正在借阅 LibraryBookCopy,LibraryBookCopy 知道它正被 LibraryUser 借阅。它们的不同之处在于它们的结构。软件的体系结构处理的是系统的结构而不是其功能。
体系结构选择
如果两个设计在功能上相同,应该选择哪个? 这个表格总结了两个设计之间的差别
表中的开始三个条目描述了两个方法的不同的本质。功能定位和合作本质都是技术差别:它们说明了责任怎样在对象之间分布,以及对象之间为了应付整个责任是怎么进行通信的。设计理念就是推理风格,它引导作出技术的决定。这对于一致的体系结构起着很重要的作用,因为设计的决定并不是在真空中做出的("我怎么考虑它?""我怎么实现它?")而是在一个有支持作用的上下文中("体系结构把这样的事物描述成什么样?"和"类似这样的事物在这里是怎样实现的?")做出的。这就是"体系结构视点"的概念。
最后的四个条目都是说明了两种设计在它们的非功能特性方面的差别。规模和性能是可以衡量的差别。因为两个系统做了相同的事情,所以复杂程度是一样的,但是它们在不同的方面表现其复杂性。考虑两种类型的变化:添加新的服务(就像允许用户在一个特定的日期预定项目)和添加新类可以被借出的事物(就像 CD 或录像带)。对于每个设计来说,实现这些变化是不同的。设计 #1 能更好的适应第一种变化,因为所有的对于预定的规则被加到了 LibrarySystem ,一个新的 Reservation 对象是简单和被动的。设计 #2 将有一个更复杂和主动的 Reservation 对象,在 LibraryUser 和 LibraryBookCopy 之间有更复杂的交互。对于第二种变化,设计 #2 比设计 #1 进行得更好,因为一个 CD 负责其自身的借出,所以任何差异或增加的需满足的条件(例如对用户的租借收费)被隐藏在loanTo: 消息的后面 - 没有其他对象需要改变。在设计 #1 中实现这个变化需要一个新的 CD 对象和在 LibrarySystem 中添加新代码。所以,在规模、性能和复杂本质方面的差异说明了设计风格的选择直接影响了系统的非功能特性。这是体系结构的选择。大多数人通常把"体系结构"和系统设计最上层的大规模功能块联系在一起,但是关于功能定位和通信本质的相同问题却必须在系统设计的各层被提出。体系结构是与结构和功能分布相关的,不是直接和功能有关的:在许多功能相同的设计中,选择出最符合用户需求的设计。