零雨其蒙's Blog

做优秀的程序员
随笔 - 59, 文章 - 13, 评论 - 58, 引用 - 0
数据加载中……

敏捷面向对象分析与设计思想

 

敏捷面向对象分析与设计思想

  

万星

 

摘要

本文主要讨论以面向对象的方法来构建企业信息系统,对比了面向对象建模与面向过程和数据建模的区别,和使用面向对象建模的动机。本文对于面向对象分析与设计中蕴含的思想进行了一些扩展,讨论了面向对象技术与RESTWorkflow,面向服务之间的关系,以及面向对象技术与这些应用的关系。

 

1       引言

本文建立在企业级信息系统开发的前提下,不讨论其他类型的计算机系统,这一点要首先声明,因为不同诉求不同领域的计算机工作者总是会对同一问题得到不同的见解,并且争论一些毫不相干的话题。

企业信息系统具有以下几个特点:

1.         需要处理大量相互关联的数据。这一特点使得信息系统总是与数据库关联在一起,并促成了早期的以数据建模方式开始构建信息系统。数据库专家致力于如何设计数据模型,以使访问高效,不易出错等。这时,管理数据是主要需要解决的问题。这一情况,在现在仍然需要重视。另一方面,这些数据之间具有广泛的联系,复杂的数据模型就像一张大网一样,各个数据彼此参照,因此当添加、改变或移除一个节点时都相当复杂。

2.         没有“逻辑”的业务逻辑。业务常常缺乏一个具有严格数学意义的逻辑,往往会因为与某个客户的谈判,临时将催款日期推后几天,而改变了过去的处理逻辑。在企业应用中,正是成千上万的“一次性业务逻辑”导致了开发的难度[Fowler02]。即使是不经常改变的业务,也可能会存在大量的针对不同情况的不同的处理逻辑。企业应用如何能正确将变元与恒元分离开,使得变元独立于恒元进行变化,是构建稳定的信息系统的主要思想。另外,业务流程在某些系统中也是经常会改变的,但是其改变的只是操作序列,而推动业务流转的元模型并未改变。因此,抽象出了工作流引擎。

3.         复杂的用户界面。企业信息系统往往会带有一个复杂的用户界面,包含大量的控件,对用户激发的系统事件进行响应调用系统操作。将业务逻辑和用户界面逻辑分开是件非常困难的事情,比如,对于某些数据的筛选,虽然看似是用户界面的一个简单的选择,实际上却蕴含着复杂的业务逻辑。另外,需要支持多种客户端,也是用户界面处理的难题。

4.         大量的并发访问。企业信息系统往往是一个多用户并发访问的系统,因此必须做好并发处理,否则就会出现线程争抢,数据库死锁,事务也会因为多种不一致性而失败。

5.         存在大量的异构的遗留系统。有的时候不得不面对拯救信息孤岛,整合遗留系统的问题,尤其是在当今已经出现了大量的企业信息系统的时候。因此SOAREST等技术的出现,成为彻底打破平台束缚,进行异构系统整合的首选。

    虽然同属于软件开发,但是信息系统开发与其它软件开发还是有很大区别的,因此,对于该领域的不同方法,面对不同的情况,发挥了不同的作用。

面向过程的面对业务处理序列建模,而非业务中固定的实体及其固有行为。而这些业务处理序列会时常发生变化,也会因为一个不可思议的条件而产生细微的差异,导致大量重复的处理代码或者需要不断的需要修改已经稳定发布的代码甚至是模块。比如订单是ERP系统中常见的实体,而处理订单的业务逻辑则各不相同,面向过程没有对稳定的东西建模而对不稳定的东西建模,因此在处理易变的业务逻辑是有问题。数据建模,是对于业务系统中的业务实体进行建模,然而这些数据表缺乏表达业务含义的能力,但是在应用早期,对于业务处理的需求也比较少,这样做是什么问题,就是在今天,如果业务逻辑简单或者很固定,则采用这种方法也是没有问题的。然而,随着业务的复杂性、多样性和易变性,特别是对于同一业务会存在多种不同的处理情况,仅依靠数据建模就不足以完成要求了。

