软件开发的相关技术
作者:naven
2005-5-10
1
、
Rational
统一开发过程(
Rational Unified Process
简称
RUP
)
是软件开发队伍的最佳实践
什么是
Rational Unified Process
RUP
是软件工程化过程它提供了在开发机构中分派任务和责任的纪律化方法它的目标是在可预见的日程和预算前提下确保满足最终用户需求的高质量产品
RUP
是有效使用
Unified Modeling Language
(
UML
)的指南。是良好沟通需求体系结构和设计的工业标准语言。
UML
是由
Rational
软件公司创建现在由标准化对象管理机构(
OMG
)维护
“统一过程”概述
“统一过程”是软件开发过程。软件开发过程是将用户的需求转化为一个软件系统的一系列活动的总称。然而,“统一过程”不仅仅是一个过程。它是一个通用过程框架,可以应付种类广泛的软件系统、不同的应用领域、不同的组织类型、不同的性能水平和不同的项目规模。
“统一过程”是基于组件的,这意味着利用它开发的软件系统是由组件构成的,组件之间通过定义良好的接口相互联系
。
在准备软件系统的所有蓝图的时候,“统一过程”使用的是“统一建模语言(
Unified Modeling anguage
)”。事实上,
UML
是“统一过程”的有机组成部分——它们是被同步开发的
。
然而,真正使“统一过程”与众不同的方面可以用三个句话来表达:它是用例驱动的、以基本架构为中心的、迭代式和增量性的。正是这些特征使得“统一过程”卓尔不群。
“统一过程”是用例驱动的“统一过程”是用例驱动的“统一过程”是用例驱动的“统一过程”是用例驱动的开发软件系统的目的是要为该软件系统的用户服务。因此,要创建一个成功的软件系统,我们必须明白其潜在用户需要什么
。
2
、统一模语言
UML
概述
标准建模语言
UML
的重要内容可以由下列五类图
(
共
9
种图形
)
来定义
:
·第一类是用例图
,
从用户角度描述系统功能
,
并指出各功能的操作者。
·第二类是静态图
(Static diagram),
包括类图、对象图和包图。其中类图描述系统中类的静态结构。不仅定义系统中的类
,
表示类之间的联系如关联、依赖、聚合等
,
也包括类的内部结构
(
类的属性和操作
)
。类图描述的是一种静态关系
,
在系统的整个生命周期都是有效的。对象图是类图的实例
,
几乎使用与类图完全相同的标识。他们的不同点在于对象图显示类的多个对象实例
,
而不是实际的类。一个对象图是类图的一个实例。由于对象存在生命周期
,
因此对象图只能在系统某一时间段存在。包由包或类组成
,
表示包与包之间的关系。包图用于描述系统的分层结构。
·第三类是行为图
(Behavior diagram),
描述系统的动态模型和组成对象间的交互关系。
·第四类是交互图
(Interactive diagram),
描述对象间的交互关系。
·第五类是实现图
( Implementation diagram )
。
RUP
及
UML
缺点:过于复杂甚至有些啰嗦。
3
、面向对象软件开发和过程
A
代码是软件开发的基础
编码是软件开发过程中最基本、最底层的技艺,然而也是最重要的技艺。任何一个领域的专家都需要花费大量的时间来进行基本技艺的锻炼,木匠需要花费大量的时间来锻炼他们对各种工具的掌握,厨师则需要练习刀工和火候。程序员也是一样的,对我们来说,语言的各种特性必须要了然于胸。而对软件的管理也需要从代码做起。
面向对象的代码已经在现在的软件开发中占据了主流的位置,面向对象的思路也有其优势所在,就像后文所讨论的,面向对象代码有着非面向对象代码的很多优势,而软件业中很多新的思潮的产生,也都是基于面向对象语言的,所以我们关注的代码将是面向对象代码。
编写优秀的面向对象代码并不是一件容易的事情,优秀的
OO
代码如行云流水,糟糕的
OO
代码让人觉得浑身起鸡皮疙瘩。编写优秀的
OO
代码要求程序员有一定的自我修养,能够以抽象的思路看待问题,找到问题的核心并对问题域进行分解。它强调的是一种解题的思路,但这个解不是唯一的。
编写优秀
OO
代码虽难,但还有更难的事情,就是让整个开发团队都产出优秀的
OO
代码。
B
面向对象软件开发过程
普通的软件开发过程和面向对象开发过程有着很大的不同。回想我们在非面向对象中开发过程中,最经常采用的任务分配方法就是以软件模块为单位,这样的好处是分配简单,不同任务之间耦合程度低,容易操作。坏处是几乎无法做到重用,也缺乏整体性的设计。而面向对象软件开发则不同,它是以类、类集合作为基本单位的。类之间关系错综复杂(虽然我们提倡低耦合的设计,但类之间的关系仍然是相对复杂的)。这种情况下程序员之间相互协作的要求就非常之高,这种关系如果处理恰当,则能够完全体现出面向对象的威力,否则,那将会是一场大灾难,面向对象的软件开发过程要养成一些好的习惯:尽量简化和稳定客户端。准备一份简洁的文档,并保持更新。尽可能多的考虑异常和错误的情况。
C
基于面向对象代码的分析框架
在一个开发过程中,往往有着多种复杂的因素:过程、技能、工具、规范、组织、个性。所有的这些,都会对最终的代码产生影响,对代码的成本和质量产生影响。软件最有价值的部分是代码,根据敏捷方法和精益编程的思路,除了代码之外的产出物,都不具有价值,或者说对最终用户没有价值。但它们都需要成本的投入,而我们应该考虑如何节省这些成本。
4
、应用框架
框架不是框框—应用框架的基本思想
软件构件化是
21
世纪软件工业发展的大势趋。工业化的软件复用已经从通用类库进化到了面向领域的应用框架。
Gartner Group
认为:“到
2003
年,至少
70%
的新应用将主要建立在如软件构件和应用框架这类‘构造块’之上;应用开发的未来就在于提供一开放体系结构,以方便构件的选择、组装和集成”。框架的重用已成为软件生产中最有效的重用方式之一。
什么是框架?
框架(
Framework
)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法
;
另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
可以说,一个框架是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,表现为一组抽象类以及其实例之间协作的方法,它为构件复用提供了上下文
(Context)
关系。因此构件库的大规模重用也需要框架。
框架和设计模式
框架、设计模式这两个概念总容易被混淆,其实它们之间还是有区别的。构件通常是代码重用,而设计模式是设计重用,框架则介于两者之间,部分代码重用,部分设计重用,有时分析也可重用。在软件生产中有三种级别的重用:内部重用,即在同一应用中能公共使用的抽象块
;
代码重用,即将通用模块组合成库或工具集,以便在多个应用和领域都能使用;应用框架的重用,即为专用领域提供通用的或现成的基础结构,以获得最高级别的重用性。
为什么要进行框架开发
?
框架的最大好处就是重用。面向对象系统获得的最大的复用方式就是框架,一个大的应用系统往往可能由多层互相协作的框架组成。
由于框架能重用代码,因此从一已有构件库中建立应用变得非常容易,因为构件都采用框架统一定义的接口,从而使构件间的通信简单。
框架能重用设计。它提供可重用的抽象算法及高层设计,并能将大系统分解成更小的构件,而且能描述构件间的内部接口。这些标准接口使在已有的构件基础上通过组装建立各种各样的系统成为可能。只要符合接口定义,新的构件就能插入框架中,构件设计者就能重用构架的设计。
框架还能重用分析。所有的人员若按照框架的思想来分析事务,那么就能将它划分为同样的构件,采用相似的解决方法,从而使采用同一框架的分析人员之间能进行沟通。
有利于在一个项目内多人协同工作;
大粒度的重用使得平均开发费用降低,开发速度加快,开发人员减少,维护费用降低,而参数化框架使得适应性、灵活性增强。
框架能使应用程序的开发简单,价格低廉,但是开发框架不是一件容易的事。它是一个需要领域和设计经验的反复过程。为了保证框架的灵活性,必须提取和发现热点。设计模式可以简化这个过程,因为它提供了对过去经验的抽象。应用框架能高度抽象同一领域内的问题,进而降低开发难度和强度。虽然框架和构件技术已经出现许多年了,开始走入实用
,
但还不成熟,有大量问题有待研究。
5
、模块化和构件化设计
6
、概要设计怎么做
在需求明确、准备开始编码之前,要做概要设计,而详细设计可能大部分公司没有做,有做的也大部分是和编码同步进行,或者在编码之后。因此,对大部分的公司来说,概要设计文档是唯一的设计文档,对后面的开发、测试、实施、维护工作起到关键性的影响。
大的系统的概要设计相当于架构设计和初步的系统设计。
概要设计的目的(架构设计)
将软件系统需求转换为未来系统的设计;
逐步开发强壮的系统构架;
使设计适合于实施环境,为提高性能而进行设计;
结构应该被分解为模块和库。
概要设计的任务
制定规范:代码体系、接口规约、命名规则。这是项目小组今后共同作战的基础,有了开发规范和程序模块之间和项目成员彼此之间的接口规则、方式方法,大家就有了共同的工作语言、共同的工作平台,使整个软件开发工作可以协调有序地进行。
总体结构设计:
功能(加工)-
>
模块:每个功能用那些模块实现,保证每个功能都有相应的模块来实现;
模块层次结构:某个角度的软件框架视图;
模块间的调用关系:模块间的接口的总体描述;
模块间的接口:传递的信息及其结构;
处理方式设计:满足功能和性能的算法
用户界面设计;
数据结构设计:
详细的数据结构:表、索引、文件;
算法相关逻辑数据结构及其操作;
上述操作的程序模块说明(在前台?在后台?用视图?用过程?······)
接口控制表的数据结构和使用规则
其他性能设计。
概要设计写什么
结构化软件设计说明书结构(因篇幅有限和过时嫌疑,在此不作过多解释)
任务:目标、环境、需求、局限;
总体设计:处理流程、总体结构与模块、功能与模块的关系;
接口设计:总体说明外部用户、软、硬件接口;内部模块间接口(注:接口≈系统界面)
数据结构:逻辑结构、物理结构,与程序结构的关系;
模块设计:每个模块“做什么”、简要说明“怎么做”(输入、输出、处理逻辑、与其它模块的接口,与其它系统或硬件的接口),处在什么逻辑位置、物理位置;
运行设计:运行模块组合、控制、时间;
出错设计:出错信息、处错处理;
其他设计:保密、维护;
7
、代码规范和注释规范
8
、单元测试和测试案例
9
、团队协作及大中规模软件开发过程
大规模软件需要团队合作开发。
第一步:需求分析,产出
Use-Case
用例图
第二步:概要设计,产出概要设计文档(模块设计和代码规范等)
第三步:架构设计,产出模块化设计,功能设计,接口设计,模块协作图等图及文档
第四步:系统设计,产出
Sequence
序列图,功能流程程图
第五步:详细设计,产出
Class
类图,伪代码设计,测试案例等
第六步:代码开发及单元测试
第七步:压力测试和整体联调测试
第八步:完成后续文档和维护文档等
10
、面向对象设计原则
The OpenThe Open--Closed PrincipleClosed Principle
任何系统在其生命周期中都会发生变化。如果我们希望开发出的系统不会在第一版本后就被抛弃,那么我们就必须牢牢记住这一点。
软件组成实体(类,模块,函数,等等)应该是可扩展的,但是不可修改的。
OCP OCP
特征
1
、可扩展(对扩展是开放的)
模块的行为功能可以被扩展,在应用需求改变或需要满足新的应用需求时,我们可以让模块以不同的方式工作
2
、不可更改(对更改是封闭的)
这些模块的源代码是不可改动的。任何人都不许修改模块的源代码。
关键是抽象!
模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改(
closed for modification
)的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。
符合
OCP
原则的程序只通过增加代码来变化而不是通过更改现有代码来变化,因此这样的程序就不会引起象非开放―封闭(
open-closed
)的程序那样的连锁反应的变化。
对可变性的封装
考虑系统中什么可能会发生变化
一种可变性不应当散落在代码的很多角落里,而应当被封装到一个对象里
正确理解继承
一种可变性不应当与另一个可变性混合在一起
选择性的封闭(
Strategic Closure
)没有任何一个大的程序能够做到
100%
的封闭。一般来讲,无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,因此就必须选择性地对待这个问题。也就是说,设计者必须对于他(她)设计的模块应该对何种变化封闭做出选择。
Liskov
替换原则替换原则
LSP The The Liskov Substitution Principle
OCP
原则背后的主要机制是抽象和多态。支持抽象和多态的关键机制是继承。
LSP
的定义
若对于每一个类型
S
的对象
o1
,都存在一个类型
T
的对象
o2
,使得在所有针对
T
编写的程序
P
中,用
o1
替换
o2
后,程序
P
的行为功能不变,则
S
是
T
的子类型。
LSP
原则清楚地指出,
OOD
中
Is-A
关系是就行为功能而言。行为功能(
behavior
)不是内在的、私有的,而是外在、公开的,是客户程序所依赖的。行为功能(
behavior
)才是软件所关注的问题!所有派生类的行为功能必须和客户程序对其基类所期望的保持一致。
LSP
和
DBC
DBC
(
Design by Contract
)定义把类和其客户之间的关系看作是一个正式的协议,明确各方的权利和义务。
DBC
对类的要求类的方法声明为先决条件(
precondition
)和后续条件(
postcondition
)。为了让方法得以执行,先决条件必须为真。完成后,方法保证后续条件为真。
DBC
对派生类的要求当重新定义派生类中的例行程序时,我们只能用更弱的先决条件和更强的后续条件替换之。
LSP
-结论
LSP
原则是符合
OCP
原则应用程序的一项重要特性。仅当派生类能完全替换基类时,我们才能放心地重用那些使用基类的函数和修改派生类型。
高层模块不应该依赖于低层模块。二者都应该依赖于抽象。
抽象不应该依赖于细节。细节应该依赖于抽象。
实施重点
从问题的具体细节中分离出抽象,以抽象方式对类进行耦合
不足
导致生成大量的类
假定所有的具体类都是会变化的,这也不总是正确的
DIP
与设计模式
DIP
以
LSP
为基础,是实现
OCP
的主要手段,是设计模式研究和应用的主要指导原则
接口的污染(
Interface Contamination
)一个没有经验的设计师往往想节省接口的数目,将一些功能相近或功能相关的接口合并,并将这看成是代码优化的一部分。
定义:从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小的接口上的。使用多个专门的接口比使用单一的总接口要好。
合成
/
聚合复用原则(
CARP
)尽量使用合成
/
聚合、尽量不使用继承
定义:在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用这些对象的目的。
11
、设计模式
在面向对象的编程中,软件编程人员更加注重以前的代码的重用性和可维护性。
设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
一般而言,一个模式有四个基本要素
1.
模式名称(
pattern name
)
一个助记名,它用一两个词来描述模式的问题、解决方案和效果。命名一个新的模式增加了我们的设计词汇。设计模式允许我们在较高的抽象层次上进行设计。基于一个模式词汇表,我们自己以及同事之间就可以讨论模式并在编写文档时使用它们。模式名可以帮助我们思考,便于我们与其他人交流设计思想及设计结果。找到恰当的模式名也是我们设计模式编目工作的难点之一。
2.
问题
(problem)
描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等。也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。
3.
解决方案
(solution)
描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。
4.
效果
(consequences)
描述了模式应用的效果及使用模式应权衡的问题。尽管我们描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。
一些基本的设计模式
Abstract Factory
:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
Adapter
:将一个类的接口转换成客户希望的另外一个接口。
A d a p t e r
模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
Bridge
:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Builder
:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Chain of Responsibility
:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
Command
:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
Composite
:将对象组合成树形结构以表示“部分
-
整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
Decorator
:动态地给一个对象添加一些额外的职责。就扩展功能而言,
它比生成子类方式更为灵活。
Facade
:为子系统中的一组接口提供一个一致的界面,
F a c a d e
模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Factory Method
:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。
Factory Method
使一个类的实例化延迟到其子类。
Flyweight
:运用共享技术有效地支持大量细粒度的对象。
Interpreter
:给定一个语言
,
定义它的文法的一种表示,并定义一个解释器
,
该解释器使用该表示来解释语言中的句子。
Iterator
:提供一种方法顺序访问一个聚合对象中各个元素
,
而又不需暴露该对象的内部表示。
Mediator
:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Memento
:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
Observer
:定义对象间的一种一对多的依赖关系
,
以便当一个对象的状态发生改变时
,
所有依赖于它的对象都得到通知并自动刷新。
Prototype
:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
Proxy
:为其他对象提供一个代理以控制对这个对象的访问。
Singleton
:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
State
:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
Strategy
:定义一系列的算法
,
把它们一个个封装起来
,
并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
Template Method
:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
Template Method
使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Visitor
:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
12
、经验谈
1
、不迷信于某篇文章或某人或某一个理论,理解和领悟胜于死搬硬套,灵活运用
2
、用抽象的思维代替程序化的思维,找出事物的共性,分离出抽象的接口,可以不变应万变,适应不同的需求。
3
、用对象代替数据,用成员方法代替函数来思考问题,设计系统。
4
、代码要规范,注释要规范。比如头文件的包含关系,类型定义、声明和实现的位置等,都需要有规范的统一。
5
、学会拿来主义,从别的优秀的系统中吸取自己想要的东西,而不是自己照搬照抄或者全部从零构建系统。
6
、框架是代码的复用,设计模式是设计的复用,面向对象是设计的方法,
UML
是设计的工具,
RUP
是软件开发的过程。
作者:naven 2005-5-10
参考文献:
1
、
www.uml.org.cn
、
2
、《
UML
:
Java
程序员指南》