鄭朝霞
摘要:在使用Java語言開發(fā)軟件過程中,好的軟件產(chǎn)品必須具有可擴展性好、可維護性及可復用性,也就是軟件系統(tǒng)要符合開閉原則的。該文討論了在軟件開發(fā)中如何引入接口來實現(xiàn)多態(tài),從而使軟件系統(tǒng)滿足開閉原則。
關鍵詞:開閉原則;Java語言;多態(tài)性
中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2016)30-0262-03
1 背景
在學習JAVA程序設計的過程中,經(jīng)常會看到這樣的話語:正是由于接口和抽象類的存在才賦予java強大的面向?qū)ο蟮哪芰?。但對于初學者來說,要理解抽象類和接口的語法很容易,要理解抽象類和接口的本質(zhì)作用卻比較難。如果我們結合面向?qū)ο蟮脑O計模式中的開閉原則來理解接口和抽象類的作用,則會比較透徹、容易理解。1988年,Bertrand Meyer在他的著作《Object Oriented Software Construction》
中提出了開閉原則(OCP,Open-Closed Principle),開閉原則的描述比較簡單,具體如下:Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.軟件實體(類,模塊,方法等等)應當對擴展開放,對修改關閉。其中open for extension(對擴展開放)指的是當出現(xiàn)新需求的時候,可以通過擴展現(xiàn)有軟件實體達到目的。開閉原則中“open”,是指軟件組件功能的擴展是開放的,允許對其進行功能擴展?!盋lose for modification”(對修改關閉)指的是不允許對該實體做任何修改,“close”是指對于原有代碼的修改是封閉的,即不應該修改原有的代碼。也就是說,需要執(zhí)行多樣行為的軟件實體應該設計成不需要修改就可以實現(xiàn)各種需求的變化,堅持開閉原則可以大大減輕軟件項目維護的工作量。
開閉原則的核心就是:軟件實體應當對擴展開放,對修改關閉。
實現(xiàn)開閉原則的關鍵就在于“抽象”,而不是基于“實現(xiàn)”來編程。下面我們來看看一個基于“實現(xiàn)”編程的例子。
假如我們要實現(xiàn)這樣一個需求:爸爸喝綠茶。通過這個需求,我們分析其中包含兩個類:爸爸類和綠茶類。兩個類之間的關系如下:
用JAVA語言實現(xiàn)的代碼如下:
public class GreenTea {
private String color;
private String teaName;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public String use() {
return "綠茶是未經(jīng)發(fā)酵制成的茶,因此較多地保留了鮮葉的天然物質(zhì).沖泡綠茶時,水溫控制在80℃~90℃左右...";
}
}
class Daddy {
public void drink(GreenTea gTea) {
System.out.println("爸爸喜歡喝" +gTea.getName());
System.out.println(gTea.use());
}
}
public class DrinkTest {
public static void main(String[] args) {
Daddy dad = new Daddy();
GreenTea gt = new GreenTea();
gt.setName(“綠茶”);
gt.setColor(“紅茶”);
System.out.println(“我是”+gt.getName()+”,顏色為:”+gt.getColor);
dad.drink(gt);
}
}
在上述代碼中,我們可以看到,這是基于實現(xiàn)來進行的編程,Daddy類是進行業(yè)務邏輯處理的,屬于高層模塊,GreenTea類是低層模塊,負責基本的原子操作。在這段代碼中,Daddy類依賴GreenTea類,也就是高層模塊依賴低層模塊,我們看看會帶來什么樣的問題。
在實際的軟件開發(fā)中,需求總是在發(fā)生變化,假如需求變成這樣:爸爸因為胃不好,改喝紅茶了。紅茶的代碼如下:
class BlackTea {
public String use() {
return "紅茶是一種發(fā)酵而成的茶,其沖泡方法更是豐富多彩,玻璃杯沖泡法、瓷杯沖泡法......";
}
}
現(xiàn)在我們就會發(fā)現(xiàn),必須要修改負責業(yè)務邏輯處理的Daddy類,爸爸才能喝紅茶,如果爸爸的需求不斷發(fā)生變化,我們就得不斷的修改Daddy類,很顯然上述代碼不符合開閉原則。
而在實際的軟件開發(fā)中,修改業(yè)務邏輯處理的高層模塊將會帶來風險,引入錯誤,對于后期的軟件開發(fā)和維護帶來很多的問題。
我們必須修改設計,讓它符合開閉原則。
2 開閉原則的應用
上述代碼顯然不符合開閉原則,因為進行業(yè)務邏輯處理的Daddy類和GreenTea類的耦合度太高,需要降低它們之間的耦合度!那么如何才能降低耦合度。
解決該問題的最關鍵因素在于抽象化,在編輯語言當中,系統(tǒng)可以將抽象設計定義為較為固定的設計方式,利用Java語言,可以定義多個接口??梢詳U展實現(xiàn)類,而抽象層都不會改變,這就進一步使得開閉原則的第二條得到滿足,對修改進行關閉。此外,系統(tǒng)的行為可以通過從抽象層導出多個新的實現(xiàn)類而改變。由于系統(tǒng)設計是開放的,這與開閉原則的第一條存在著高度的一致性。
在這里我們設計了一個三層的結構,通過引入接口和抽象類來實現(xiàn)開閉原則。第一層是接口ITea,定義了一個use()方法;第二層是抽象類Tea,實現(xiàn)了ITea接口;第三層是具體的實現(xiàn)類,可以擴展很多個。
接口ITea只定義子類應該實現(xiàn)的方法use(),而沒有實現(xiàn)公有的功能。在ITea接口中只有一個方法use(),專門進行茶的處理, 由所有的實現(xiàn)類實現(xiàn)該方法。同時 ITea作為接口應該是穩(wěn)定且可靠的,不應該經(jīng)常發(fā)生變化,否則接口做為契約的作用就失去了效能。
引入了抽象類Abstrac class Tea,實現(xiàn)了公有的功能setColor()、getColor()、setName()、getName,但并沒有實現(xiàn)use()方法,use()針對抽象茶類進行編程,由具體的實現(xiàn)類分別去實現(xiàn)各自的use()方法。并通過構造方法由客戶端來設置實例化的具體茶對象。如果需要增加一種新的茶,如咖啡Coffee,只需要將Coffee也作為Tea的子類,無須修改現(xiàn)有類庫的源代碼。
現(xiàn)階段在大部分的軟件在開發(fā)的過程當中,都定義了實現(xiàn)類的接口,通過這種接口的定義,只需要改變一個實現(xiàn)類就可以實現(xiàn)所有實體的轉(zhuǎn)變。
通過引入接口和抽象類,就可以讓負責業(yè)務邏輯處理的Daddy類依賴抽象類Tea,而不是依賴實現(xiàn)類GreenTea類、BlackTea類。也就是高層模塊應該依賴抽象,而不是具體的實現(xiàn)。如圖所示,各子類由接口類定義了接口方法,只需要在不同的子類中編寫不同的實現(xiàn)即可,當然也可以擴展自有的方法。
修改后的設計如下:
修改設計后的實現(xiàn)代碼如下:
這個是接口ITea,主要定義方法use()
public interface ITea {
public String use();
}
這個是抽象類Tea,抽象類Tea實現(xiàn)了接口ITea
public abstract class Tea implements ITea{
private String color;
private String teaName;
public ITea(String teaName, String color) {
this.teaName = teaName;
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public abstract String use();
}
這個是實現(xiàn)類GreenTea,繼承了抽象類Tea, 實現(xiàn)了use()方法。我們可以擴展很多類似的實現(xiàn)類。
public class GreenTea extends Tea {
public String use() {
return "綠茶是未經(jīng)發(fā)酵制成的茶,因此較多地保留了鮮葉的天然物質(zhì).沖泡綠茶時,水溫控制在80℃~90℃左右...";
}
}
擴展的實現(xiàn)類紅茶類BlackTea,同樣也是繼承了抽象類Tea,實現(xiàn)了use()方法。我們不需要修改其他的設計,只需要擴展實現(xiàn)類就可以了。
class BlackTea extends Tea {
public String use() {
return "紅茶是一種發(fā)酵而成的茶,其沖泡方法更是豐富多彩,玻璃杯沖泡法、瓷杯沖泡法......";
}
}
這個是業(yè)務邏輯處理類Daddy,依賴于抽象類Tea.
class Daddy {
public void drink(Tea tea) {
System.out.println("爸爸喜歡喝"+tea.getTeaName()+"!");
System.out.println("顏色為:"+tea.getColor()+"!");
System.out.println(tea.use());
}
}
下面的代碼主要為了測試軟件的功能,是不是實現(xiàn)了開閉原則。
public class DrinkTest {
public static void main(String[] args) {
Daddy dad = new Daddy();
GreenTea gt = new GreenTea(“綠茶”,”綠色”);
BlackTea bt = new BlackTea(“紅茶”,”褐色”);
dad.drink(gt);
dad.drink(bt);
}
}
上面的程序是利用Java語言的多態(tài)性來實現(xiàn)的軟件開閉原則。多態(tài)性是指程序中定義的父類引用變量所指向的對象和該引用變量所進行的方法調(diào)用在代碼編寫時并不確定,該引用變量所指向的對象往往都是在程序運行的過程當中才決定的,以及該變量的調(diào)用究竟是在哪個子類當中所實現(xiàn)的,同時這也必須是要在程序運行的過程當中才可以實現(xiàn)的。這是由于具體的子類是在程序運行的過程當中才被決定下來的,通過這種方式,源代碼并不需要 經(jīng)過修改就可以被直接實現(xiàn),在這個過程當中調(diào)用的具體方法也會隨著被改變,同時程序在運行過程當中所綁定的代碼也會被隨著改變,同時該程序也可以以多個狀態(tài)運行,這就是所謂的多態(tài)性,正是因為具有多態(tài)性才導致軟件具有較強的擴展性與靈活性。
在Daddy類中,drink(Tea tea)中父類類型(Tea)的引用將指向子類的對象(GreenTea、BlackTea等等),在程序運行的過程中,根據(jù)指向的不同實現(xiàn)類,將調(diào)用不同的子類對象的use()方法。GreenTea、BlackTea等實現(xiàn)類繼承抽象類Tea,并實現(xiàn)了Tea的use()方法。由于GreenTea、BlackTea實現(xiàn)了use()方法,那么父類類型的引用gt在調(diào)用該方法時將會調(diào)用子類中重寫的use()方法。
也就是說,如果爸爸的需求不斷發(fā)生變化,我們不需要修改進行業(yè)務邏輯處理的Daddy類,只需要擴展子類。如果爸想喝茉莉花茶,我們就擴展一個JasmineTea類,增加新的子類不影響已存在類的多態(tài)性、繼承性,以及其他特性的運行和操作。
很顯然上述代碼體現(xiàn)了JAVA語言的多態(tài)性,軟件設計符合開閉原則。
3 結束語
大多數(shù)的程序設計都需要與閉合原則存在一定的一致性,開閉原則也是對不同模塊進行評價的基本依據(jù),我們可以利用開閉原則來判斷系統(tǒng)的擴展性與靈活性。
絕大部分的設計模式都必須符合開閉原則,在對每一個模式進行優(yōu)缺點評價時都會以開閉原則作為一個重要的評價依據(jù),以判斷基于該模式設計的系統(tǒng)是否具備良好的靈活性和可擴展性。
所以,我們在實際的軟件開發(fā)中,需要通過擴展來實現(xiàn)業(yè)務邏輯的變化,而不是修改。如果一個軟件系統(tǒng)符合開閉原則的,那么從軟件工程的角度來看,它至少具有這樣的好處:可擴展性好、可維護性好。
參考文獻;
[1] 薩默維爾.軟件工程[M]. 程成, 譯. 9版.北京: 機械工業(yè)出版社, 2011.
[2] Metsker S J.Java設計模式[M]. 2版.北京: 電子工業(yè)出版社, 2012.
[3] 趙亞娟. 計算機軟件JAVA編程特點及其技術研究[J]. 數(shù)字技術與應用, 2016(1): 113.
[4] 葛萌, 張琳娜, 陳偉. Java多態(tài)性機制應用研究[J]. 攀枝花學院學報, 2016(2): 25-28.
[5] 羅詩敏. 基于MVC的學生信息管理系統(tǒng)的分析與設計[D]. 廣州: 華南理工大學, 2014