三层架构[TK78Gartner95]是一个清晰的分层架构,也是众多分层架构中最著名的一个。表现层,业务层和持久化层,形成一个单向依赖层模式,也分离了不同的系统处理关注点。虽然不同的理论提出了不同的分层架构,但是我们都可以通过对于三层架构的扩展和细分,去发现其他层次的意义。

新的技术带来了新的解决问题的方案,然而已有的经验如何服务于这些新的技术,去解决那些由来已久的问题,或者以传统的方式去解决新技术自身的问题,同样是值得思考的。

 

1.1    对世界建模的三大方法论

在对于世界建模的方法论中,主要有三种方法占领了高地,分别是结构化方法(面向过程[Yourdon79]),面向对象范型,和实体关系模型(面向数据范型)。结构化方法强调过程序列,面向对象强调对象交互,实体关系模型强调实体与其间的关系,其不关注业务处理,即只是静态建模。

1.2    信息系统与三大范型

在开发企业级信息系统过程中,我们都要对于业务中的概念进行建模,无外乎就是业务实体(领域模型),业务操作(业务处理),单据,被统计的数据集,业务实体之间有某种关系(一对多、多对一等关联关系,特化/泛化的继承关系,生命周期同步或异步的包含代表的组合/聚合关系,一方对另一方调用或其他行为的依赖关系),业务操作的序列这些概念,而实体或者对象或者结构体都包含字段/属性/成员变量。而三大方法论只不过是从不同角度观察和描述世界,因为软件不能也不必对于世界进行完整的建模,因此选择有利于项目软件活动的建模方法是必要的,每种方法都各有所长,在过去四十年间,使用各种方法都创建了大量成功的系统和导致了很多失败的项目。

简单而言,信息系统主要为了解决业务问题,业务通过一个个促使状态发生变化的操作得以开展,使用结构化方法主要是通过外部力量改变数据状态,而面向对象方法是通过对象自身的行为改变其内部状态(所谓封装的概念)。

在这里我将简单分析一下三大方法是如何对上述业务概念进行建模的。在结构化方法中,过程是一等公民,我们首先要知道做事情的一连串过程,经典的分析工具是业务流程图和数据流程图,此方法更加强调数据的结构,是单数还是集合。一个过程就是一个改变实体状态的操作序列。

