摘要:在分析了構件間各種操作的基礎上,將軟件體系結構的概念引入現有的構件模型,用于對復合構件進行描述,從而改進了現有的構件模型,給出了一種基于軟件體系結構的可復用構件模型,并給出了該模型的應用。關鍵詞:軟件復用;軟件構件;軟件體系結構;構件模型
中圖分類號:TP311文獻標志碼:A
文章編號:1001-3695(2008)01-0120-03
復用是成熟工程領域的一個基本特征,傳統工業以及計算機硬件產業均是很好的例子。這些工業化發展的特點都是工業生產由符合標準的零部件的生產和基于標準零部件的產品生產兩方面組成。其中,符合標準的零部件即為構件,而基于構件的產品生產即為構件的組裝。實踐表明,這種模式是產業工程化、工業化的成功之路,軟件產業要發展并形成規模經濟,也應該遵循工業化生產的這種模式,而該模式的關鍵就是構件的生產和復用[1]。也就是說,軟件復用的核心和基礎即為軟件構件[2]。
隨著軟件系統的規模和復雜度的不斷增大,為了提高軟件的可復用性和可維護性,軟件開發所關注的主要問題已不再是算法和數據結構,而是軟件系統的總體結構和組織,即軟件體系結構[3]。對于軟件體系結構,許多專家和學者從不同的角度和側面對其進行了刻畫。其中美國卡內基·梅隆大學的D. Garlan和M. Shaw給出了一個廣泛能接受的定義:軟件體系結構是軟件設計過程中的一個層次,處理總體系統結構設計和描述方面的一些問題,包括對系統組成元素的描述,這些元素的交互,指導這些元素組成的模式以及模式的約束[4]。軟件體系結構通過計算元素和這些元素間的交互來定義一個軟件系統,而這些計算元素即為軟件構件。所以,軟件構件也是軟件體系結構的核心元素。
1構件的概念
軟件復用包括兩個基本的開發活動,即面向復用的開發(development for reuse)和基于復用的開發(development with reuse)。前者為生產可復用軟件構件的過程;后者是通過可復用軟件構件的組裝來生產新的軟件系統的過程[5]。而軟件體系結構也通過描述構件和構件間的交互來描述一個軟件系統。所以,軟件構件既是軟件復用的基本元素,也是軟件體系結構的核心元素。無論研究軟件復用,還是軟件體系結構,軟件構件都是必須要研究的一個重要概念。
構件的概念和面向對象中的對象概念有很多類似之處。對象通過將數據和在其上執行的操作進行封裝而達到了一定程度上的復用,但對象在復用的過程中存在很多問題:
a)由于對象對數據和操作進行了封裝,要對其進行復用,就必須了解該對象的實現細節。這給對象的復用造成了很大的障礙。
b)對象之間的集成是通過消息通信。在這種集成方式中,對象之間的關系分散并固定在對象的實現中,對象的組裝缺乏靈活性。
c)由于對象繼承關系和行為的重疊,使得對象的替代性較差。用一個新的對象替代原有對象時可能會影響所有與其有繼承關系的對象。
在計算機硬件產業領域,任何廠商生產的零部件(構件),只要遵循標準的接口規范,均可以方便地與其他廠商生產的零部件進行組裝并集成到系統中,而無須了解零部件的內部結構。類似地,在軟件系統中要對軟件構件進行大規模的復用,就必須為軟件構件制定某種規范,而這些規范可以由軟件接口提供。所以,對象是封裝了數據和操作的軟件模塊,而構件是滿足某種規范并提供特定功能的軟件模塊。面向對象作為一種主流編程技術,對象可以作為實現軟件構件的一種途徑。
2構件模型
2.1構件模型的引入
構件模型是面向構件和基于構件的軟件開發方法的核心,是構件的本質特征及構件間關系的抽象描述[6]。由于構件通常由規約和實現兩部分組成,構件規約是在復用時對外可見的部分,是構件必須遵循的接口規范,而構件實現是構件的內部實現細節,是對外不可見的部分。一般構件模型均包括對構件規約和構件實現兩方面的描述,即構件::=構件規約,構件實現[5]。
2.2構件間操作
基于構件的軟件開發是通過將不同的構件進行組裝來形成新的軟件系統的過程,組裝過程是通過構件間的連接操作來實現的。構件間的連接操作多種多樣,如Ian Sommerville提出的順序連接(sequential composition)、層次連接(hierarchical composition)和加成連接(additive composition)[7],以及何積豐教授提出的鏈狀連接(chain of components)和平行連接(disjoint parallel)[8]等。
這里將構件間的連接操作分為以下兩種連接:
a)串行連接。設A,B是兩個構件,若構件A所需的外部接口IRA和構件B供外部使用的接口IPB滿足IRA ∩IPB ≠,則構件A可以通過使用構件B提供的接口來與其進行連接,筆者將這樣的連接操作叫做構件B串行連接到構件A上,記做AB。
b)并行連接。設A,B是兩個構件,若構件A和構件B同時串行連接到構件C上,即有CA,CB,則構件A和構件B同時并行地為構件C提供服務,把這樣的連接操作叫做構件A和構件B的并行連接,記做A⊕B。
由于構件間連接操作的引入,可以將構件分為原子構件和復合構件。原子構件(atomic component)即進行連接操作的基本單元;復合構件(complex component)由原子構件通過連接操作組合而成。原子構件和復合構件均屬于構件的范疇。于是,可以將構件集合C和在C上的運算即連接操作所組成的系統定義為一個代數系統,記做C,,⊕。由于原子構件和復合構件均屬于構件范疇,即對任意C1,C2∈C有C1C2∈C,C1⊕C2∈C。構件的連接操作在構件集合C上是封閉的。
2.3復合構件模型
復合構件是由原子構件通過連接操作組合而成,其構件實現相對比較復雜,而現有的構件模型對復合構件的描述均相對不足。為了更好地對復合構件的實現進行描述,引入軟件體系結構概念,將每一個復合構件均看做是一個小型的軟件系統,構件::=構件規約,構件實現,從而可以從軟件體系結構的角度對其進行描述,用軟件體系結構描述復合構件的構件實現部分,這樣可以比較系統、有效地對復合構件進行描述。于是對于復合構件,將其表示為
complex component=specification,interface mapping,component SA
Specification為構件規約,是對外可見的部分,由構件接口描述,包括構件提供的接口和所需的接口兩個方面。Interface mapping為接口映射,表示復合構件的構件規范即接口定義向組成該復合構件的成員構件的接口定義的映射。Component SA即為復合構件的軟件體系結構描述,表示該復合構件中的成員構件及成員構件間的交互。
根據D. Garlan和M. Shaw對軟件體系結構的定義,筆者將復合構件的軟件體系結構模型定義為
component SA=components,connectors,composition
其中:連接件(connector)可以被看做是一種特殊的構件,它可以使接口不匹配的兩個構件進行連接操作。對于串行和并行兩種不同的連接操作,分別定義為兩種不同的連接件:
在復合構件的軟件體系結構模型中,components為組成該復合構件的成員構件集合;connectors為組成該復合構件的連接件集合;composition為各個構件的連接方式描述。
綜上所述,在現有構件模型的基礎上,筆者通過引入軟件體系結構概念描述復合構件,從而可以將構件模型描述為
component=specification,implementation
specification=IP,IR
component=atomic component|complex component
atomic component=specification,implementation reference
complex component=specification,interface mapping,component SA
component SA=components,connectors,composition
其中:specification為構件規約部分;implementation為構件實現部分;IP為構件對外提供的接口;IR為構件所需的外部接口;atomic component為原子構件;implementation reference為對構件具體實現的引用;complex component為復合構件。
3構件模型的應用
3.1構件接口的實現
根據構件模型,構件由構件規約和構件實現兩部分組成,構件規約由構件接口描述,而構件接口包括構件對外提供的接口和構件所需的外部接口。傳統的面向對象的接口技術對構件對外提供的接口部分提供了很好的支持,但構件所需的外部接口部分卻隱藏在實現細節中,難以根據接口處的信息定義構件的集成,也不符合本文對構件的定義。
為了解決構件所需外部接口對外不可見的問題,引入依賴注入技術。依賴注入技術起源于輕量級構件容器提供的控制反轉機制(inversion of control),即由容器定位插件的具體實現。依賴注入技術用部署描述文件描述構件之間的依賴關系,在運行時由容器按部署描述文件動態地將被調用構件注入到調用構件之中[9]。所以,可以利用依賴注入技術在部署描述文件中對構件所需外部接口進行描述,使其對外可見并與構件實現細節分離。
3.2構件模型在構件開發中的應用
在研究構件模型的基礎上,采用spring框架提供的輕量級容器,利用J2EE技術實現了一個OA系統[10]。下面以其中的用戶登錄模塊為例,介紹構件模型在面向構件和基于構件開發中的應用。
可以把整個用戶登錄模塊視為一個復合構件,該復合構件的軟件體系結構滿足層次體系結構風格[11],將該復合構件分為頁面層、控制層、業務層和數據訪問層,每一層均為其上一層提供服務并作為其下一層的客戶端。頁面層包括原子構件LoginPage,控制層包括原子構件LoginController,業務層包括原子構件UserOP和SeConfigOP,數據訪問層包括原子構件UserDAO和SeConfigDAO。于是可以用復合構件模型對用戶登錄模塊進行描述。其中一部分描述如下:
Component LoginModule
Specification
IP
IP1:function 基于瀏覽器的用戶登錄功能
IR
IR1:method MD5Encode(String) return String
IR2:object setDataSource(DataSource)
IR3:object setSuccessView(String)
…
Implementation
Interface Mapping
IP1→LoginPage
IR1→LoginController
IR2→UserDAO,SeConfigDAO
IR3→LoginController
…
Component SA
Components
LoginPage,UserOP,SeConfigOP,UserDAO,SeConfigDAO
Connectors
LoginController
Composition
LoginPage((UserOPUserDAO)⊕(SeConfigOPSeConfig DAO)) via LoginController
再用構件模型對其中的原子構件進行描述。其中原子構件UserDAO描述如下:
Component UserDAO
Specification
IP
IP1:method queryByUserID(String) return Object
IP2:method userUpdate(Object) return boolean
IR
IR1:object setDataSource(DataSource)
Implementation
Implementation Reference
cn.edu.ssct.oa.loginmodule.dao.UserJdbcDAO
這樣,用戶登錄模塊中的每個原子構件均為其他原子構件提供某些方法(method)或對象(object),而用戶登錄復合構件對外提供一個完整的用戶登錄功能(function)。
3.3構件模型的實現
下面基于spring框架給出上面描述的用戶登錄模塊的實現。其中每一個原子構件均要實現其對外提供的接口并利用IoC模式在配置描述文件中描述其所需的外部接口。以原子構件UserDAO為例,UserDAO的接口如下:
public interface IUserDAO{
Object queryByUserID(String userID);
Boolean userUpdate(Object userObj);
}
接口實現如下:
public class UserJdbcDAO implements IUserDAO{
private DataSource dataSource;
public void setDataSource(DataSource dataSource){
this.dataSource=dataSource;
}
public Object queryByUserID(String userID) {
…
}
public Boolean userUpdate(Object userObj){
…
}
}
在配置描述文件中描述如下:
〈bean id=\"userDAO\" class=\"cn.edu.ssct.oa.loginmodule.dao.UserJdbcDAO\"〉
〈/bean〉
其中:原子構件UserDAO所需的外部接口,即構件dataSource對外提供的接口,可以在需要時利用IoC模式由容器插入到UserDAO中。
在原子構件均已得到實現的條件下,同樣可以利用IoC模式對原子構件進行組裝,組裝過程可以在配置描述文件中進行描述如下:
〈bean id=\"urlMapping\"
class=\"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping\"〉
〈property name=\"mappings\"〉
〈props〉
〈prop key=\"/login.spring\"〉loginController〈/prop〉
〈/props〉
〈/property〉
〈/bean〉
〈bean id=\"loginController\"
class=\"cn.edu.ssct.oa.loginmodule.controller.LoginController〉
〈property name=\"userOP\"〉〈ref bean=\"userOP\" /〉〈/property〉
〈property name=\"seConfigOP\"〉〈ref bean=\"seConfigOP\" /〉〈pro perty〉
〈/bean〉
〈bean id=\"userOP\" class=\"cn.edu.ssct.oa.loginmodule.business.UserOP〉
〈property name=\"userDAO\"〉〈ref bean=\"userDAO\" /〉〈/property〉
〈/bean〉
〈bean id=\"seConfigOP\" class=\"cn.edu.ssct.oa.loginmodule.business.SeConfigOP\"〉
〈property name=\"seConfigDAO\"〉〈ref bean=\"seConfigDAO\" /〉〈/property〉
〈/bean〉
〈bean id=\"userDAO\" class=\"cn.edu.ssct.oa.loginmodule.dao.UserJdbcDAO\"〉
〈/bean〉
〈bean id=\"seConfigDAO\" class=\"cn.edu.ssct.oa.loginmodule.dao.SeConfigJdbcDAO\"〉
〈/bean〉
這樣,復合構件LoginModule就組裝好了,只要LoginMo dule所需的外部接口得到提供,復合構件LoginModule的功能就可以利用spring框架得以實現。
4結束語
將軟件體系結構引入構件模型,可以很好地描述構件及構件間交互,并對無論是自頂向下還是自底向上的軟件工程方法均提供了很好的支持。對于自頂向下的方法,可以將整個軟件系統看做是一個復合構件,該復合構件又由其他的復合構件和原子構件組裝而成。這樣,就可以將軟件系統逐步分解,遞歸地對其進行描述。對于自底向上的方法,首先對所有的原子構件進行描述,再在軟件體系結構的層次上分別對它們進行組裝,最后構成一個完整的軟件系統。
運用面向對象的接口技術,IoC模式等技術,較好地實現了提出的構件模型。構件可以在接口處進行集成,并在其內部實現細節不被了解的條件下得到復用。這樣,大大提高了構件的可復用程度,從而為軟件系統搭建了一個靈活、可擴展的體系結構。
參考文獻:
[1]楊芙清,梅宏,李克勤.軟件復用與軟件構件技術[J].電子學報,1999,27(2):68-75.
[2]梅宏. 軟件復用技術研究與應用[J].科技與經濟,2002,15:39-49.
[3]ALLEN R J.A formal approach to software architecture[D]. Pittsburghers:Carnegie Mellon University,1997.
[4]GARLAN D,SHAW M. An introduction to software architecture:advances in software engineering and knowledge engineering[M].New York:World Scientific Press,1993.
[5]張世琨,張文娟,常欣,等.基于軟件體系結構的可復用構件制作和組裝[J]. 軟件學報,2001,12(9):1351 1359.
[6]楊芙清,王千祥,梅宏,等.基于復用的軟件生產技術[J].中國科學E輯,2001,31(4):363-371.
[7]SOMMERVILLE I.Software engineering[M]. 7th ed. 北京:機械工業出版社,2004.
[8]HE Ji feng,LIU Zhi ming,LI Xiao shan.Component calculus[R].Macau:The United Nations University, 2003.
[9]FOWLER M.Inversion of control:containers and the dependency injection pattern[EB/OL]. (2004).http://www.martinfowler.com/.
[10]JOHNSON R,HOELLER J,ARENDSEN A.Spring:java/j2ee Application Framework[EB/OL].(2004).http://www.springframework.org/.
[11]GARLAN D,SHAW M.Software architecture:perspectives on an emerging discipline[M].Englewood Cliffs,NJ:Prentice Hall,1996.
“本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文”