曾经看到过一篇blog,上面探讨了学习UML和学习设计模式的区别,同时提出了学习UML还不如学习设计模式的观点。自我感受是,UML和设计模式在本质上属于完全不同的两个方向。设计模式是一种被反复使用、多数人知晓的、代码设计经验的总结。它可以更容易让人理解而且保证了代码的可靠性,而UML则是一种模型化和可视化的一种语言。可以这么说,设计模式是一种方法,它让我们的代码更有层次感
。而UML则是一种表达,它让我们的代码更加形象化。通过UML我们不必太过关注代码,不必担心客户是否了解相关的语言知识,整体的架构和功能的分析和实现成为我们的主要任务。应此,学习UML和设计模式都是很必要的。
UML是标准建模语言的简称,它是一个支持模型化和软件系统开发的图形化语言。为原件开发的所有阶段提供模型化和可视化支持。一般来说,软件开发可以分为5个阶段:1.需求分析阶段,主要是根据客户的需求进行系统功能建模,每一个客户需求都是一个用例。2.分析,主要是考虑需要解决的问题,利用逻辑图和动态图等方式描述系统特征。3.设计,主要是将分析阶段扩展为解决方案。4.构造,主要是吧设计阶段的类转化为相应的代码。5.测试,对系统进行单元和协同测试,以验证系统是否符合要求。
UML有3类主要的图,分别是静态图,动态图和物理图。其中静态图主要表示类、对象的结构和之间的关系。动态图表示在运行期间软件实体的变化。物理图则表示软件实体的不变物理结构,如源文件、2进制文件等。
现在通过实例观察类图的用法:
例如在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) 和类属性稍有不同的是,可以传入参数,“:”后代表的是传参的类型
类的关联大致包括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的内容最好比较简洁明朗,达到言简意赅的效果才是最好。