在面向对象中,对象是一等公民,我们主要关注的是对象如何通过向彼此发送消息,支配行为,改变状态。很多面向对象的专著和论文都强调了面向对象建模首先是对行为建模,其次是对数据结构建模,比如责任驱动设计(RDD[WW89],契约式编程等。

而在实体关系模型中,首先要找到实体,然后确定实体和实体之间的关系。很多开发者都发现E-R图和领域模型图(通常用UML类图表示)很相似,甚至分不清数据模型和领域模型,这是很正常的,因为它们是对同一个世界进行建模。而这些实体/对象都会包括属性,所以某些模型看上去是相似的了,但是这种相似不等于完全相同,所以才会有诸如对象关系阻抗不匹配的困扰。实体关系建模反映了在完成某一业务过程时各个实体以及它们之间的关系的状态快照(SnapShot)或者选择状态快照(限定捕捉某些实体的某些字段以及某种特定组合)。这些快照有可能就是业务过程中产生的单证或者统计数据表中的统计单元。状态(State)这个词在面向对象技术的词汇表中代表的意思是对象属性在某一时刻的值集合。从面向对象的角度来看,开发者需要将这些在刷新(flush)内存之后依然需要保存的状态持久化到数据库中(或者平面文件(flat file)中,诸如文本文件、XML文件之类的),所以在面向对象范型中,数据库只是持久化机制的一种,并非系统的核心,某些顺时状态只需要保存在内存中即可。而以数据库为中心开发的系统,会倾向于将任何状态都持久化到数据库中,比如购物车的不同实现方式,使用面向对象设计可能会倾向于使用Hashmap,而以数据库为中心则倾向于将其映射到数据库中。

2       面向对象分析与设计

面向对象分析与设计是非常流行的方法,除了流行的设计模式、设计原则和最佳实践之外,其本身的含义则缺乏鲜明的注释,本节以ERP系统为例,试图将其与其他两种方法论视图做一下对比。

以另外两种方式对于领域建模的思维来看ERP系统,看到的就是一个个单据的填写与流转(数据流程图),数据库中记录的变化,统计报表。那么业务呢?业务就只是填写单据和生成报表吗?系统所做的工作就只是帮助用户记录填写的数据,或者仅仅就是支持CRUD操作吗?

如果以面向对象视角来分析,看到的是什么呢?在一个对象系统中,对象扮演业务领域中的角色,彼此发送消息,完成属于自己职责的任务。

2.1    信息系统中一些常见概念

在这一节中,主要探讨在企业信息系统中常见的一些概念,在面向对象系统视角下的含义。

[单证]

单证处理是信息系统的主要功能,某些信息系统对于用户而言就是处理单证,然而就本质而言,某些单证是处理业务的附属品,它们本身没有行为,或者是为了作为执行某种操作的依据(在进行某项业务时所开理的手续,如出库单,合同;进行某项业务时对照信息,如理货单),或者是为了长久记录操作状态以备事后查证(收费凭证)或统计分析,简单而言,他们是为业务而存在的,并非业务本身。这些单据的特点是,其数据都来自于其他领域对象或者纯虚构(为了系统得以运转而虚构出来的对象,而非领域中的对象)[Larman04],因此它们不是真正的对象。而某些单据,则是领域中的对象,比如计划,其不是其他对象状态快照的简单集合,而是具有行为的对象,它代表的抽象意义是对于未来的安排与决策。

[UI]

而用户界面是什么?在面向对象分析中,它只不过是用户操纵对象社区的接口,和展示对象状态的接口(或者说是窗口)。另外一个叫做表现层的概念,则又将页面逻辑和应用控制逻辑进行分离。为了隐藏客户端对于对象社区的知晓,简化类型转换和异常处理,经常会使用VO(值对象)[Fowler2002]搜集领域对象中需要展示给用户的状态(如StrutsFormBean)。在分布式系统中,会使用其作为DTO(数据传输对象),如今XML是异构系统的一个选项。

使用DelphiVB等微软平台的工具进行开发时,常会使用数据感知控件构造界面,这意味着不具有一个代表领域概念的对象层,而且倾向于采用对象关系建模,并且程序常常是过程化的。这在业务简单稳定,大多数操作仅仅是简单的CRUD操作时这样做是合理且有效的,然而在大型复杂的信息系统构建过程中,这将不能应对系统的变化和扩展。

[数据库]

在以对象为中心的信息系统中,会决定将哪些对象进行持久化。在这里存在一个问题,持久化的一定是一个定格到某个状态的对象,还是对象中的数据到数据表的一个非面向对象映射。前者通过借助O/R映射工具可以实现一个优雅的对象持久化层次,而后者会使用一个不完整的对象模型。使用成熟的O/R映射工具可以避免很多对象模型到实体关系模型映射的困难。不需要使用实体关系模型再对领域重新建模,而可以采用一个有规律的转化(比如引用与外键的关系)。

2.2    信息系统构建中的面向对象分析

面向对象系统的稳定性保证在于其对于领域的准确抽象,比如饭店的核心业务就是点菜,上菜,结帐。随着饭店规模的扩张,各种技术的发展,金融规则的变更,这三个核心的业务是不会变的,变的是如何点菜(服务员记在脑袋里,记在点菜单上,记在手持PDA上,还是顾客自己在点菜单上勾画,在电子菜单上点击选择,这些都只是点菜方式的不同),需要产生哪些凭证(是否打印出来供顾客确认,是否需要上菜时进行核对,是否需要与后厨核对,是否需要与财务人员核对,不同的答案组合会得到不同的票据,其上的内容也会有所不同),如何上菜(顾客自取还是服务员送达),如何结帐(饭前结帐还是饭后结帐,现金支付,还是未来随着饭店不断发展壮大之后可以采用会员卡结帐,积分结帐,信用卡结帐等多种方式)。面向对象分析优劣体现为以下几个方面:

1、是否对于领域有个相对合理的抽象。比如前例,合理的抽象绝对不会是[填写点菜单]。这需要领域专家的参与,因为只有他们最清楚该领域的核心是什么。领域驱动设计,建立合理的领域对象被认为是解决企业应用核心复杂性之道。[Evans03Fowler02]

2、是否有效分离表现层,业务层和持久层表现层作为对外提供服务的接口,指明了外部如何使用该系统的契约,这样业务层可以作为Web Service暴露给外部,可以是REST风格的服务接口,抑或作为另一个系统的数据源。而数据源层作为业务层访问外部服务的接口,指明了该系统如何使用其它服务的契约,这些服务就可能是数据库服务,消息服务,甚至是另一个可以提供数据的系统。这也恰恰提醒我们,表现层接口和数据源层(我还是觉得叫数据访问层比较好)接口并不直接相关,因此在设计时不要将这两个接口进行简单的设计映射,表现层接口的契约与数据源层接口的契约简单对应会造成系统不稳定,数据访问层的变动将导致表现层接口的变动。另外,尤其要记住,它们是为不同目的设计的!

3、是否有效分离应用逻辑与领域逻辑。在大型企业信息系统中,会分离出单独的应用逻辑层,以确保领域逻辑的稳定和可复用性。领域逻辑和应用逻辑都属于业务逻辑。应用逻辑有时被称为工作流逻辑[Fowler02]。这一分离对于大型信息系统而言是非常重要的,这使得领域模型独立于不断变化的应用需求。应用需求的例子包括点菜服务(Order Dishes Service),其中会用到点菜(Order Dishes,只包含创建并将点菜单与支付方式,点菜单项与Dish对象进行关联或者解除关联等操作),点菜单(Dishes Order),支付方式(Payment Method),菜单(Menu,其中包含Dish对象)等领域对象和其中的领域逻辑(比如点菜单中的添加点菜项目逻辑中,可能会包含合并相同菜项,数量加1,并更新消费总额的领域逻辑),同时还可能包括向其他系统发送消息等逻辑,如库存系统,财务系统。这些领域逻辑会在不同的应用需求之下使用,比如老板管理菜单时,同样会用到菜单对象。而是否通知更新其他系统,或者其它应用需求,与领域本质并无直接关系,并且会不断变更(比如进行精细化库存管理之前并不需要将点菜与库存直接关联),因此将其分离是很重要的。需要注意的是,应用逻辑与应用控制层并非一物,应用控制层位于表现层,作用在于接受请求,调用领域逻辑或者应用逻辑,其核心在于实现控制逻辑,在小型信息系统中不存在单独的应用逻辑层,其中对于领域逻辑的调配和非领域逻辑的实现也在这一层中完成。在分层结构中,应用逻辑会被实现为服务层中的对象,而在服务层中,有时则只使用业务门面(Facade)模式,门面只是对于领域对象一个薄层,业务逻辑完全存在于领域对象之中[Fowler02]。然而无论采用何种分层架构或在分层架构中的哪一层次实现应用逻辑和领域逻辑,对于应用逻辑与领域逻辑的分离都是重要的,这比在系统中仅仅分离出业务逻辑更进了一步。

总之,构建一个真正代表领域核心的、合理抽象、职责明晰的领域模型是保证系统稳定发展的前提。

2.3    业务建模方法与用例使用

业务建模(在面向对象分析中指建立领域模型)和用例使用分别是系统分析工具和需求搜集分析工具。建立领域模型和编写用例文本是一个交替进行的过程。在编写用例前,开发人员和领域专家会一起建立一些核心的抽象模型,然后编写用例(主要用以发现新的需求[Cockburn01])从而获得启发,扩展领域模型。

编写用例要注意以下几点:

1.         编写用例是指编写用例文本,而非绘制用例图。用例图的作用在于说明用例之间的关系,参与者与用例之间关系,以及参与者之间的关系。作为用例文本的高层展示具有一定作用,然而专注于用例关系会导致没法专注于对于复杂灵活的业务需求的发现与捕捉。

2.         要区分业务用例和系统用例。业务用例主要描述业务流程,可以使用活动图,业务流程图等技术加以图形化表示;系统用例主要描述系统功能,与传统的功能列表相比,其强化了参与者与系统的交互,并且对于分解多条件事件和异常处理有巨大好处。好的系统用例有助于在业务建模时发现核心抽象,并且对于以用户故事的形式组织迭代开发具有指导意义。系统用例可以使用交互图进行图形化表示。经典的结构化方法引入业务流程图以描述业务流程,并使用DFD(数据流程图)进行细化,实际上只是完成了业务用例的编写,而对于系统用例则采用功能列表等技术,用例方法并非是面向对象的,但是它最广泛的被用于面向对象范型之中。

3.         编写用例应采用迭代方式。不少组织在瀑布模型中使用用例方法,一次编写大量用例,并且每个用例都具有大量扩展流程,编写者和审核者都可能因为大段的文字描述而产生疲劳感和抵触情绪。因此,在每个迭代中只细化几个场景(可能是主成功场景或扩展场景)即可,同时Larman也指出一次迭代的最小工作单元就是一个场景[Larman04]。对于用例的重构也是不可或缺的,随着对于需求的深入了解,用例也需要不断更新。比如需要将过于琐碎的步骤提取成新的用例。

4.         要保持用例中执行步骤的抽象程度一致。用例场景中的每个步骤都有可能作为一个引用用例,用例可以被看作一个不断展开下去的故事[Cockburn01]。因此某些明显为了某一目标而出现的步骤被归结为一个引用用例可以保持抽象一致。

5.         注意区分系统事件和系统操作。系统事件指的是外部输入事件,参与者发起系统事件,如按下鼠标左键;系统操作是指处理系统事件的操作[Larman04],比如当参与者按下鼠标左键时,系统提示用户按下鼠标左键[Ambler02]

6.         编写用例参照一个好的格式和遵循一些优秀的实践和模式大有裨益。Cockburn的《编写有效用例》和另两位作者的《有效用例模式》值得推荐,尤其是前者几乎是必读书籍,其毫不含糊的直击每个用例初学者都会遇到的问题,如怎样处理条件分支,怎样编写含有CRUD操作的用例等。

7.         要清楚用例并不是需求的全部。建立词汇表(可作为数据词典使用),UI需求说明书,业务规则等以记录其他需求有助于编写高效的用例[Larman04Cockburn01],另外,也有助于发现真正的业务逻辑,而不被UI操作,数据存储策略等需求干扰。

 

建立领域对象将从用例文本(主要是系统用例)中获得灵感。主要包括:

1.         发现对象,确定Role Stereotypes

2.         发现并分配职责

有大量优秀的著作讨论如何发现对象和分配职责,其中由RDD(责任驱动设计)的创始人Wirfs-Brock编写的《对象设计:角色,责任和协作》是对于该问题讨论的专著,Larman的《UML和模式应用》中提出的GRASP原则(通用职责分配软件模式),Ambler的《Object Primer》和McConnel的《代码大全》中对于职责分配的讨论。

此外对于构建企业信息系统的开发人员而言,Martin Fowler的《分析模式:可复用对象模型》和Eric Evans《领域驱动设计:企业应用核心复杂性解决之道》提供了业务领域现成的模式和分析问题的方法。

2.4    信息系统构建中的面向对象设计

区分需求分析、系统分析和系统设计是非常重要的,Ed YourdonPeter Coad的巨著《面向对象分析与设计》明确的划分了这一点。然而Evans却强调要建立一个统一的,贯穿需求分析、系统分析与设计过程的领域模型,以统一业务人员与开发人员的思想。而在分析模型(即领域模型)与设计模型之间建立低表示差异具有重要的意义[Larman04]。本文对于分析模型,领域模型,领域对象,设计模型等概念的区分参照[Larman04]

与结构化方法相比,面向对象设计拥有大量的模式,虽然某些模式同样适用于非面向对象设计,但是面向对象固有的能力可以更好的实践这些模式。

基于建立好的抽象的领域模型进行面向对象设计,使用接口,委托,继承,多态等技术手段分离变化,是面向对象范型进一步实现高稳定性、易扩展性、高可重用性信息系统的保证。

3       工作流与面向对象技术

3.1    业务流程,工作流与业务逻辑,业务规则

业务流程,工作流,业务逻辑,业务规则等概念既相似又不同。在不同规模的信息系统中,它们所起的作用不尽相同,而在不同的著作中,对于它们的定义也不同。

在大多数语境下,业务流程和工作流是同一概念,某些工作流规范亦称为业务流程管理规范,而采用了UML活动图来定义工作流。

业务逻辑表示完成业务的逻辑,那些涉众比较广,跨越了很多会话,生命周期很长的业务逻辑应该被归于业务流程,而某些业务规则,亦是业务逻辑的一部分。当然更多的是介于两者之间的业务逻辑(如前文讨论领域逻辑)。

业务规则表示了处理某项业务的规则,可能是企业内部的规章制度,法律法规,税务规则等。在企业信息系统中,经常会遇到针对不同类型的产品或客户,需要基于不同的业务规则进行处理的情况。

对于项目所建立的信息系统而言,哪部分是最复杂的,最多变的,最能产生收益和造成风险的,是必须要分析清楚的。

工作流引擎在于将业务流程或工作流中的节点之间的流转或调用提取出来,构建为通用的框架,并将流程定义分离出来,以提高业务流程管理的能力。

然而,并不是所有的信息系统的核心复杂性都来自于业务流程的多变和复杂,而恰恰是复杂多变的领域逻辑,而改变工作流程的情况并不常见,流程也并不复杂。

因此,工作流引擎或平台依然无法解决领域逻辑的复杂性和易变性,而解决这些问题的强大工具依然是面向对象技术。

3.2    工作流与面向对象技术

工作流引擎可以主要负责业务流程的调度,其中的每个节点,调用一个业务门面中的方法。一个业务流程可以代表一个应用,比如点餐,可能会涉及到很多参与者,位于不同会话,这往往可能对应于一个用例。将应用逻辑封装在较高的层中可以使得变更该层的实现更为容易——你可能用一个工作流引擎就做到这一点[Fowler02]

把工作流引擎只是看作一个实现应用逻辑的框架是有好处的,而某些工作流引擎也是使用面向对象技术实现的。这与前文讨论分离应用逻辑和领域逻辑是密切相关的。就前文的例子而言,可以将点菜,更新库存,通知财务系统看作是三个节点,其中前者是人工节点,后两者是自动节点,这样,添加和修改应用逻辑,只需要修改流程定义即可,而不需要修改应用逻辑对象(此时整个工作流引擎代替了应用逻辑对象)。因此,事务管理和安全控制也应该在这一层次实现。

    这一层次的事务管理,往往属于业务事务[Fowler02],如果像上文一样设计,处理起来会更加困难。

节点的实现方式有很多,某些规范定义了Web Service,而采取本地方法调用从效率和编写测试维护难度等方面都是更好的选择。这符合Fowler的分布式对象第一定律:不要分布你的对象[Fowler02]

工作流平台和引擎在公文报批,OA中能发挥最大的作用,而在ERP系统中,只是解决了次要问题,更复杂的问题依然需要借助面向对象技术,把它作为系统的一个应用层在大型复杂信息系统应用中会是一个好的选择。

然而,如果业务流程不会经常变更,使用工作流引擎带来的运行效率,事务处理等问题将使得投入得不偿失。

判断的依据可以是:绘制活动图来表示业务流程,如果业务流程中包含很多节点,而每个节点所完成的功能只是简单的CRUD操作,也不需要处理复杂多样的业务规则,则将重点放在工作流引擎上具有重大价值;如果业务流程只包含几个节点,而每个节点所要完成的处理非常复杂,包括复杂的校验,计算,关联关系,需要处理复杂多样的业务规则,则应该考虑将重点放在对于业务规则的处理上面。

在我们的项目中,我们将属于不同参与者完成的步骤作为工作流节点,而由同一个参与者完成的任务则放到应用逻辑对象中,然后以业务门面进行封装,以减少离线事务处理[Fowler02]。由于同时采用远程客户端和Web客户端,因此我们还会将工作流调用对象和业务门面暴露为远程门面,通过使用Spring框架实现远程调用的一致性。



[1] () Martin Fowler 2004Inversion of Control Containers and the Dependency Injection patternhttp://www.martinfowler.com/articles/injection.html

[2] ()Christian Gross2006Ajax and REST Recipes: A Problem-Solution ApproachApress

[3] Leonard Richardson, Sam Ruby2007RESTful Web ServicesO'Reilly Media, Inc

 

posted on 2008-06-28 21:14 零雨其蒙 阅读(1108) 评论(0)  编辑  收藏 所属分类: 面向对象理论与实践


只有注册用户登录后才能发表评论。


网站导航: