Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

《UML和模式应用》——拉曼 #17

Open
thzt opened this issue Feb 3, 2017 · 0 comments
Open

《UML和模式应用》——拉曼 #17

thzt opened this issue Feb 3, 2017 · 0 comments

Comments

@thzt
Copy link
Owner

thzt commented Feb 3, 2017

编程很有乐趣,但开发高质量的软件却是困难的。
从好的观点,需求或“构想”开始,到最终变成一个实际运行的软件产品,
所需要的不仅仅是编码这一项工作。

分析和设计,定义如何解决问题,需要对哪些内容编程,
用易于交流,评审,实现和演化的多种方式来获取这个设计,这正是本书的核心所在。

如果没有软件工程过程作为背景,软件设计就显得有些枯燥和神秘。
Craig Larman加入并介绍了统一过程UP(Unified Process),
并介绍如何以一种相对简单和实际的方式来应用统一过程。

通过在一个迭代的,风险驱动的,以架构为中心的过程中介绍案例研究,
揭示了软件开发中实际的动态发展,并分析外部作用的影响。
设计活动与其他任务相关,它们不再是一个纯粹的系统化转换或使用创造性直觉的脑力活动。

设计模式表达了面向对象设计专家用于创建系统的“最佳实践”的习惯用法和方案。

UML并不是OOA/D,也不是方法,它只是图形表示法。
如果没有真正掌握如何创建优秀的面向对象设计,或者如何评估和改进现有设计,
那么学习UML或者UML CASE工具是毫无意义的。
对象思想才是重点和难点。

我们需要一种用于OOA/D和“软件蓝图”的语言,
这既是思考的工具,也是一种沟通的形式。

在OO开发中,至关重要的能力是熟练的为软件对象分配职责。

分析是强调对问题和需求的调查研究,而不是解决方案。
设计强调的是满足需求的概念上的解决方案(在软件方面和硬件方面),而不是其实现。
最终,设计可以实现,而实现(如代码)则表达了真实和完整的设计。

有益的分析和设计可以概括为:做正确的事(分析)和正确的做事(设计)。

关键步骤:
(1)定义用例
(2)定义领域模型
(3)定义交互图
(4)定义设计类图

在面向对象分析(OOA: object-oriented analysis)过程中,强调的是在问题领域内发现和描述对象(或概念)。
在面向对象设计(OOD: object-oriented design)过程中,强调的是定义软件对象以及它们如何协作以实现需求。
最后,在实现或面向对象程序设计过程中,会实现设计出来的对象。

需求分析可能包括人们如何使用应用的情节或场景,这些情节或场景可以被编写成用例(use case)。
用例是需求分析中的一个常用工具。

面向对象分析的结果可以表示为领域模型(domain model),在领域模型中展示重要的领域概念或对象。
领域模型并不是对软件对象的描述,它使真实世界领域的概念和想象可视化。
因此,它也被称为概念对象模型(conceptual object model)。

面向对象设计关注软件对象的定义——它们的职责和协作。
顺序图(sequence diagram)是描述协作的常见表示法。
它展示出软件对象之间的消息流,和由消息引起的方法调用。

除了在交互图中显示对象协作的动态视图外,还可以使用设计类图(design class diagram)来有效的表示类定义的静态视图。
这样可以描述类的属性和方法。

