面向对象只是用来描述世界的工具之一
面向对象如今在软件行业是如此著名的一个术语,以至于人们以为面向对象是现代科学发展到一定程度才出现的研究成果。在很多人看来,面向过程和面向对象都是一种软件技术。例如,把面向过程归纳为结构化程序设计,DFD图,ER模型,UC矩阵等,而面向对象则被归纳为继承,封装,多态,复用等具体的技术。事实上,上述所有技术都只是人们在采用不同的方法来认识和描述这个世界时所采用的工具,它们都只是表征而不是本征。
UML的创始人之一Grady Booch在2004年的IBM Developer Works Live大会的访谈中讲过一段话:我对面向对象编程的目标从来都不是复用。相反,对我来说,对象提供了一种处理复杂性问题的方式。有了对象,我们能够通过提升抽象级别来构建更大的,更复杂的系统。我认为,这才是面向对象编程运动的真正胜利。面向过程和面向对象是一个古已有之的认识论问题。之所以面向对象方法会兴起,是因为这种认识论能够帮助我们构造更为复杂的系统来解释越来越复杂的现实世界。认识到这一点,我们应该知道比掌握具体的技术更为重要的是,掌握认识论所采用的方法和分析过程。只有掌握了方法,才能自如的使用工具。
面向对象方法与面向过程方法根本的不同,就是不再把世界看作是一个紧密关联的系统,而是看成一些相互独立的小零件。这些零件依据某种规则组织起来,完成一个特定的功能。面向对象和面向过程的这个差别导致了整个分析设计方法的革命。分析设计从过程分析变成了对象获取,从数据结构变成了对象结构。
面向对象分析和设计的基本步骤
分析强调的是对问题和需求的调查研究,而不是解决方案。设计强调的是满足需求的概念上的解决方案,而不是其实现。有益的分析和设计可以概括为:做正确的事(分析),正确的做事(设计)。在面向对象分析过程中,强调的是在问题领域发现和描述对象(或概念)。在面向对象设计过程中,强调的是定义软件对象以及它们如何协作以实现需求。最后,在实现或面向对象程序设计过程中,会实现设计出来的对象。
一个完整的面向对象的分析和设计,包括以下几个环节:
(1)定义用例
需求分析可能包括人们如何使用应用的情节或场景,这些情节或场景可以被编写成用例。
(2)定义领域模型
面向对象分析关注从对象的角度创建领域描述,需要鉴别重要的概念,属性和关联,面向对象分析的结果可以表示为领域模型,在领域模型中展示重要的领域概念和对象。需要注意的是,领域模型并不是对软件对象的描述,而是真实世界领域中概念的描述。
(3)分配对象职责并绘制交互图
面向对象设计关注软件对象的职责和协作,顺序图是描述协作的常见表示法,它展示出软件对象之间的消息流,和由消息引起的方法调用。
(4)定义设计类图
除了在交互图中显示对象协作的动态视图外,还可以用设计类图来有效的表示类定义的静态视图,这样可以描述类的属性和方法。领域模型表示的是真实世界的类,设计类图表示的是软件类。
面向对象设计和语言能够缩小软件构件和我们所设想的领域模型之间的差距,即实现低表示差异(lower representational gap)。
用统一的可视化的语言来描述设计思想
从20世纪70年代末期面向对象运动兴起以来,到现在为止,面向对象已经成为了软件开发的最重要的方法。面向对象的兴起是从编程领域开始的,第一种面向对象语言Smalltalk的诞生宣告了面向对象开始进入软件领域。最初,人们只是为了改进开发效率,编写更容易管理,能够重用的代码,于是,在编程语言中加入了封装,继承,多态等概念,以求得代码的优化。但分析和升级仍然是以结构化的面向过程方法为主。
在实践中,人们很快就发现了问题,编程需要的对象,不但不能够从设计中自然而然的推导出来,而且强调连续性和过程化的结构化设计,与事件驱动型的离散对象结构之间有着难以调和的矛盾。由于设计无法自然推导出对象结构,使得对象结构到底代表了什么样的含义变得模糊不清。同时,设计如何指导编程,也成了困扰在人们心中的一大疑问。
为了解决这些困难,一批面向对象的设计方法(OOD方法)开始出现,例如Booch86,GOOD(通用面向对象开发),HOOD(层次化面向对象设计),OOSE(面向对象结构设计)等。这些方法可以说是如今面向对象方法的奠基者和开拓者,它们的应用为面向对象理论的发展提供了非常重要的实践和经验。
然而,虽然解决了从设计到开发困难,随着应用程序的进一步复杂,需求分析成为比设计更为重要的问题。这是因为人们虽然可以写出漂亮的代码,却常常被客户指责不符合需要而推翻重来。
于是OOA(面向对象分析)方法开始走上了舞台,其中最为重要的方法便是UML的前身:由Booch创造的Booch方法,由Jacobson创造的OOSE,Martin/Odell方法和Rumbaugh创造的OMT,Shlaer/Mellor方法。
这些方法虽然各不相同,但它们的共同理念却是非常相似的,于是三位面向对象大师决定将他们各自的方法统一起来,在1995年10月推出了第一个版本,称为“统一方法”(Unified Method)。随后又以“统一建模语言”(Unified Modeling Language)的正式名称提交到OMG(对象管理组织),在1997年1月正式成为一种标准建模语言。
UML是描述,构造和文档化系统制品的可视化语言。可视化是一个关键点,图可以帮助我们更为便利的观察全景,发现软件元素或分析之间的联系,同时允许我们忽略或隐藏旁枝末节,这是UML或其他图形化语言的本质价值。
然而,即使是同样的语言,同样的文字,同样的语法,有的人能够写出优美的小说和瑰丽的诗句,有的人却连一封书信都写不通顺。这种差别除了对语言掌握的功力之外,更重要的是写作人自己脑子里的思想和理念。因此,比学会用UML建模本身更重要的是要理解UML语言背后所隐含的最佳实践。
软件所处的业务领域决定了它的复杂度
软件的核心是其为用户解决领域相关问题的能力,所有其他特性,不管有多么重要,都需要服务于这个基本目的。当领域很复杂时,这是一项艰巨的任务,要求高水平的技术人员共同努力。开发人员必须钻研领域以获取业务知识,他们必须练习建模技巧,并精通领域设计。
然而,在大多数软件项目中,这些问题并未引起足够的重视。大部分有才能的开发人员对学习与他们的工作领域有关的知识不感兴趣,更不会下力气去扩展自己的领域建模技巧。技术人员喜欢那些能够练习技巧的可量化的问题。领域工作很繁杂,而且要求掌握很多复杂的新知识,而这些新知识看似对提高编程水平并无裨益。相反,技术人员更愿意从事精细的框架工作,试图用技术来解决领域问题。他们把学习领域知识和领域建模的工作留给别人去做。
有很多因素会使软件开发复杂化,但最根本的原因是问题领域本身错综复杂。如果要为一个业务复杂的企业提高自动化程度,那么我们开发的软件将无法回避这种复杂性,所能做的只有控制这种复杂性。软件核心的复杂性需要我们直接去面对和解决,如果不这么样,必将导致工作重点的偏离。在一个团队中,反映了对项目深层次理解的模型开发有时也会在混乱中迷失方向,此时,理解领域核心的领导者就能够将软件项目带回到正确的轨道上来。
在软件中,专门用于解决领域问题的那部分通常只占整个软件系统的很小一部分,这与其重要性远远不成比例,要想实现最佳的设计构思,就得去研究模型中的元素并且将它们视为一个系统。我们需要将领域对象与系统中的其他功能分离,这样就能够避免将领域概念和其他只与软件技术相关的概念相混淆,也不会把领域与整个软件系统混为一谈。
如果与领域有关的代码分散在大量的其他代码之中,那么查看和分析领域代码就会变得相当困难。对用户界面的简单修改实际上很可能会改变业务逻辑,而要想调整业务规则也很可能需要对用户界面代码,数据库操作代码或者其他的程序元素进行仔细的筛查。要想创建出能够处理复杂任务的程序,需要把不同的关注点分开考虑,使设计中的每个部分都得到单独的关注。在分离的同时,也需要维持系统内部复杂的交互关系。
分割软件系统有各种各样的方法,但是根据软件行业的经验和惯例,普遍采用分层架构。整个软件系统由多个逻辑层次构成,层中的任何元素都仅依赖于本层的其他元素或其下层的元素,向上的通信必须通过间接的传递机制。分层的价值在于每一层都只代表程序中的某一特定方面,这种限制使每个方面的设计都更具内聚性,更容易解释。当然,选择恰当的分层方式是至关重要的。
尽管使用分层架构的项目种类繁多,但是大多数成功的架构使用的都是包括上面四个概念层的某个版本:
(1)用户界面层
负责向用户显示信息和解释用户指令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。
(2)应用层
定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。
(3)领域层(或模型层)
负责表达业务概念,业务状态信息以及业务规则。领域层是业务软件的核心。
(4)基础设施层
为上面各层提供通用的技术能力。为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。
将所有与领域模型相关的代码放在一个层中,并把它与用户界面层,应用层以及基础设施层的代码分开。领域对象应该把重点放在如何表达领域模型上,而不需要考虑自己的显示和存储问题,也无需管理应用任务等内容。这使得模型的含义足够丰富,结构足够清晰,可以捕捉到基本的业务知识,并有效的使用这些知识。
从现实世界的领域模型到领域层中的软件对象
领域模型是对领域内的概念类或现实世界中对象的可视化表示。
确定一组概念类是面向对象分析的核心。通过识别问题领域中的概念,应用UML表示法,领域模型被描述为一组没有定义操作的类图,它提供了概念透视图。它可以用来展示:领域对象(概念类),概念类之间的关联,以及概念类的属性。
领域模型是现实世界中对象的概念透视图,阐述领域中的概念,而非软件透视图。相关的用例概念和领域专家的观点将作为创建领域模型的输入,而领域模型又会影响操作契约,词汇表和设计模型,尤其是设计模型中的领域层的软件对象。
领域层的软件类的名称是源于领域模型的,以使对象具有领域相关的信息和职责,这样可以减少我们的思维与软件模型之间的表示差异。
创建领域模型分为以下几个步骤:
(1)寻找概念类
(2)将概念类绘制成UML类图中的类
(3)添加关联和属性
其中,找到概念类是关键问题,我们可以通过识别名词短语来寻找概念类,这种技术称为语言分析。即,在对领域的文本性描述中识别名词和名词短语,将其作为候选的概念类或属性。虽然自然语言中的词语通常带有二义性,但是语言分析仍然可以提供一种灵感来源。那么从哪里去找到这些词语呢?其中某些术语来源于用例。另外一些术语则源于其他文档,或者领域专家的想法。无论如何,用例都是挖掘名词短语的重要来源之一。
另外一种方法是重用和修改现有的模型,这是首要的,最佳且简单的方法。在许多常见领域中都存在已发布的,绘制精细的领域模型和数据模型,这些领域包括库存,金融,卫生等等。最后一种方法是通过制作和查找概念类的候选列表,并且建议在分析时建立一些优先级。
开发人员应该了解领域建模
人们总是把软件开发比喻成制造业,通过这个比喻可以推断出一个结论,经验丰富的工程师做设计工作,而技能水平较低的劳动力负责组装产品。这种做法使许多项目陷入困境,原因很简单,在软件开发中设计是无处不在的。开发团队中每个成员都有自己的职责,但是将分析,建模,设计和编程工作完全分离会对项目产生不良影响。
如果编写代码的人员认为自己没必要对模型负责,或者不知道如何让模型为应用程序服务,那么这个模型就和程序没有任何关联。如果开发人员没有意识到改变代码就意味着改变模型,那么他们对程序的重构不但不会增强模型的作用,反而还会削弱它的效果。
因此,任何参与建模的技术人员,不管在项目中的主要职责是什么,都必须花时间了解代码。任何负责修改代码的人员则必须学会用代码来表达模型。每一个开发人员都必须不同程度的参与模型讨论并且与领域专家保持联系。
参考
大象 - Thinking in UML
UML和模式应用
领域驱动设计
Dr. Alan Kay on the Meaning of “Object-Oriented Programming”