一个建模过程
自然的交流过程
使用领域模型交流的过程
我们通过两个对话的对比,实际上知道了有三个关联的实体,货物、路径指定器、航线。
第一个对话中,涉及到了很多说法也就是图中标红的部分,这些说法各不相同,但是实际上也就代表了三四个概念。但是交流过程是顺畅的,因为根据上下文可以理解。但是,由于说法各不相同,导致任何一个说法都没法单独提取出来作为模型元素,脱离了自然语境之后,这些词对领域知识的表达能力几乎为0。
而第二个对话中,核心强调了两个概念,即Intinerary 和 Route Specification。首先从字面上说,这两个是很容易和领域知识关联起来的,这就形成了模型的表达能力很强的基础元素。其次,整个对话贯穿了这两个概念,让理解起来非常顺畅,不需要结合上下文联想也不会产生歧义。此外,可以看到,这两个概念最开始是开发人员定义出来的,他一直在说,最终,用户也开始说这个词。这时候,双方在这个模型的表达方式上形成了统一。
合适的模型建立过程
大声建模
我们的大脑非常适合语言。所以通常开始进行领域知识交接的时候,用的就是自然对话。遗憾的是,自然对话并不会使用领域模型。
使用单词和短语是集为重要的。这些将直接作用于模型中。词和短语的语义会反应模型的语义。
明确表达模型建立过程中的各种结构,这样不完善的地方容易被听出来。
既要利用系统性分析设计方面的能力,又要利用对代码和程序体系的掌握,也需要利用语言上的走查实验。
不同语言系统的人,持续的表达自动创造共同的表达,这是人的天赋。
持续表达的过程,持续的抽象短语和词,表达会趋于一致和流畅,并能够快速互相指出差别。然后自然而然的共享了同样的表达,这是图和文档无法做到的。
讨论是结合模型,使用模型元素和交互术语来描述场景,将各种概念结合起来,找到更简单的表达来表示你的想法和理解,然后将这些元素应用到图和代码中。这个过程慢慢完成了建模。
解释性模型
领域专家会使用各种方式来解释领域知识,这些方式不是领域模型,而是按照自己的方式组织的模型,却可以用来传递领域知识。这些方式可能是图或者文字。这对建模是有意义的,是一种解释性的模型。
解释性模型提供了一定自由度,它可以不那么严格精准也不纠结于各种建模模式,也没有和类图和代码的一一对应关系。但这这样的模型让理解过程更加轻松容易。例如如下这样的:
它不比是对象模型,也最好不是UML图,这样可以明确知道解释性模型就是用来解释的,而跟实现无关。
一个团队,一种语言
不同的语言
领域专家能熟练使用自己领域的术语。开发人员可以用描述性的功能性术语(例如UML)。在没有共同语言的项目上,开发人员和领域专家、分析人员之间需要很多的“翻译”,即需要大量的解释。甚至同一个人不同时间的表达和描述也不一致。此外还有大量的误解。这导致对领域的深刻描述往往稍纵即逝,无法记录到代码和文档中。也就无法产生持续深刻的知识理解,无法进行长久的迭代,从而让软件开发低效而没有扩展性。最多就形成了功能的集合体,而丧失了底层理解。
任何一方的预言都不能单独成为公共语言。这是由于各方语言的特点决定的 。因此需要团队整体的努力来一起构建通用语言。同时将沟通语言、模型、实现紧密联系在一起。
正如合适的建模过程所言,不同语言在磨合过程中会形成共同部分。
文档和图
UML图在传达对象关系和交互上很擅长,但是却没有对象概念的定义,同时也无法表达对象的职责。同时,很多复杂的规则用UML表达是很费力的,即便是表达出来了,那么图变得极其复杂,其不再是一个模型,而是实现方式的另一种视图。
图是一种沟通和解释手段,他们可以促进头脑风暴。简洁的小图能够很好的完成这个目标,反而综合性大图(UML的对象带有完整的属性和方法)让人淹没在细节中,让图失去明确的目的性。图不应该是面面俱到的设计规范,而是思想纲要。
所以我们应该避免使用这样包罗万象的模型图,反而,应该使用简化图甚至草图。让一幅图只包含其中的3~5个核心对象,这样方便集中注意力。然后,团队在这个简化模型基础上进行沟通修改,这才是讨论中真正重要的部分。
设计的重要细节应该在代码中提现。
互为补充的图和文档能够将注意力集中在核心要点上。自然语言描述能填补细节。需要注意,模型不是图,图的目标是帮助表达和解释模型。
代码可以作为设计细节的记录,书写良好的Java代码和UML一样有很强表达能力。
书面文档一旦形成,由于惰性,所以很容易跟不上代码和项目语言的演变。所以,文档只应该:
- 作为代码和口头交流的补充
- 必须要的代码尽量少,并保持鲜活和更新
实际上,极限变成主张完全不使用多余文档,而让代码解释自己。代码始终是需求的最终体现,而文档则不能。
注释不影响程序行为,文档和图不影响程序行为,口头交流和白板不能保存,因此最终能够表达程序行为的仅仅只有代码。
事实上,如果专门维护文档,则文档维护人员需要同步迭代,多了一种个性化的表达和理解,多了份澄清过程,也就多了多了一份理解难度和多一份误解风险。
而使用代码作为文档和交流媒介,则强迫开发人员使用优良的编程范式。但是代码作为文档也有局限性,即太多的细节,让行为不再显而易见。这跟大而全的UML图有类似的问题。此时,需要文档来说明大尺度的结构和核心元素概念,并且文档可以澄清设计意图,代码则不行。
所以文档可以作为代码和交流的补充。
仔细选择尽可能精简的模型画图,然后将文字放在周围,用文字描述定义和职责,图形表达关系和交互。
简图和草图让人意识到这些都是临时的,从而毫无压力的进行修改迭代。
文档必须深入到项目讨论的过程和实现的过程中去,同时文档中必须使用通用语言,这样更加简洁和明确,因此当通用语言和模型更新是文档会被迫更新。通过尽可能减少文档,并定位与补充代码和口头交流,可以避免文档和项目脱节。
代码
由于文档和图是作为代码补充而出现的,之前也说过了代码本身需要和领域模型强一致,并使用通用语言。因此一旦模型发生变化,是不能期待其他方式来提现这种变化的,只能重构代码,迫使元素和方法的命名跟得上新的模型。
为了保证变量和代码组织方式表达的严格的准确的含义,可以使用类似于申明式设计(第十章)。这类方法中程序元素的名称就决定了一个元素在程序中的实际行为。这种方法需要程序员自律并利用替丁的设计方式(第三部分)。
模式: Ubiquitous Language 通用语言
领域模型可以成为软件项目通用语言的核心。该模型从项目参与者公认的、反应领域深层含义的概念、术语和关系。
这种模型不局限于UML图,而是充分利用各种表达方式,包括了非正式图表、交谈、代码本身、对应的测试,这些手段共同促进了书面文档的效用也减少了文档的数量。
通过尝试不同的表达方式来消除复杂部分,重构代码、重命名类方法模块,以便保持一致。
通用语言的词汇包括了类和主要操作的名称。其中的术语,有些用来表达讨论种比较明确的规则和实体,有些用来描述模型的高级组织原则(例如14章和16章种提到的ContextMap和大型结构),也就是说术语同时来源与领域抽象以及软件工程,这也体现了领域专家和开发人员的深度合作。
通常基本的概念是从领域术语中推导出来的,但是经过了软件工程的清理使之更明确更严密。
开发人员使用通用语言来描述系统中的实体、功能等,而领域专家使用该语言讨论需求、开发计划和特性。
开发人员和领域专家可以一步步使用模型对象来走查场景,从而进行非正式测试。每次的讨论,开发人员和领域专家都能够使用模型,加深彼此的理解,并精华其中的概念进而迭代模型。
领域专家甚至可以用模型语言来编写用例,甚至用来说明验收测试。
从最初,模型可能不完善甚至有问题,不能表达领域专业术语那样丰富的语义,也缺乏代码中的细节和灵活性。但是持续使用基于模型的通用语言让团队有机会更新表达方式,迭代模型,从而也促使开发人员重构类和方法。
为了解释和给出更完整的上下文,领域专家可能使用超过通用语言意外的的表达方式,这些表达能够检查和更新模型,最终通用会整合其中的核心。
通过大量使用基于模型的统一语言,达到不流畅不罢休的地步,最终能够得到一个完整、易于理解的模型。它由简单元素组合而成并能够表达复杂的语义。这个过程种,领域专家应该关注无法充分表达领域理解的术语和结构,而开发人员应该关注那些妨碍实现以及逻辑上的不清晰。
由于通用语言是基于模型的,两者是一致的,即修改表达的过程就是修改模型的过程。
在敏捷过程中,需求是随着项目前景而演变的,几乎不存在现成知识来充分说明一个程序,用通用语言来沟通交流,应该是这个演变的一部分。
本章说的通用语言,聚焦于一个模型的场景,14章会讨论多个模型共存时如何防止分裂的问题,具体来说就是定义不同系统和模型之间的界限上下文。而16章则讨论如何将模型组织成大型结构。