(零雨其蒙原创 转载请注明)
2007
年
3
月
14
日星期三
第
37
章
使用模式设计持久性框架
持久性对象
Persistent Object
是指需要持久性存储的对象。
框架
好莱坞原则
即“不要给我们打电话,我们会打给你”。意思是,用户定义的类(例如新的子类)将从预定义的框架类接收消息。这通常是通过实现超类的抽象方法来实现的。
这种方式就是说框架(容器)来调用用户自定义的类,而不是相反,其实就是
Rod Johnson
所谓的“
IoC
”,而被
Martin Fowler
进一步解释的“依赖注入”(
DI
)。
线程安全
作为技术服务子系统,持久性服务(其他的很多服务也一样)应该被设计成线程安全的。
关键思想
映射(
mapping
)
在类和持久性存储(例如,数据库中的表)之间,对象属性和记录的域(列
,字段)之间必须有某种映射关系。也就是说,在这种模式之间必须有模式映射(
schema mapping
)
。
模式映射在
Hibernate
和
iBatis
等现代框架中都是包含在
XML
文档中的,在
Java
中对象由
POJO
来担当,
Larman
已经说得足够详细。
对象标识(
object identity
)
为了方便将记录与对象联系起来,确保没有不适当的重复,记录和对象必须有唯一的对象标识。
数据库中会有完全相同的两条记录吗?按理来说应该没有吧(正常情况应该没有,也可能某个表没有设关键字?)
数据库映射器(
database mapper
)
负责具体化和虚化的纯虚构数据库映射器。
具体化和虚化(
materialization and dematerialization
)
具体化是指将持久存储中数据的非对象表示(例如记录)转换为对象。虚化是指与具体化相反的动作,也称为钝化(
passivation
)。
缓存(
cache
)
持久性服务为提高性能缓存具体化后的对象。
对象的事务状态(
transaction operation
)
就对象与当前事务而言,了解对象状态是有用的。例如,了解哪些对象已经被修改以便决定是否需要将它们存入持久存储中。
事务操作(
transaction operation
)
提交和回滚操作。
滞后具体化(
lazy materialization
)
并非一开始就具体化所有对象,只有当需要时才具体化特定实例。
虚代理(
virtual proxy
)
滞后具体化可以通过使用
(
称为虚代理的
)
智能引用(
smart reference
)来实现。
模式
将对象表示为表
将对象表示为表(
Representing Objects as Tables
)模式
[BW96]
建议为每个持久对象类在关系数据库中定义一个表。包括基础数据类型(
number
,
string
,
boolean
等)在内的对象属性将映射为列。
如果是这样,或许就要改变传统的从设计数据库开始设计系统了,数据库中的表是通过持久性对象来确定的。
对象标识符
对象标识符(
Object Identifier
)模式
[BW96]
建议给每个记录和对象(或对象的代理)分配一个对象标识符(
OID
)
动机
:
为了确保记录的重复具体化不会导致重复对象,需要有关联对象和记录的一致性方法。
数据库映射器模式
数据库代理(
Database Broker
)
[BW95]
模式,即创建一个类来负责对象的具体化、虚化和缓存。该模式也被称为数据库映射器(
Database Mapper
)模式
[Fowler01]
。
Template Method
模板方法(
GoF
)
该模式的思想是,在超类中定义一个方法(模板方法),超类定义了算法的框架,其中既有固定部分也有变化部分(
就是那些可以被子类重写的部分)。模板方法调用其它一些方法,这些方法中有些可能会被子类覆写。因此,子类可以覆写(重写
override
)这些变化的方法,以此在变化点增加自己特有的行为。
具体实现还会引入回调函数,钩子函数这样的技术,等有空我会就
C++
,
Delphi
,
Java
和
C#
来分析一下这个主题。
缓存管理模式
缓存管理(
Cache Management
)模式
[BW96]
建议由数据库映射器负责维护缓存。如果每个持久性对象使用不同的映射器,那么每个映射器就可以维护自己的缓存。
事务状态
当执行删除或保存操作时,都是先将持久性对象转换成适当的状态(就是由数据库中将对象提出将其状态设为
Old Clean
),然后执行提交或回滚方法时,再真正修改数据库。这和
DBMS
做的事务管理原理差不多,中间做一个过渡,就好比要把一个文件从
C
盘转移到
D
盘,使用剪切,将文件保存在内存中(文件的状态改变了,位置从硬盘变成了内存),然后当在
D
盘执行粘贴命令时,才会真正将文件转移过来。这里面的内存就相当于
Old Clean
状态。
State
状态模式(
GoF
)
问题:
对象的行为依赖于它的状态,而它的方法中包含能够反映依赖状态的条件动作的
case
逻辑。是否存在替代条件逻辑的方法?
解决方案
:给每个状态创建状态类,并实现一个公共的接口。将语境对象中的依赖于状态的操作委派给其当前的状态对象。确保语境对象总是向反映其当前状态的状态对象。
听上去有些拗口,但时间上没那么复杂,其实还是使用的接口和多态(好多
GoF
模式用的都是这种方法),给持久性对象增加一个状态属性(类型是接口),然后与状态对象接口形成对应关系,存在不同的状态类来实现这一接口,根据持久性对象地状态属性被赋值为哪个具体的状态类,来决定调用哪个状态类的方法(多态)。详细情况看第
461
页的图就可以了。
P461
在性能方面,这些状态对象实际上是无状态的(没有属性)。因此,这些类只需要有一个实例(每个类都是单实例类)。例如,数以千计的持久性对象可以引用同一个
OldDirtyState
实例。
Command
命令模式(
GoF
)
问题:
如何处理需要诸如排序(优先级)、排队、延迟、记录日志或重做等功能的请求或任务?
解决方案
:为每个任务创建一个类,并实现共同的接口(这句话一出,往往就意味着使用多态)。
P462
动作成为了对象,因此可以被排序、记录日志、排队等。
虚代理
Virtual Proxy
是其他对象(
real subject
)的代理,当它第一次被引用时具体化该对象。
在
Java
中,可以使用动态代理实现虚代理。(需要再好好看看
Java
的动态代理)
第
38
章
UML
部署图和构件图
部署图
部署图表示的是,如何将具体软件制品(例如可执行文件)分配到计算节点(具有处理服务的某种事物)上。部署图表示了软件元素在物理架构上的部署。
构件图
构件
Component
表示封装了其内容的系统模块,它在其环境中的表现形式可以被替代。构件通过所提供的和所需要的接口定义了其行为。同样,如果构件作为类型,那么它的一致性是通过这些所提供的和需要的接口来定义的。
[OMG03b]
第
39
章
架构的文档化:
UML
和
N+1
视图模型
软件架构文档
SAD
SAD
描述有关架构的总体想法,包含架构分析的关键决策。
从本质上讲,
SAD
是对架构性决策(例如技术备忘录)的总结以及对
N+1
架构视图的描述。
N+1
(或
4+1
)视图模型
4+1
视图模型
4
个视图分别是:逻辑、进程、部署和数据。“
+1
”视图指的是用例视图。
架构视图的细节
逻辑视图
进程视图
部署视图
数据视图
安全视图
实现视图
开发视图
用例视图
准则
不要忘记动机!
也就是要把为什么这样设计写清楚!
Struts
框架用到的模式
ActionServlet
是访问表现层的外观。
ActionServlet
和
Action
对象的设计采用了命令处理器模式。
ActionServlet
扮演了命令处理器角色,接收请求并将它们映射为
Action
(
Action
是
Command
对象
)对象,
Action
对象负责执行请求。
ActionServlet
是前端控制器,处理请求的初始接触点。
Action
对象是业务委派——向“业务”或领域层服务委派的抽象。
Action
对象也扮演了适配器的角色,将框架调用适配为领域层对象的接口。
ActionServlet
实现了模板方法模式,
process
是模板,
processXXX
是钩子方法。
第
40
章
迭代式开发和敏捷项目管理的进一步讨论
迭代开发各阶段的目标
P487
UP
是用例驱动的
,这意味着工作是围绕用例的实现来组织的。也就是说,每次迭代都要实现若干个用例或者用例场景(如果用例太复杂不能在一次迭代中完成)。
第一次迭代
选择要选择重要用例的简单场景或理想路径场景(主成功场景)。
在细化阶段
需要处理与该用例相关的具有重要架构意义的不同需求,这要可以迫使团队接触到架构的各个方面:主要层、数据库、用户界面、主要子系统之间的接口等。这就有助于及早创建跨越系统众多部分的
“广泛但浅显”
的实现
——这是细化阶段的常见目标。
本书心得:
终于把这部巨著看完了,不过第
1
章到第
9
章都没做笔记,只是在一篇总结中对用例、领域模型等内容做了总结和讨论,但是没有从大局观上来记述
UP
各个阶段需要怎样做,持续的时间和创建的制品,等读第二遍时,从宏观角度在记述一遍,总结一下。另外很多模式、原则都需要在实践中去进一步理解,因此这不是看一遍就
over
的书。另外这也是一本入门书,下一步我会阅读一些本书提到的重要文献,对各个问题进行深入的探究,并配合实践(以
J2EE
和
Delphi
为工具和案例研究)。