軟體項目實訓及課程設計指導——如何合理地創建對象實例以降低程序類之間關係的耦合度
1、對象創建是面向對象OOP編程技術中不可缺少的一件事情
創建類的對象實例是在所有的面向對象OOP編程開發中軟體應用系統的開發人員所必須要面對的問題,軟體應用系統中的業務活動是由各個對象實例之間的相互交互而構成的。但頻繁地創建對象實例不僅會降低軟體應用系統的整體運行的性能,也增加了不必要的程序類之間的耦合關係。
如下示圖中的Employee類與其中的TimeCard類之間就存在有「一對多」 形式的關聯關係,因為在Employee類的對象實例中耦合有TimeCard類的對象實例。
因此,如何正確和合理地創建出類的對象實例?什麼時候應該創建類的對象實例?如何保證所創建出的類的對象實例能夠適時地被銷毀?
其實上面的核心問題也就是軟體應用系統的開發人員如何能夠更高效地創建、並且松藕合、達到程序模塊的可擴展性?繼續採用如下的對象實例的創建形式嗎?
OracleDAO oneOracleDAO=new OracleDAO();
2、合理地進行對象的創建以降低程序類之間關係的耦合度
兩個程序類之間如果存在或者出現有包括控制關係、調用關係、數據傳遞關係等情形時,這兩個程序類之間也就產生了耦合關係。而其中的調用關係是通過對目標對象實例中的對外的功能服務方法實現的(public方法),因此軟體應用系統的設計人員有必要合理地進行對象實例的創建以降低程序類之間關係的耦合度。
如下示圖中的Employee類與其中的Calculater類之間就存在有「依賴」關係,因為Calculater類以對象實例的方式參與到Employee類的功能方法calcSalary(Calculater strate)的參數中,也就是Employee類的功能方法calcSalary在執行時依賴於Calculater類的對象實例——Employee類與Calculater類之間就存在有「依賴」關係。
3、完善GRASP創建者設計模式的不足
通用職責分配軟體模式(GRASP)中的創建者設計模式只是指導軟體應用系統的設計人員決定創建對象的職責具體是由哪個目標程序類來承擔,但並沒有明確地告訴開發人員應該採用什麼形式和技術手段完成創建對象的具體工作。
作者在本文及後續相關的文章中試圖指導讀者在軟體應用系統的項目開發中如何合理地創建對象實例以降低程序類之間關係的耦合度,進一步提高對軟體應用系統中程序類之間關係的設計能力。
4、面向對象程式語言中的對象實例的傳統創建方式——利用new語句創建對象實例
(1)應用 new語句創建對象實例
在支持面向對象OOP編程技術的各種語言中(如Java語言)都提供有採用new語句並在程序類中提供公有(public)的構造方法實現對象實例創建的語法支持。比如,在Java程序設計語言的程序代碼中要創建出代表用戶信息的實體類UserInfoPO的對象實例時,可以採用下面的語句:
UserInfoPO oneUserInfoPO=new UserInfoPO();
使用new操作符創建對象實例一般需要三個基本的過程:首先為對象實例分配一定的內存空間,然後再調用對象所在類的構造方法,最後返回所創建出的對象實例的引用。
而且在Java程序設計語言中的對象只有實例化後,JVM虛擬機系統才真正地創建出它並為它分配出合適的內存空間;如果未實例化對象就直接使用它,則該對象將為空對象並拋出NullPointerException類型的異常。
在如下示圖所示的程序示例中,由於對StudentInfo類的對象只是定義了(StudentInfo oneStudent=null;),但並沒有對它進行對象實例化(oneStudent=new StudentInfo();)就直接使用StudentInfo類的對象oneStudent,從而產生出NullPointerException空指針類型的異常錯誤(參見如下示圖中的控制臺中的異常信息)。
當然,如果一個對象已經進行了對象實例化,但又採用了oneStudent=null;這樣的語句進行賦值,該對象實例已經被刪除後如果再繼續使用該對象,也同樣會產生出NullPointerException空指針類型的異常錯誤,讀者可以參見如下示圖中的程序代碼示例(注意其中黑體標識的語句)以及在控制臺中顯示的異常信息。
(2)了解對象的生命周期
Java程序設計語言中的對象生存期主要分為:創建、使用和銷毀(刪除)三個階段。當對象失除其作用域時,JVM虛擬機系統自動在後臺清除這些對象——如在某方法中創建了一個對象實例,當該方法返回時,也就無法再引用該對象實例。
當然,軟體應用系統的開發人員也可強行提前清除某一對象,這只需要將它置為null 或者調用該對象中的close()方法。比如,對於物理資料庫系統的資料庫連接對象、文件IO對象等在使用完畢後,應該及時地釋放該對象所佔用的系統資源、並清除該對象。
5、常規的應用new語句創建對象實例方式所存在的問題
常規採用new操作符語句進行對象實例創建的方式存在一定的問題,首先需要在對象創建的語句中直接指定目標類的名稱。一旦目標類名稱發生變化,就需要修改該new語句中的目標類名稱;其次在不同的方法中需要該對象實例時,都需要利用new語句對該對象進行創建。不僅產生了大量重複的對象實例創建的語句,而且也將對象實例創建的職責分散到不同的功能方法中。一旦對象實例的創建過程和要求發生變化時,就需要修改分散到不同的功能方法中的對象實例的創建語句,增加了代碼維護的工作量。
另外,有些程序類的對象實例是不應該重複地創建的——比如資料庫連接Connection對象等;其次,某些程序類只允許產生出單例的對象實例。對於對象實例創建時的這些基本的要求,採用普通的new操作符語句是做不到的。
因此,如何能夠更高效地創建對象實例、並且鬆耦合以達到程序模塊的可擴展性和可維護性?
6、利用工廠設計模式分離對象實例的創建邏輯和對象實例的使用邏輯
為了能夠降低程序類之間關係的耦合度,軟體應用系統的開發人員有必要合理和有效地創建對象——也就是需要分離對象實例的創建邏輯和對象的使用邏輯。對象的使用方只需要了解對象對外的功能方法的接口,而不需要自行負責對該對象的創建工作。封裝對象的創建過程和邏輯、並將對象的創建職責由某個特定的功能類(如工廠類)承擔——這是對面向對象OOP設計方法中的「單一職責設計原則」的具體應用。
一旦能夠分離對象實例的創建邏輯和對象實例的使用邏輯,也就能夠降低由於對象實例的創建而產生的程序類之間關係的耦合度。當被創建的目標對象所在的程序類的代碼發生變化時,不需要修改分散到不同功能方法中的對象創建的new操作符語句,而只需要集中修改承擔對象實例創建職責的工廠類中的對象實例的創建方法。
下圖所示的黑體部分所標識的代碼,表示在示例項目銀行帳戶信息管理系統中,利用資料庫連接對象的工廠類ConnectDBFactory中的newConnectDBBean方法創建出對應的對象實例的程序代碼。
如下程序為資料庫連接對象的工廠類ConnectDBFactory類的完整程序代碼示例,在ConnectDBFactory工廠類中應用了觀察者設計模式。其中的工廠方法newConnectDBBean接收需要創建對象的類名稱參數,並返回目標對象所在的接口類型的對象實例。
package com.px1987.webbank.factory;
import com.px1987.webbank.config.ClassNameConfig;
import com.px1987.webbank.dao.*;
import com.px1987.webbank.dao.inter.ConnectDBInterface;
import com.px1987.webbank.exception.*;
import java.util.Observable;
import java.util.logging.*;
public class ConnectDBFactory extends Observable {
public ConnectDBInterface newConnectDBBean(String connectDBBeanClassName) throws WebBankException {
ConnectDBInterface connectDBBean=null;
Class oneClass=null;
try {
oneClass = Class.forName(connectDBBeanClassName);
}
catch (ClassNotFoundException e){
int logImpleKind=Integer.parseInt(ClassNameConfig.getProperty("logImpleKind"));
this.addObserver(LogInfoFactory.newLogInstance(logImpleKind));
this.setChanged(); //注意要設置變化點
notifyObservers(e); //當出現異常時將通知各個觀察者
throw new WebBankException("不能正確地獲得"+connectDBBeanClassName+"類");
}
try {
connectDBBean = (ConnectDBInterface)oneClass.newInstance();
}
catch (InstantiationException e){
int logImpleKind=Integer.parseInt(ClassNameConfig.getProperty("logImpleKind"));
this.addObserver(LogInfoFactory.newLogInstance(logImpleKind));
this.setChanged(); //注意要設置變化點
notifyObservers(e); //當出現異常時將通知各個觀察者
throw new WebBankException("不能正確地創建"+connectDBBeanClassName+"類的對象實例");
}
catch (IllegalAccessException e) {
int logImpleKind=Integer.parseInt(ClassNameConfig.getProperty("logImpleKind"));
this.addObserver(LogInfoFactory.newLogInstance(logImpleKind));
this.setChanged(); //注意要設置變化點
notifyObservers(e); //當出現異常時將通知各個觀察者
throw new WebBankException("不能正確地創建"+connectDBBeanClassName+"類的對象實例");
}
return connectDBBean;
}
}
如何正確地應用Web MVC架構模式分離表示層和模型處理層耦合關係
軟體項目實訓及課程設計指導—Web表示層典型功能實現的應用實例
項目實訓及課程設計——如何合理地設計軟體應用系統的Web表示層
如何合理地設計軟體應用系統中數據訪問服務層內的各個功能程序類
如何應用數據訪問服務層分離系統中的業務層和持久層之間耦合關係