曾经看到过一篇blog,上面探讨了学习UML和学习设计模式的区别,同时提出了学习UML还不如学习设计模式的观点。自我感受是,UML和设计模式在本质上属于完全不同的两个方向。设计模式是一种被反复使用、多数人知晓的、代码设计经验的总结。它可以更容易让人理解而且保证了代码的可靠性,而UML则是一种模型化和可视化的一种语言。可以这么说,设计模式是一种方法,它让我们的代码更有层次感 。而UML则是一种表达,它让我们的代码更加形象化。通过UML我们不必太过关注代码,不必担心客户是否了解相关的语言知识,整体的架构和功能的分析和实现成为我们的主要任务。应此,学习UML和设计模式都是很必要的。
UML是标准建模语言的简称,它是一个支持模型化和软件系统开发的图形化语言。为原件开发的所有阶段提供模型化和可视化支持。一般来说,软件开发可以分为5个阶段:1.需求分析阶段,主要是根据客户的需求进行系统功能建模,每一个客户需求都是一个用例。2.分析,主要是考虑需要解决的问题,利用逻辑图和动态图等方式描述系统特征。3.设计,主要是将分析阶段扩展为解决方案。4.构造,主要是吧设计阶段的类转化为相应的代码。5.测试,对系统进行单元和协同测试,以验证系统是否符合要求。
UML有3类主要的图,分别是静态图,动态图和物理图。其中静态图主要表示类、对象的结构和之间的关系。动态图表示在运行期间软件实体的变化。物理图则表示软件实体的不变物理结构,如源文件、2进制文件等。
现在通过实例观察类图的用法:
1.类的表示
例如在java中我们定义了一个Person类
public
class
Person {
private
String
name
;
private
int
age
;
public
int
publicId
;
protected
int
className
;
public
void
setName(String name){
this
.
name
= name;
}
public
String getName(){
return
this
.
name
;
}
}
这个类在UML图中的表示是
从这个实例中我们看出:
1.类的组成:类名称写在第一排,类的属性写在第二排,类的方法写在第三排。
+代表public,-代表private,#代表protected。
2.类的属性:其格式为 (+|-|#)?(yourClassName):(type)
3.类的方法:其格式为 (+|-|#)?(yourClassName/((params)?/)):(type) 和类属性稍有不同的是,可以传入参数,“:”后代表的是传参的类型
2.类的关联
类的关联大致包括3个方面,继承、包含另一个类的引用
和
接口。
继承:
在java中定义一个Man类,让它继承Person类
public
class
Man
extends
Person {
private
int
money
;
public
int
getMoney() {
return
money
;
}
public
void
setMoney(
int
money) {
this
.
money
= money;
}
}
在UML图中可以这么表示
从这张图可以看出:我们可以用一个带空心三角形的符号来表示继承的关系。同时,箭头指向的类为超类,箭头的源头为子类。
包含另一个类的引用
在这里我们创建一个Woman类,它包含Man的一个引用。在实际生活中很多家庭主妇的钱都是老公赚的(除了女强人,哈哈)。因此,在Woman类中money属性的值是由Man的一个引用来决定的。代码实现如下:
public
class
Woman {
private
Man
myMan
;
private
int
money
;
public
int
getMoney(){
this
.
money
=
myMan
.getMoney();
return
this
.
money
;
}
public
void
setMyMan(Man man){
this
.
myMan
= man;
}
}
用UML图我们可以这么表示:
从图中我们可以看到:当一个类拥有另一个类的引用时,应用带箭头的线表示,箭头指向为被引用的对象,源头为引用者。
这里要注意箭头的不同,继承为三角形,而引用则不是。
接口
java中接口是实现多态的重要一环,也是封装不可缺少的一部分。在UML中对接口的使用也有特殊的表示:
从图可以看出:UML中的接口用一个圆圈和一条直线表示。
3.创建特殊的类
在java中我们常需要创建一个接口来实现封装。我们也常需要创建一个抽象类来实现多态。有时候,我们也要用一个静态类作为一个静态常量的容器。下面我们一起学习如何在UML中表示这些特殊的类。
接口类
在java中我们定义一个Student接口,这个接口要实现write()和read()方法。
public
interface
Student {
public
void
write();
public
void
read();
}
这个接口在UML中的表示方法如下:
从图中我们可以看出:可以用<<interface>>来表示一个接口类。
静态类
我们在java中定义一个Math的静态类,其中定义PI的属性:
public
class
MathParams {
static
class
Math {
public
static
final
double
PI
= 3.141592654;
}
}
这个静态类,我们在UML中可以这么表示:
从图中可以看出:可以用<<utility>>来表示一个静态类。
抽象类
在java中我们定义一个抽象Person类,这个Person类有两个方法,抽象的write()方法和具体的eat()方法(因为每个人都会吃,但不是每个人都会写字,呃...)。
public
abstract
class
PersonAbstract {
public
abstract
void
write();
public
void
eat(){
//...
}
}
这个抽象类在UML中的表达方式是:
从图中可以看出,抽象的类和方法都用斜体 表示。因为本人用的是VISIO开发工具,其默认是用斜体表示。还有另一种表示方法是在抽象的类和方法后加上{abstract}约束。而且这个“{}”不仅可以加"abstract",还可以添加自己的属性,如{name = "Lee",age = 13}。
4.类图中的一些常用标记
这三种表示方法差别并不大,所以在大多数时候只采用第一种方式就行了。最后一种方式可以表示:两个类是组合在一起的,即Woman的一个对象复制后,它对应的那个Man对象也会复制。当Woman的一个对象清空时,那么它对应的那个Man对象也会被清空。
内部类是用一个圆圈中含有十字架加上一条线段组成(在VISIO中并未找到,还请了解的人相告)。
在包含类的引用时,当包含的引用不止一条时,我们就需要用一个容器装载包含的引用,可能是Vector,也可能是其他的。这时,我们可以用关联类的表示:
5.一个简单的应用
这里是一个简单的二元树算法:
首先,为了更加容易的理解算法,我们回顾一下Comparable接口和Comparator接口。
Comparable和Comparator都实现了比较的功能,不能的是:1.Comparable是java.lang包,而Comparator是java.util包中。2.Comparable接口需要绑定到一个指定的类上,而Comparator则不需要。因此,comparator更加灵活。3.Comparable需要重写comparaTo方法,且只需传入绑定的对象一个参数,而Comparator需要重写compare方法,需要传入比较的两个对象,两个参数。下面我们用比较两个字符串的长度来看看两中方法的差别:
使用Comparable来比较两个字符串的长度:
//通过实现Comparable接口来自定义比较的规则,实现compareTo方法
public
class
MyComparable
implements
Comparable<MyComparable> {
//这个例子实现比较两个字符串的长度
private
String
compareString
;
MyComparable(String compareString){
this
.
compareString
= compareString;
}
//这里重写两个比较的规则
@Override
public
int
compareTo(MyComparable o) {
//
TODO
Auto-generated method stub
return
this
.
compareString
.length() - o.
compareString
.length();
}
}
使用Comparator来比较两个字符串的长度:
import
java.util.Comparator;
//实现Comparator接口无需绑定类,可实现动态排序
public
class
Mycomparator
implements
Comparator<String> {
@Override
public
int
compare(String o1, String o2) {
//
TODO
Auto-generated method stub
return
o1.length() - o2.length();
}
}
了解了Comparable接口的用法后,我们来看简单的二元树的算法:
public
class
TreeMap {
TreeMapNode
topNode
=
null
;
public
void
add(Comparable key,Object value){
if
(
topNode
==
null
)
topNode
=
new
TreeMapNode(key,value);
else
topNode
.add(key,value);
}
public
Object get(Comparable key){
return
topNode
==
null
?
null
:
topNode
.get(key);
}
}
class
TreeMapNode {
public
static
final
int
LESS
= 0;
public
static
final
int
GREATER
= 1;
private
Comparable
itsKey
;
private
Object
itsValue
;
private
TreeMapNode
nodes
[] =
new
TreeMapNode[2];
public
TreeMapNode(Comparable key,Object value) {
//
TODO
Auto-generated constructor stub
this
.
itsKey
= key;
this
.
itsValue
= value;
}
public
void
add(Comparable key, Object value) {
//
TODO
Auto-generated method stub
if
(
itsKey
.compareTo(key) == 0)
itsValue
= value;
else
addSubNode(selectSubNode(key),key,value);
}
private
void
addSubNode(
int
node, Comparable key, Object value) {
//
TODO
Auto-generated method stub
if
(
nodes
[node] ==
null
)
nodes
[node] =
new
TreeMapNode(key, value);
else
nodes
[node].add(key, value);
}
private
int
selectSubNode(Comparable key) {
//
TODO
Auto-generated method stub
return
( key.compareTo(
itsKey
) < 0)?
LESS
:
GREATER
;
}
public
Object get(Comparable key) {
//
TODO
Auto-generated method stub
if
( key.compareTo(
itsKey
) == 0)
return
itsValue
;
return
getSub(selectSubNode(key),key);
}
private
Object getSub(
int
node, Comparable key) {
//
TODO
Auto-generated method stub
return
nodes
[node] ==
null
?
null
:
nodes
[node].get(key);
}
}
下面我们用UML图来表示这个类:
6.总结
UML本来就是一个让代码更加形象的东东,应此不需要太过复杂。所以UML的内容最好比较简洁明朗,达到言简意赅的效果才是最好。