领域模型表示的是真实世界的类,设计类图表示的是软件类。
(注:先用领域模型为真实世界建立对象模型(数学模型),再用设计类图中的软件类表达它。
(领域模型和设计类图是不同的概念,但是都可以用UML可视化的表示。

统一建模语言(UML)是描述,构造和文档化系统制品的可视化语言。
在UP(统一过程)中,领域模型中的UML框被称为领域概念(或概念类),领域模型表示的是概念透视图。
设计模型中的UML框被称为设计类,设计模型一句建模者的需要,表示的是规格说明透视图或实现透视图。

概念类:现实世界中的概念或事物。
软件类:软件构件在规格说明或实现透视图中的类。
实现类:特定OO语言中的类。

“没有银弹”指的是,不存在某种特殊的工具或技术,可以在生产率,缺陷减少,可靠性和简易性等方面给软件带来极大的变化。
工具无法弥补设计上的疏漏。

UML仅仅是标准的图形化表示法,例如框,线等。
使用常用符号的可视化建模能够带来极大的帮助,但它不可能与设计和对象思想同等重要。
设计知识是极不寻常的且更为重要的技能,它并不是通过学习UML表示法或者CASE或MDA工具就可以掌握的。
如果不具备良好的OO设计和编程技能,那么即使使用UML,也只能画出拙劣的设计。

软件开发过程,描述了构造,部署以及维护软件的方式。
统一过程已经成为了一种流行的面向对象系统的迭代软件开发过程。

UP是十分灵活和开放的,并且鼓励引进其他迭代开发方法中的有用的实践,诸如极限编程(Extreme Programming, XP),Scrum等等。
例如,在UP项目中可以引入XP的测试驱动开发,重构,持续集成等实践。
同样,也可以引入Scrum的公共项目室(“作战室”)和Scrum日常会议等实践。

UP把普遍认可的最佳实践(如迭代生命周期和风险驱动开发)结合起来,成为联系紧密并具有良好文档的过程描述。

迭代开发(iterative development)是UP和大多数其他现代方法中的关键实践。
在这种生命周期方法中,开发被组织成一系列固定的短期(如三个星期)小项目,称为迭代。
每次迭代都产生经过测试,集成并可执行的局部系统。
每次迭代都具有各自的需求分析,设计,实现和测试活动。
迭代生命周期基于对经过多次迭代的系统进行持续扩展和精化,并以循环反馈和调整为核心驱动力,使之最终成为适当的系统。
随着时间和一次又一次迭代的递进,系统增量式发展完善,因此这一方法也被称为迭代和增量式开发。
因为反馈和调整使规格说明和设计不断进化,所以这种方法也称为迭代和进化式开发。

UP提倡风险驱动与客户驱动相结合的迭代计划。
这意味着早期的迭代目标要能够识别和降低最高风险,并且能构造客户最关心的可视化特性。
风险驱动迭代开发更为明确的包含了以架构为中心迭代开发的实践,
意味着早期迭代要致力于核心架构的构造,测试和稳定。
为什么?因为没有稳固的架构就会带来高风险。

敏捷开发方法通常应用时间定量的迭代和进化式开发,使用自适应计划,
提倡增量交付并包含其他提倡敏捷性(快速和灵活的响应变更)的价值和实践。
由于特定实践多种多样,因此不可能精确的定义敏捷方法。
然而,具备进化式精化的计划,需求和设计的短时间定量迭代是这些方法所共有的基本实践。

Scrum敏捷方法中的实践范例包括公共项目工作室和自组织团队,
这些实践通过每日例行会议来协调工作,在例会上要求每位成员回答四个特定问题。
极限编程方法中的实践范例包括结对编程和测试驱动开发。

建模(构建UML草图……)的目的主要是为理解,而非文档。
建模的真正行为能够并且是应该能够对理解问题或解决方案空间提供更好的方式。
从这个角度而言,“实行UML”(或“实行OOA/D”)的目的并不是指设计者创建大量详细的UML图并递交给编程者(这其实是非敏捷和面向瀑布的思维方式),
而是指为良好的OO设计快速探索可选的方案和途径。

UP项目将其工作和迭代组织为四个主要阶段:
(1)初始:大体上的构想,业务案例,范围和模糊评估。
(2)细化:已精化的构想,核心架构的迭代实现,高风险的解决,确定大多数需求和范围以及进行更为实际的评估。
(3)构造:对遗留下来的风险较低和比较简单的元素进行迭代实现,准备部署。
(4)移交:进行beta测试和部署。

需求,就是系统(或者项目)必须提供的能力和必须遵从的条件。
UP提出了一系列的最佳实践,其中之一就是需求管理。
需求管理部主张采用瀑布的观点,即在编程之前项目的第一个阶段就试图完全定义和固化需求。
在变更不可避免,涉众意愿不明朗的情况下,UP更推崇用“一种系统的方法来寻找,记录,组织和跟踪系统不断变更的需求”。

需求分析的最大挑战是寻找,沟通和记住(通常是记录)什么是真正需要的,并能够清楚的讲解给客户和开发团队的成员。
UP能够包容需求中的变更,并将其作为项目的基本驱动力。

在统一过程中,需求按照“FURPS+”模型进行分类:
(1)功能性:特性,功能,安全性
(2)可用性:人性化因素,帮助,文档
(3)可靠性:故障频率,可恢复性,可预测性
(4)性能:响应时间,吞吐量,准确性,有效性,资源利用率
(5)可支持性:适应性,可维护性,国际化,可配置性。

任何提出“尽可能定义大多数需求,然后再进行设计和实现”的建议,都与迭代进化式开发和UP的思想相悖。

用例(use case)就是一组相关的成功和失败场景集合,用来描述参与者如何使用系统来实现其目标。
用例强调了用户的目标和观点,“谁使用系统?他们使用的典型场景是什么?他们的目的是什么?”。
用例就是需求,主要是说明系统如何工作的功能性或行为性需求。
用例的主要思想是:为功能性需求编写用例,从而降低详细的老式特性列表的重要性或减少这种列表的使用。

黑盒用例,是最常见和推荐使用的类型,它不对系统内部工作,构件或设计进行描述。
反之,它以通过职责来描述系统,这是面向对象思想中普遍统一的隐喻主题——软件元素具有职责,并且与其他具有职责的元素进行协作。
通过使用黑盒用例定义系统职责,人们可以规定系统必须做什么(行为和功能性需求),而不必决定系统如何去做(设计)。
实际上,“分析”和“设计”的区别就在于“什么”和“如何”的差异。
这是在优秀软件开发中的重要主题:在需求分析中应避免进行“如何”的决策,而是规定系统的外部行为,就像黑盒一样。
此后,在设计过程中,创建满足该规格说明的解决方案。

发现用例的基本过程:
(1)选择系统边界
(2)确定主要参与者
(3)确定每个主要参与者的目标
(4)定义满足用户目标的用例,根据其目标对用例命名
当然,在迭代和进化式开发中,在开始阶段不必完整或准确的定义所有目标和用例,这是不断深入发掘的过程。

在开始用例建模时,首先要询问的是“谁来使用系统,他们的目标是什么?”
而非“系统的任务是什么?”

初学者通常会犯的错误是,将用例图和用例关系作为当务之急,而不是编写文本。
Flower和Cockburn等世界级的用例专家都对用例图和用例关系不予重视,而是注重编写文本。
用例图是一种优秀的系统语境图,也就是说,用例图能够展示系统边界,位于边界之外的事物以及系统如何被使用。
用例驱动设计,小组设计协作对象和子系统是为了执行或实现用例。

领域模型,是对领域内的概念类或现实世界中对象的可视化表示。
领域模型也称为概念模型,领域对象模型和分析对象模型。
在UP中,术语“领域模型”指的是对现实世界概念类的表示,而非软件对象的表示。

面向对象开发者在创建软件类时,受到真实世界领域的启发。
因此,涉众所设想的领域与其在软件的表示之间的表示差异被降低。

在对领域的文本性描述中识别名词和名词短语,将其作为候选的概念类或属性。

没有所谓唯一正确的领域模型,所有模型都是对我们试图要理解的领域的近似。
领域模型主要是在特定群体中用于理解和沟通的工具,
有效的领域模型捕获了当前需求语境下的本质抽象和理解领域所需要的信息,并且可以帮助人们理解领域的概念,术语和关系。

逻辑架构是软件类的宏观组织结构,它将软件类组织为包(或命名空间),子系统和层等。
之所以称其为逻辑架构,是因为并未决定如何在不同的操作系统进程或网络中物理的计算机上对这些元素进行部署(后一种决定是部署架构的一部分)。

领域层与应用逻辑层:领域对象
典型的软件系统都有UI逻辑和应用逻辑,例如GUI窗口小部件的创建和税金计算。
现在关键问题是,我们如何使用对象设计应用逻辑?
我们可以创建一个称为XYZ的类,然后将所有方法置入其中,以实现所有需要的逻辑。
这一方法在技术上是可行的(尽管在理解和维护上有较大困难),但是OO思想并不提倡这种方法。
那么提倡的方法是什么呢?答案是:
创建软件对象,使其名称和信息类似于真实世界的领域,并且为其分配应用逻辑职责。
这种软件对象称为领域对象,领域对象表示问题领域空间的事物,并且与应用或业务逻辑相关。
以这种方式设计对象,则可以将应用逻辑层更准确的称为架构的领域层,即包含领域对象,处理应用逻辑的层。

另一个关键问题是,领域层和领域模型之间具有怎样的关系。
我们着眼于领域模型(将重要的领域概念的可视化)以获取对领域层类命名的灵感。
领域层是软件的一部分,领域模型是概念角度分析的一部分,它们是不同的。
但是利用来自领域模型的灵感创建领域层,我们可以获得在实现世界和软件设计之间的低表示差异。
(注:领域模型是真实世界的对象模型,领域对象是用软件类表示的软件对象。

应该花费时间使用交互图进行动态对象建模,而不仅是使用类图进行静态对象建模。
应该把时间花费在交互图(顺序图或通信图),而不仅是类图上。
忽视这一原则是十分常见的UML错误实践。

UML使用交互图来描述对象间通过消息的交互。
交互图可以用于动态对象建模,交互图有两种类型,顺序图和通信图。
大部分UML初学者知道类图,并且通常认为类图是OO设计中唯一重要的图形,但实际上并非如此。
尽管静态视图类图确实有效,但动态视图的交互图(更确切的说是动态交互建模中的“动作”)的价值更高。
因为当我们要考虑真正的OO设计细节时,就必须要“落实”发送哪些消息,发送给谁,以何种顺序发送等具体问题。

UML使用类图表示类,接口及其关联,类图用于静态对象建模。

思考软件对象设计以及大型构件的流行方式是,考虑其职责,角色和协作。
这是被称为职责驱动设计(RDD)的大型方法的一部分。
在RDD中,我们认为软件对象具有职责,即对其所作所为的抽象。
UML把职责定义为“类元的契约或义务”。

RDD是思考OO软件设计的一般性隐喻。
把软件对象想象成具有某种职责的人,他要与其他人协作以完成工作。
RDD使我们把OO设计看做是有职责对象进行协作的共同体。

极限编程所提倡的重要测试实践是:首先编写测试。
它还提倡不断的重构代码以改进质量,包括降低冗余,提高清晰度等。

重构是重写或重新构建已有代码的结构化和规律性方法,但不会改变已有代码的外在行为,
而是采用一系列少量转换的步骤,并且每一步都结合了重新执行的测试。
重构的本质是一次实行一小步保留行为的转换。
每次转换之后,要重新执行单元测试,以保证重构不会导致错误。
因此,重构和TDD具有关系——所有的单元测试要支持重构过程。

使用UP的过程成熟标志是,知道何时创建制品能够带来显著价值,或者是当遇到呆板的“完成作业”式的步骤时能够较好的略过。

建模不是过多,而是太少。
开发人员经常会避开任何分析或建模,因为这看起来似乎是低价值和费时间的事。
但是,如果战功了分析和设计的基本原则,适应了这种“语言”(在墙上完成用例,UML或UI原型等),
并且将其应用于敏捷建模的精神中,此时建模是能够带来价值的。

如因素表所做的一样,收集和组织有关的架构性因素,可以称为“架构的科学”。
根据相互依赖情况,优先级,权衡考虑等,做出解决这些因素的决定,可以称为“架构的艺术”。

记录架构选择,决策以及动机。
决定在何处花费精力进行必要的设计,预防将来可能的变化,这是架构设计师的艺术。
架构分析指的是,在功能性需求的语境中识别和解决非功能性需求。

如果开发团队中广泛依赖于某个包X,则他们肯定希望包X是比较稳定的,
因为当包X发生变化时,需要不断的进行版本同步,并且要修改依赖于该包的软件,这样会导致版本过载(version thrashing)。

在一个子系统中,避免直接抛出来自较低层子系统或服务的异常。
应该将较低层的异常转换成在本层次子系统中有意义的异常。
较高层的异常包裹较低层异常并添加一些信息,使得该异常在较高层子系统语境中有意义。

在多个例子中研究模式才有助于消化理解,组建业余学习小组是较常见的学习手段。
参与者在学习小组上分享模式的应用案例,讨论模式书籍中的内容。

在UP设计模型中,除UML包图,类图和交互图之外,软件架构文档(SAD)是另外一个重要制品。
SAD描述有关架构的总体想法,包含架构分析的关键决策。
在实践中,SAD可以帮助开发人员理解系统的基本概念。
从本质上讲,SAD是对架构性决策(例如技术备忘录)的总结以及对N+1架构视图的描述。

拥有架构是一回事,对架构的清晰描述的另外一回事。
架构试图是交流,教育和思考的工具,它可以用文本和UML图表达。
在SAD中,架构设计师将创建被称为逻辑视图的部分,在其中插入这些UML图,并写上有关每个包和层的用途的注释,以及逻辑设计背后的动机。

迭代开发的重要思想之一就是根据反馈不断改进,而不是试图详细预测和计划整个项目。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant