類的理解
類(Class)封裝了數(shù)據(jù)和行為,是面向對象的重要組成部分脓豪,它是具有相同屬性巷帝、操作、關系的對象集合的總稱扫夜。在系統(tǒng)中楞泼,每個類都具有一定的職責,職責指的是類要完成什么樣的功能笤闯,要承擔什么樣的義務堕阔。一個類可以有多種職責,設計得好的類一般只有一種職責颗味。在定義類的時候超陆,將類的職責分解成為類的屬性和操作(即方法)。類的屬性即類的數(shù)據(jù)職責浦马,類的操作即類的行為職責时呀。設計類是面向對象設計中最重要的組成部分,也是最復雜和最耗時的部分捐韩。
在軟件系統(tǒng)運行時退唠,類將被實例化成對象(Object),對象對應于某個具體的事物荤胁,是類的實例(Instance)。
類圖(Class Diagram)使用出現(xiàn)在系統(tǒng)中的不同類來描述系統(tǒng)的靜態(tài)結構屎债,它用來描述不同的類以及它們之間的關系仅政。
在系統(tǒng)分析與設計階段垢油,類通常可以分為三種圆丹,分別是實體類(Entity Class)滩愁、控制類(Control Class)和邊界類(Boundary Class),下面對這三種類加以簡要說明:
(1) 實體類:實體類對應系統(tǒng)需求中的每個實體辫封,它們通常需要保存在永久存儲體中硝枉,一般使用數(shù)據(jù)庫表或文件來記錄,實體類既包括存儲和傳遞數(shù)據(jù)的類倦微,還包括操作數(shù)據(jù)的類妻味。實體類來源于需求說明中的名詞,如學生欣福、商品等责球。
(2) 控制類:控制類用于體現(xiàn)應用程序的執(zhí)行邏輯,提供相應的業(yè)務操作拓劝,將控制類抽象出來可以降低界面和數(shù)據(jù)庫之間的耦合度雏逾。控制類一般是由動賓結構的短語(動詞+名詞)轉化來的名詞郑临,如增加商品對應有一個商品增加類栖博,注冊對應有一個用戶注冊類等
(3) 邊界類:邊界類用于對外部用戶與系統(tǒng)之間的交互對象進行抽象,主要包括界面類厢洞,如對話框仇让、窗口、菜單等犀变。
在面向對象分析和設計的初級階段妹孙,通常首先識別出實體類,繪制初始類圖获枝,此時的類圖也可稱為領域模型蠢正,包括實體類及其它們之間的相互關系。
類與類之間的關系
- 關聯(lián)關系
關聯(lián)(Association)關系是類與類之間最常用的一種關系省店,它是一種結構化關系嚣崭,用于表示一類對象與另一類對象之間有聯(lián)系,如汽車和輪胎懦傍、師傅和徒弟雹舀、班級和學生等等。在UML類圖中粗俱,用實線連接有關聯(lián)關系的對象所對應的類说榆,在使用Java、C#和C++等編程語言實現(xiàn)關聯(lián)關系時,通常將一個類的對象作為另一個類的成員變量签财。在使用類圖表示關聯(lián)關系時可以在關聯(lián)線上標注角色名串慰,一般使用一個表示兩者之間關系的動詞或者名詞表示角色名(有時該名詞為實例對象名),關系的兩端代表兩種不同的角色唱蒸,因此在一個關聯(lián)關系中可以包含兩個角色名邦鲫,角色名不是必須的,可以根據(jù)需要增加神汹,其目的是使類之間的關系更加明確庆捺。
如在一個登錄界面類LoginForm中包含一個JButton類型的注冊按鈕loginButton,它們之間可以表示為關聯(lián)關系屁魏,代碼實現(xiàn)時可以在LoginForm中定義一個名為loginButton的屬性對象滔以,其類型為JButton。如圖1所示:
圖1 關聯(lián)關系實例
圖1對應的Java代碼片段如下:
public class LoginForm {
private JButton loginButton; // 定義為成員變量
...
}
public class JButton {
...
}
在UML中蚁堤,關聯(lián)關系通常又包含如下幾種形式:
(1) 雙向關聯(lián)
默認情況下醉者,關聯(lián)是雙向的。例如:顧客(Customer)購買商品(Product)并擁有商品披诗,反之撬即,賣出的商品總有某個顧客與之相關聯(lián)。因此呈队,Customer類和Product類之間具有雙向關聯(lián)關系剥槐,如圖2所示:
圖2 雙向關聯(lián)實例
圖2對應的Java代碼片段如下:
public class Customer {
private Product[] products;
...
}
public class Product {
private Customer customer;
...
}
(2) 單向關聯(lián)
類的關聯(lián)關系也可以是單向的,單向關聯(lián)用帶箭頭的實線表示宪摧。例如:顧客(Customer)擁有地址(Address)粒竖,則Customer類與Address類具有單向關聯(lián)關系,如圖3所示:
圖3 單向關聯(lián)實例
圖3對應的Java代碼片段如下:
public class Customer {
private Address address;
...
}
public class Address {
...
}
(3) 自關聯(lián)
在系統(tǒng)中可能會存在一些類的屬性對象類型為該類本身几于,這種特殊的關聯(lián)關系稱為自關聯(lián)蕊苗。例如:一個節(jié)點類(Node)的成員又是節(jié)點Node類型的對象,如圖4所示:
圖4 自關聯(lián)實例
圖4對應的Java代碼片段如下:
public class Node {
private Node subNode;
...
}
(4) 多重性關聯(lián)
多重性關聯(lián)關系又稱為重數(shù)性(Multiplicity)關聯(lián)關系沿彭,表示兩個關聯(lián)對象在數(shù)量上的對應關系朽砰。在UML中,對象之間的多重性可以直接在關聯(lián)直線上用一個數(shù)字或一個數(shù)字范圍表示喉刘。
對象之間可以存在多種多重性關聯(lián)關系瞧柔,常見的多重性表示方式如表1所示:
表1 多重性表示方式列表
表示方式 | 多重性說明 |
---|---|
1..1 | 表示另一個類的一個對象只與該類的一個對象有關系 |
0..* | 表示另一個類的一個對象與該類的零個或多個對象有關系 |
1..* | 表示另一個類的一個對象與該類的一個或多個對象有關系 |
0..1 | 表示另一個類的一個對象沒有或只與該類的一個對象有關系 |
m..n | 表示另一個類的一個對象與該類最少m,最多n個對象有關系 (m≤n) |
例如:一個界面(Form)可以擁有零個或多個按鈕(Button)睦裳,但是一個按鈕只能屬于一個界面造锅,因此,一個Form類的對象可以與零個或多個Button類的對象相關聯(lián)廉邑,但一個Button類的對象只能與一個Form類的對象關聯(lián)哥蔚,如圖5所示:
圖5 多重性關聯(lián)實例
圖5對應的Java代碼片段如下:
public class Form {
private Button[] buttons; //定義一個集合對象
...
}
public class Button {
...
}
(5) 聚合關系
聚合(Aggregation)關系表示整體與部分的關系倒谷。在聚合關系中,成員對象是整體對象的一部分肺素,但是成員對象可以脫離整體對象獨立存在恨锚。在UML中宇驾,聚合關系用帶空心菱形的直線表示倍靡。例如:汽車發(fā)動機(Engine)是汽車(Car)的組成部分,但是汽車發(fā)動機可以獨立存在课舍,因此塌西,汽車和發(fā)動機是聚合關系,如圖6所示:
圖6 聚合關系實例
在代碼實現(xiàn)聚合關系時筝尾,成員對象通常作為構造方法捡需、Setter方法或業(yè)務方法的參數(shù)注入到整體對象中,圖6對應的Java代碼片段如下:
public class Car {
private Engine engine;
//構造注入
public Car(Engine engine) {
this.engine = engine;
}
//設值注入
public void setEngine(Engine engine) {
this.engine = engine;
}
...
}
public class Engine {
...
}
(6) 組合關系
組合(Composition)關系也表示類之間整體和部分的關系筹淫,但是在組合關系中整體對象可以控制成員對象的生命周期站辉,一旦整體對象不存在,成員對象也將不存在损姜,成員對象與整體對象之間具有同生共死的關系饰剥。在UML中,組合關系用帶實心菱形的直線表示摧阅。例如:人的頭(Head)與嘴巴(Mouth)汰蓉,嘴巴是頭的組成部分之一,而且如果頭沒了棒卷,嘴巴也就沒了顾孽,因此頭和嘴巴是組合關系,如圖7所示:
圖7 組合關系實例
在代碼實現(xiàn)組合關系時比规,通常在整體類的構造方法中直接實例化成員類若厚,圖7對應的Java代碼片段如下:
public class Head {
private Mouth mouth;
public Head() {
mouth = new Mouth(); //實例化成員類
}
...
}
public class Mouth {
...
}
類與類之間的關系(2)
- 依賴關系
依賴(Dependency)關系是一種使用關系,特定事物的改變有可能會影響到使用該事物的其他事物蜒什,在需要表示一個事物使用另一個事物時使用依賴關系测秸。大多數(shù)情況下,依賴關系體現(xiàn)在某個類的方法使用另一個類的對象作為參數(shù)。在UML中,依賴關系用帶箭頭的虛線表示孩等,由依賴的一方指向被依賴的一方传惠。例如:駕駛員開車,在Driver類的drive()方法中將Car類型的對象car作為一個參數(shù)傳遞肮帐,以便在drive()方法中能夠調用car的move()方法,且駕駛員的drive()方法依賴車的move()方法,因此類Driver依賴類Car关串,如圖1所示:
圖1 依賴關系實例
在系統(tǒng)實施階段拧廊,依賴關系通常通過三種方式來實現(xiàn),第一種也是最常用的一種方式是如圖1所示的將一個類的對象作為另一個類中方法的參數(shù)晋修,第二種方式是在一個類的方法中將另一個類的對象作為其局部變量吧碾,第三種方式是在一個類的方法中調用另一個類的靜態(tài)方法。圖1對應的Java代碼片段如下:
public class Driver {
public void drive(Car car) {
car.move();
}
...
}
public class Car {
public void move() {
...
}
...
}
- 泛化關系
泛化(Generalization)關系也就是繼承關系墓卦,用于描述父類與子類之間的關系倦春,父類又稱作基類或超類,子類又稱作派生類落剪。在UML中睁本,泛化關系用帶空心三角形的直線來表示。在代碼實現(xiàn)時忠怖,我們使用面向對象的繼承機制來實現(xiàn)泛化關系呢堰,如在Java語言中使用extends關鍵字、在C++/C#中使用冒號“:”來實現(xiàn)凡泣。例如:Student類和Teacher類都是Person類的子類枉疼,Student類和Teacher類繼承了Person類的屬性和方法,Person類的屬性包含姓名(name)和年齡(age)鞋拟,每一個Student和Teacher也都具有這兩個屬性骂维,另外Student類增加了屬性學號(studentNo),Teacher類增加了屬性教師編號(teacherNo)严卖,Person類的方法包括行走move()和說話say()席舍,Student類和Teacher類繼承了這兩個方法,而且Student類還新增方法study()哮笆,Teacher類還新增方法teach()来颤。如圖2所示:
圖2 泛化關系實例
圖2對應的Java代碼片段如下:
//父類
public class Person {
protected String name;
protected int age;
public void move() {
...
}
public void say() {
...
}
}
//子類
public class Student extends Person {
private String studentNo;
public void study() {
...
}
}
//子類
public class Teacher extends Person {
private String teacherNo;
public void teach() {
...
}
}
- 接口與實現(xiàn)關系
在很多面向對象語言中都引入了接口的概念,如Java稠肘、C#等福铅,在接口中,通常沒有屬性项阴,而且所有的操作都是抽象的滑黔,只有操作的聲明,沒有操作的實現(xiàn)环揽。UML中用與類的表示法類似的方式表示接口略荡,如圖3所示:
圖3 接口的UML圖示
接口之間也可以有與類之間關系類似的繼承關系和依賴關系,但是接口和類之間還存在一種實現(xiàn)(Realization)關系歉胶,在這種關系中汛兜,類實現(xiàn)了接口,類中的操作實現(xiàn)了接口中所聲明的操作通今。在UML中粥谬,類與接口之間的實現(xiàn)關系用帶空心三角形的虛線來表示肛根。例如:定義了一個交通工具接口Vehicle,包含一個抽象操作move()漏策,在類Ship和類Car中都實現(xiàn)了該move()操作派哲,不過具體的實現(xiàn)細節(jié)將會不一樣,如圖4所示:
圖4 實現(xiàn)關系實例
實現(xiàn)關系在編程實現(xiàn)時掺喻,不同的面向對象語言也提供了不同的語法芭届,如在Java語言中使用implements關鍵字,而在C++/C#中使用冒號“:”來實現(xiàn)巢寡。圖4對應的Java代碼片段如下:
public interface Vehicle {
public void move();
}
public class Ship implements Vehicle {
public void move() {
...
}
}
public class Car implements Vehicle {
public void move() {
...
}
}