朱紹宏,覃章榮
(廣西師范大學(xué) 計(jì)算機(jī)科學(xué)與工程學(xué)院,廣西 桂林 541004)
Web Service[1-4]既是一類基于B/S架構(gòu)的應(yīng)用程序,也是為實(shí)現(xiàn)團(tuán)隊(duì)內(nèi)部的流程化管理和高效地完成日常決策任務(wù),而專門創(chuàng)建的一套完整的解決方案。由于它需要滿足不同部門和人員的業(yè)務(wù)需求,導(dǎo)致在軟件開發(fā)過程中,用戶需求會(huì)頻繁地發(fā)生變動(dòng)。傳統(tǒng)的軟件開發(fā)模式缺乏靈活性,不能適應(yīng)用戶需求的快速變化,無法有效地用于Web Service軟件的開發(fā)。如何高效地開發(fā)Web Service程序成為目前業(yè)界亟需解決的問題。
在軟件開發(fā)實(shí)踐中,常規(guī)的解決方案一方面是提供簡單易用的軟件開發(fā)工具,幫助開發(fā)者組織資源和減少失誤,如Eclipse、IntelliJ IDEA等集成開發(fā)環(huán)境,經(jīng)過多個(gè)版本更新和技術(shù)迭代已經(jīng)成為開發(fā)者的必備工具;另一方面是提供通用代碼開發(fā)框架,簡化軟件開發(fā)中的模塊集成、屬性配置等基礎(chǔ)工作,如Spring、J2EE框架已經(jīng)被廣泛應(yīng)用在企業(yè)應(yīng)用的開發(fā)過程中。然而,對(duì)于Web Service這類應(yīng)用程序,由于其業(yè)務(wù)規(guī)則復(fù)雜多變,開發(fā)人員為生成相應(yīng)軟件,仍然需要耗費(fèi)大量時(shí)間和精力編寫復(fù)雜、臃腫的業(yè)務(wù)邏輯代碼,這為今后程序的部署、維護(hù)和升級(jí)帶來了高成本問題。
為了有效解決上述問題,目前軟件開發(fā)人員主要使用2類方法以達(dá)到提升軟件開發(fā)效率和目標(biāo)軟件質(zhì)量的目的。第1類主要是針對(duì)于解決軟件開發(fā)過程中存在大量冗余代碼的問題,通過實(shí)現(xiàn)代碼重用,降低手工編碼的工作量,以提升軟件開發(fā)效率。如Mcmillan等[5]通過從開放源代碼庫中挖掘特征描述和源代碼,實(shí)現(xiàn)代碼重用,針對(duì)軟件項(xiàng)目中存在大量通用的功能模塊,通過代碼重用來有效減少通用模塊的編碼量,提升軟件的開發(fā)效率。Perez De Rosso等[6]提出一種用于開發(fā)Web應(yīng)用程序的方法,該方法將獨(dú)立的、可重用的功能抽象成“概念”,然后由開發(fā)人員從“概念”存儲(chǔ)庫中提取“概念”,通過配置和組合“概念”來構(gòu)建應(yīng)用程序。第2類則是為了在需求分析階段準(zhǔn)確地了解用戶需求,減少需求變更引發(fā)項(xiàng)目返工問題而提出的基于模型的需求驗(yàn)證方法[7-10],通過將用戶非形式的需求表述構(gòu)建成抽象的需求模型,使開發(fā)人員能夠充分理解用戶需求,減少需求錯(cuò)誤,保障軟件開發(fā)過程的順利開展。在實(shí)際應(yīng)用中,由于對(duì)用戶的需求進(jìn)行建模存在較高成本,并且在需求足夠清晰之后,模型將會(huì)被廢棄,這導(dǎo)致建模的投入成本很難取得與之相匹配的回報(bào)。
近年來,基于低代碼開發(fā)范式的方法逐漸受到研究者的青睞。其作為一種可視化軟件開發(fā)方法,通過提供圖形界面的可視化編程,實(shí)現(xiàn)將抽象化的模型直接映射為目標(biāo)軟件的實(shí)現(xiàn)代碼,有效解決建模投入與回報(bào)不成正比的問題[11-12]。因此,許多研究人員基于這種方法做了大量研究與探索。李雅雯[13]提出一種將IFML模型轉(zhuǎn)換為HTML和CSS實(shí)現(xiàn)代碼的低代碼方法,可用于解決移動(dòng)應(yīng)用開發(fā)中難以跨平臺(tái)的問題。羅異[14]提出一種用于Web程序生成的低代碼方法,可用于自動(dòng)生成Web程序的表現(xiàn)層代碼。Viswanathan等[15]提出一種使用活動(dòng)圖和順序圖對(duì)工作流程建模,并將模型轉(zhuǎn)換成軟件原型的低代碼方法,然而生成的原型由面向過程的代碼組成,不適用于Web Service程序的開發(fā)。Forward[16]等提出一種基于類模型(概念類圖)和狀態(tài)機(jī)模型生成應(yīng)用程序的低代碼方法,但是使用該方法生成實(shí)際可用的應(yīng)用程序,仍然需要開發(fā)人員手工編寫大量代碼。Chen等[17]提出一種將自定義XML類型的數(shù)據(jù)模型轉(zhuǎn)換成應(yīng)用程序的低代碼方法,然而該方法生成的應(yīng)用程序只能實(shí)現(xiàn)簡單的增加、刪除、修改與查找功能。Kundu等[18]提出一種從UML序列圖自動(dòng)生成一般應(yīng)用程序代碼的方法,首先以序列圖來構(gòu)造一個(gè)可視化模型,然后通過設(shè)計(jì)映射規(guī)則將圖中的模型元素映射為代碼,由于序列圖一般不會(huì)包含代碼層面的信息(如變量的定義、修改、讀取、初始化變量以及異常處理等),因此,該方法只能用于生成代碼框架。王詩宇等[19]提出一種針對(duì)移動(dòng)用戶界面開發(fā)的低代碼方法,通過定義模型轉(zhuǎn)換的映射規(guī)則,實(shí)現(xiàn)將MUICM元素轉(zhuǎn)換為Android平臺(tái)的用戶界面代碼。Yang等[20-21]為了提升目標(biāo)軟件的質(zhì)量,針對(duì)需求分析階段容易出現(xiàn)需求錯(cuò)誤的問題,提出一種低代碼開發(fā)方法RM2PT。該方法通過定義用于模型轉(zhuǎn)化的轉(zhuǎn)換規(guī)則和轉(zhuǎn)換算法,實(shí)現(xiàn)將統(tǒng)一建模語言[22](UML)符號(hào)和對(duì)象約束語言[23](OCL)表達(dá)式構(gòu)建的需求模型轉(zhuǎn)化成軟件原型,能夠快速驗(yàn)證用戶需求,減少因需求錯(cuò)誤造成的不必要損失。然而,由于原型無法實(shí)現(xiàn)數(shù)據(jù)持久化,且不是標(biāo)準(zhǔn)Web Service程序,所以目前還難以進(jìn)行實(shí)際部署與應(yīng)用。
上述工作為推動(dòng)軟件的高效開發(fā)做出了大量貢獻(xiàn),然而,就筆者調(diào)查所知,目前還沒發(fā)現(xiàn)采用低代碼方法自動(dòng)生成標(biāo)準(zhǔn)化Web Service程序的相關(guān)文獻(xiàn)報(bào)道,因此,基于RM2PT的研究工作,本文提出一種直接將需求模型自動(dòng)轉(zhuǎn)換為Web Service程序的低代碼開發(fā)方法。該方法建立OCL表達(dá)式與數(shù)據(jù)基本操作的之間的轉(zhuǎn)換規(guī)則,結(jié)合創(chuàng)建的轉(zhuǎn)換模板和轉(zhuǎn)換算法自動(dòng)生成數(shù)據(jù)可持久化的、標(biāo)準(zhǔn)化的Web Service應(yīng)用程序,從而可有效提高Web Service程序的開發(fā)效率與質(zhì)量。
REST[24]是一種互聯(lián)網(wǎng)架構(gòu)風(fēng)格,它使用HTTP協(xié)議,并將網(wǎng)絡(luò)上的所有內(nèi)容視為資源。同一資源具有多種表現(xiàn)形式,最常見的形式是JSON[25],并且每個(gè)資源都有一個(gè)唯一的標(biāo)識(shí),通過這種唯一標(biāo)識(shí),可以輕松地對(duì)資源進(jìn)行操作?;赗EST的體系結(jié)構(gòu)設(shè)計(jì),通常使用HTTP的4種方法來表示資源的CRUD(創(chuàng)建、檢索、更新和刪除)操作:
1) GET:訪問資源。
2) POST:創(chuàng)建資源。
3) DELETE:刪除資源。
4) PUT:更新資源。
基于REST體系結(jié)構(gòu)的Web Service稱為RESTFul Web Service[26],其結(jié)構(gòu)簡單,使前端和后端實(shí)現(xiàn)解耦,促進(jìn)了獨(dú)立開發(fā),也易于維護(hù)和測(cè)試,在業(yè)界得到普遍應(yīng)用。
在繪制系統(tǒng)順序圖(SSD)時(shí),通常會(huì)顯示出其中的系統(tǒng)事件,即設(shè)計(jì)系統(tǒng)的事件或I/O消息,而輸入的系統(tǒng)事件則表示系統(tǒng)具有用于處理該事件的系統(tǒng)操作[27]。在本文中,業(yè)務(wù)邏輯的實(shí)現(xiàn)代碼被封裝在系統(tǒng)操作中,對(duì)外表現(xiàn)為軟件系統(tǒng)在公共接口中提供的操作,用于對(duì)輸入的系統(tǒng)事件做出處理,并對(duì)外輸出處理結(jié)果。
需求模型是使用RM2PT工具建模之后得到的模型,屬于UML的受限子集,包含:
1) 概念類圖:類圖使用類和對(duì)象描述目標(biāo)系統(tǒng)的結(jié)構(gòu),而概念類圖是通過刪除類圖中的方法所形成一種變體結(jié)構(gòu)。
2) 操作契約(contract):是使用OCL表達(dá)式定義的描述功能需求的一種敘述規(guī)范,用來約束UML模型,其包含如下4個(gè)部分:
(i) Signature:定義了系統(tǒng)操作名稱、輸入?yún)?shù)、系統(tǒng)操作的返回類型。
(ii) Definitions:為減少冗余,提取前置條件和后置條件的公共部分作為共享規(guī)范,用于變量的復(fù)用。
(iii) Precondition:定義了系統(tǒng)執(zhí)行前應(yīng)該滿足的前置條件。
(iv) Postcondition:指定了系統(tǒng)執(zhí)行之后應(yīng)該滿足的后置條件。
3) 系統(tǒng)順序圖:呈現(xiàn)了系統(tǒng)事件在一定時(shí)間內(nèi)的發(fā)生次序。
4) 用例圖:以可視化的方式呈現(xiàn)參與者與目標(biāo)系統(tǒng)之間的交互行為。
關(guān)注點(diǎn)分離的原則認(rèn)為:軟件系統(tǒng)必須分解為功能重疊盡可能少的部分[28],即程序不要編寫為一個(gè)整體,而是將代碼分解成多個(gè)模塊,這些模塊是最終確定的系統(tǒng)小塊,每個(gè)小塊都能夠完成一個(gè)簡單的工作。按照此原則,分層體系結(jié)構(gòu)模式成了一種可靠的通用模式,它使Web Service應(yīng)用具有高內(nèi)聚,低耦合,易于實(shí)現(xiàn)、測(cè)試和維護(hù)的特點(diǎn)。此外,考慮到當(dāng)前業(yè)界主流的開發(fā)架構(gòu)在企業(yè)開發(fā)中的受歡迎程度,本文選擇Spring Boot軟件架構(gòu)來生成Web Service程序。根據(jù)該架構(gòu),Web Service程序可分離出如圖1所示的系統(tǒng)操作(system operation)、實(shí)體倉庫(entity repository)、實(shí)體類(entity class)以及視圖(view)4種核心功能模塊。系統(tǒng)操作是應(yīng)用程序的核心模塊,包含業(yè)務(wù)邏輯的具體實(shí)現(xiàn);實(shí)體倉庫充當(dāng)業(yè)務(wù)層和關(guān)系型數(shù)據(jù)庫的交互媒介;實(shí)體類是數(shù)據(jù)交互的載體,用于存儲(chǔ)程序內(nèi)部的信息,應(yīng)用程序也會(huì)將實(shí)體類映射為數(shù)據(jù)庫中的數(shù)據(jù)表;視圖用于渲染圖形用戶界面(GUI)。由于entity類和view的生成過程與文獻(xiàn)[21]中的過程相似,因此本文重點(diǎn)是將需求模型轉(zhuǎn)換為實(shí)體倉庫和系統(tǒng)操作。
圖1 Web Service核心功能模塊Fig. 1 Main functional modules of Web Service
低代碼(low-code)開發(fā)方法使用模型作為業(yè)務(wù)需求的形式化描述,通過定義的處理邏輯將業(yè)務(wù)模型轉(zhuǎn)換為可以運(yùn)行的軟件應(yīng)用程序。因此,本文以Low-Code開發(fā)理念為指導(dǎo),使用RM2PT工具構(gòu)建的需求模型作為本文方法的轉(zhuǎn)換基礎(chǔ),通過定義用于代碼生成的轉(zhuǎn)換規(guī)則、轉(zhuǎn)換模板和轉(zhuǎn)換算法,實(shí)現(xiàn)直接將需求模型自動(dòng)轉(zhuǎn)換為Web Service程序。圖2顯示了本文方法的工作原理,方法包含2種角色——設(shè)計(jì)者與用戶。設(shè)計(jì)者指代本文方法的提出者,而用戶則指代對(duì)用戶的需求進(jìn)行建模的需求分析師,其建模得到的需求模型作為本文方法的輸入。
圖2 自動(dòng)生成方法的工作原理Fig. 2 Overview of automatic generation approach
在當(dāng)前的軟件開發(fā)過程中,編程人員的主要任務(wù)是編寫目標(biāo)軟件的業(yè)務(wù)邏輯,業(yè)務(wù)邏輯中的查找對(duì)象、設(shè)置引用、創(chuàng)建和刪除對(duì)象以及更新對(duì)象,符合眾所周知的CRUD(創(chuàng)建、檢索、更新和刪除)操作,而OCL具有豐富的描述系統(tǒng)狀態(tài)的語法機(jī)制,可用于描述程序中的業(yè)務(wù)邏輯狀態(tài)。因此創(chuàng)建轉(zhuǎn)換規(guī)則的目的是建立OCL表達(dá)式與數(shù)據(jù)基本操作之間的映射關(guān)系。轉(zhuǎn)換模板用于限定生成代碼的格式,使代碼符合標(biāo)準(zhǔn)的編碼規(guī)范,從而提高生成代碼的質(zhì)量。轉(zhuǎn)換模板通過提取分層后的功能模塊的代碼特征構(gòu)建而成,因此,圖1中的4種功能模塊都需要構(gòu)建轉(zhuǎn)換模板。轉(zhuǎn)換算法是本文用來處理和解析需求模型以及用來生成Web Service程序的一種偽代碼描述,后面的章節(jié)將詳細(xì)介紹轉(zhuǎn)換規(guī)則、轉(zhuǎn)換模板以及轉(zhuǎn)換算法的實(shí)現(xiàn)細(xì)節(jié)。
圖3是應(yīng)用程序的數(shù)據(jù)操作示意圖,其中,數(shù)據(jù)訪問層和數(shù)據(jù)庫經(jīng)常受到技術(shù)更新?lián)Q代影響,容易產(chǎn)生較大的變動(dòng)。而GRASP受保護(hù)變量原則認(rèn)為:預(yù)先找出不穩(wěn)定的變化點(diǎn),使用統(tǒng)一的接口封裝起來,當(dāng)未來發(fā)生變化的時(shí)候,可以通過接口擴(kuò)展新的功能,而不需要修改原有的實(shí)現(xiàn)[29]。為了解決這個(gè)問題,本文引入了JPA[30]中定義的數(shù)據(jù)基本操作來降低底層技術(shù)更新對(duì)程序的影響。此外,目前Web Service程序最常見的操作是從數(shù)據(jù)庫中獲取和顯示數(shù)據(jù)[31],而RM2PT中定義的26條轉(zhuǎn)換規(guī)則無法生成這些操作。為了生成Web Service程序,需要在這26條轉(zhuǎn)換規(guī)則的基礎(chǔ)之上,創(chuàng)建生成數(shù)據(jù)操作的轉(zhuǎn)換規(guī)則。因此,本文通過建立OCL表達(dá)式與JPA基本操作之間的映射關(guān)系,形成了如表1所示的10條新的轉(zhuǎn)換規(guī)則。
圖3 應(yīng)用程序數(shù)據(jù)操作示意圖Fig. 3 Illustration of application data operations
表1 轉(zhuǎn)換規(guī)則Tab. 1 Transformation rules
(1)save:將執(zhí)行操作后產(chǎn)生的對(duì)象ob保存到ClassName類的對(duì)象集合中。
(2)update:在對(duì)象屬性被賦值之后,需要將對(duì)象修改后的屬性信息更新到系統(tǒng)中,因此類名為ClassName的對(duì)象ob在執(zhí)行賦值操作之后將會(huì)被更新到系統(tǒng)中。
(3)delete:從系統(tǒng)中ClassName類的對(duì)象集合中,刪除對(duì)象實(shí)例ob,使系統(tǒng)符合后置條件。
(4)findAll:在系統(tǒng)中查找ClassName類的所有對(duì)象。
(5)findById:在ClassName類的所有實(shí)例中,通過使用OCL關(guān)鍵詞any,以查詢條件Id查找對(duì)象ob。
(6)findByLike:依據(jù)對(duì)象的屬性值查找系統(tǒng)中具有相似屬性值的所有對(duì)象。
(7)findByAssociation:從對(duì)象ob的關(guān)聯(lián)對(duì)象中查找所有符合約束條件o的單個(gè)對(duì)象ob。
(8)findByAttribute:從ClassName類的所有對(duì)象中,通過使用OCL關(guān)鍵詞any和約束條件condition(o)來查找單個(gè)對(duì)象ob。
(9)findByAssociation:從對(duì)象ob的所有關(guān)聯(lián)對(duì)象中查找符合約束條件o的所有對(duì)象實(shí)例。
(10)findbyAttribute:通過約束條件o以及OCL關(guān)鍵詞select在系統(tǒng)中查詢ClassName類的所有對(duì)象obs。
在上述轉(zhuǎn)換規(guī)則中,基本操作中的檢索操作,除了findAll以外,都可以通過任意組合形成更復(fù)雜的查詢操作。例如,OCL表達(dá)式的語義描述為:根據(jù)屬性name和關(guān)聯(lián)對(duì)象teacher查找系統(tǒng)中存在的學(xué)生類對(duì)象,那么其基本操作可以組合為findByNameAndTeacher,其返回值由OCL關(guān)鍵字決定。通過這種方式,數(shù)據(jù)查詢能夠涵蓋軟件開發(fā)中大多數(shù)常見的數(shù)據(jù)訪問操作。
Xtend[32]是一種靜態(tài)類型的編程語言,相比其他JVM語言更加簡潔、易讀。Xtend不僅提供了一種編寫代碼生成器的機(jī)制,還提供了如類型推斷、擴(kuò)展方法、分派方法和lambda表達(dá)式等強(qiáng)大的功能,從而使模型訪問和遍歷變得非常簡單、直觀并且易于閱讀和維護(hù)。這些優(yōu)點(diǎn)促使本文使用Xtend語言來定義代碼的轉(zhuǎn)換模板。本文定義的轉(zhuǎn)換模板包含2種組成成份:第一種是靜態(tài)成份,是通過提取應(yīng)用程序的代碼特征構(gòu)建的,此部分可以直接作為代碼的組成;第二種是在符號(hào)“《 》”中定義的動(dòng)態(tài)成份,它包含模型解析和代碼生成的處理邏輯,當(dāng)這些處理邏輯執(zhí)行完成之后,其產(chǎn)生的代碼將會(huì)直接嵌入到模板中,與靜態(tài)部分組成完整的實(shí)現(xiàn)代碼。如圖4所示,模板A為Web Service類的轉(zhuǎn)換模板(用于封裝系統(tǒng)操作),模板B為實(shí)體倉庫的轉(zhuǎn)換模板,模板C為系統(tǒng)操作的轉(zhuǎn)換模板。
圖4 轉(zhuǎn)換模板示例Fig. 4 Transformation templates example
1)在模板A中:@RestController,@RequestMapping這些作為Spring Boot框架的注解用來表示類的屬性和作用。在程序部署之后,根據(jù)URL地址,服務(wù)端首先會(huì)定位到實(shí)現(xiàn)類,然后再定位到該類中的某一個(gè)具體系統(tǒng)操作。因此,在模板A和模板C中定義的@RequestMapping注解將會(huì)作為服務(wù)的一種資源標(biāo)識(shí)。DaoManage是對(duì)象管理器,能夠返回實(shí)體倉庫的引用對(duì)象。CurrentUtils是定義的工具類,用來存儲(chǔ)和獲取臨時(shí)引用,臨時(shí)引用是在操作契約中定義的一種特殊對(duì)象實(shí)例,由多個(gè)操作契約所共享,用來減少代碼冗余。表達(dá)式《name》的輸出內(nèi)容是系統(tǒng)順序圖中的服務(wù)名稱?!禛enerateWSCode()》中的表達(dá)式,對(duì)應(yīng)于算法2在生成系統(tǒng)操作的實(shí)現(xiàn)代碼之后,將代碼封裝到由模板A所形成的實(shí)現(xiàn)類框架的執(zhí)行過程。
2)模板B使用關(guān)鍵字public,以及《e.name+"Repository"》共同組成一個(gè)public接口,“e”為概念類圖中的類。此接口會(huì)默認(rèn)繼承2個(gè)父級(jí)接口以實(shí)現(xiàn)表1中的(1)~(5)基本操作,而《GenerateRECode()》中的表達(dá)式則用于生成(6)~(10)以及它們組合形成的基本操作。
3)在模板C中,“c”為算法2正在處理和解析的操作契約,因此表達(dá)式《c.op 》將會(huì)動(dòng)態(tài)地解析為當(dāng)前操作契約的名稱。為了生成具有RESTFul風(fēng)格的Web Service,本文需要為生成的系統(tǒng)操作設(shè)置HTTP動(dòng)詞來表示資源的狀態(tài)扭轉(zhuǎn)。因此,算法2將會(huì)根據(jù)操作契約的后置條件生成4種HTTP請(qǐng)求方法類型,然后以字符的形式輸出,即 《HttpMethod(c)》 將會(huì)被解析為“GET”“POST”“PUT”“DELETE”中的一種字符。關(guān)鍵字public、類型String、系統(tǒng)操作的名稱《c.op》以及輸入變量 《Parameter(c)》 共同組成系統(tǒng)操作的操作簽名。表達(dá)式 《GenerateSOCode()》 對(duì)應(yīng)于算法2中將操作契約轉(zhuǎn)換為業(yè)務(wù)代碼之后,將代碼封裝到由模板C所形成的系統(tǒng)操作框架的執(zhí)行過程。《Return(c)》 中定義的表達(dá)式可以被動(dòng)態(tài)地解析為系統(tǒng)操作的返回值。根據(jù)MVC原則,視圖層不應(yīng)該包含應(yīng)用邏輯或業(yè)務(wù)邏輯,而是應(yīng)該把請(qǐng)求委派給領(lǐng)域?qū)拥念I(lǐng)域?qū)ο螅晥D層只需要呈現(xiàn)領(lǐng)域?qū)ο蠓祷氐奶幚斫Y(jié)果。在程序部署運(yùn)行之后,視圖層以HTTP請(qǐng)求的形式訪問領(lǐng)域?qū)?,因此,需要考慮領(lǐng)域?qū)拥揭晥D層的通訊問題。鑒于JSON作為數(shù)據(jù)傳輸?shù)囊环N理想載體,模板C中定義了用于生成JSON格式字符的處理代碼,其中JSONObject是用來創(chuàng)建JSON格式數(shù)據(jù)的包裝類,其引用對(duì)象將會(huì)包裝業(yè)務(wù)邏輯的處理結(jié)果“data”、“msg” 以及“code”狀態(tài)數(shù)據(jù)。模板定義了捕獲程序異常的try-catch代碼,以及將JSON數(shù)據(jù)轉(zhuǎn)化為字符串并將其作為系統(tǒng)操作返回值的代碼。
本文中轉(zhuǎn)換算法主要有3種:第一種為算法1所示的REST生成算法,可以根據(jù)系統(tǒng)操作契約的后置條件,輸出HTTP的4種請(qǐng)求方法,用于標(biāo)識(shí)系統(tǒng)操作將要響應(yīng)的HTTP請(qǐng)求類型;第二種為算法2所示的Web Service類的生成算法,將系統(tǒng)操作封裝到JAVA類中;第三種為算法3所示的實(shí)體倉庫類的生成算法。
算法1是REST生成算法,以操作契約作為輸入,輸出為HTTP請(qǐng)求方法的字符。算法首先創(chuàng)建2個(gè)標(biāo)記Tag和M,然后解析定義在后置條件中的OCL表達(dá)式,對(duì)其中的OCL表達(dá)式執(zhí)行遍歷操作,并判斷子表達(dá)式的類型。如果子表達(dá)式屬于刪除對(duì)象類型的表達(dá)式,那么算法將Tag設(shè)置為1;如果子表達(dá)式屬于創(chuàng)建對(duì)象類型的表達(dá)式,那么算法將Tag設(shè)置為2;如果子表達(dá)式屬于更新對(duì)象類型的表達(dá)式,那么算法將M設(shè)置為1。當(dāng)遍歷完成之后,算法會(huì)判斷Tag的值,如果值為1則輸出Delete字符;如果值為2則輸出Post字符;如果值為初始值,算法會(huì)判斷M的值,如果M的值為1則輸出Put字符,否則輸出Get字符。按照單一職責(zé)原則[33],一個(gè)模塊應(yīng)該只有一個(gè)引起它發(fā)生變化的原因,應(yīng)該只做一種工作。因此系統(tǒng)操作應(yīng)該僅包含創(chuàng)建、刪除與更新對(duì)象3種業(yè)務(wù)邏輯中的一種功能,但不限于包含一種或多種查找對(duì)象的功能。而并不是所有人都會(huì)按照此原則編寫操作契約,因此設(shè)置HTTP方法的優(yōu)先級(jí)是必要的,當(dāng)前算法處理邏輯的優(yōu)先級(jí)為:Delete > Post > Put > Get。
算法1 REST生成算法。
輸入:contract——Contract。
輸出:請(qǐng)求方法類型。
1. Begin
2. Tag ← 0
3.M← 0
4. Post ← parse(contract)
5. oclExp ← parse(Post)
6. for sub-exp ∈ oclExp do
7. type ← GetType(sub-exp)
8. if type = Delete type
9. Tag ← 1
10. else if type = Create type
11. Tag ← 2
12. else if type = Update type
13.M← 1
14. end
15. end
16. if Tag = 1
17. Output(“Delete”)
18. else if Tag = 2
19. Output(“Post”)
20. else ifM= 1
21. Output(“Put”)
22. else
23. Output(“Get”)
24. end
25. end
26. end。
算法2為Web Service類的生成算法,算法以系統(tǒng)順序圖、操作契約、Web Service類以及系統(tǒng)操作的轉(zhuǎn)換模板作為輸入,輸出Web Service類。首先,算法創(chuàng)建一個(gè)空集合用于保存操作契約中的OCL表達(dá)式(OCL表達(dá)式由一行或多行子表達(dá)式組成);其次,遍歷全部系統(tǒng)順序圖,獲取該系統(tǒng)順序圖的系統(tǒng)事件名稱op,并根據(jù)Web Service類的轉(zhuǎn)換模板生成Web Service類的代碼框架;然后,對(duì)全部操作契約執(zhí)行遍歷操作,如果當(dāng)前操作契約的名稱與op相同,則根據(jù)系統(tǒng)操作的轉(zhuǎn)換模板生成系統(tǒng)操作的代碼框架;之后,解析操作契約中的臨時(shí)對(duì)象,如果臨時(shí)對(duì)象不為空,則為其生成引用;接下來,解析操作契約中的definition、前置條件和后置條件部分,如果它們不為空,則會(huì)解析其中的OCL表達(dá)式并放入到oclSet集合中;接著,遍歷oclSet集合中的OCL表達(dá)式,將子表達(dá)式與轉(zhuǎn)換規(guī)則中的表達(dá)式做匹配,獲取該表達(dá)式的類型;此后,根據(jù)轉(zhuǎn)換規(guī)則將此行表達(dá)式轉(zhuǎn)換成為代碼。當(dāng)遍歷執(zhí)行完成,操作契約中的OCL表達(dá)式會(huì)轉(zhuǎn)換成系統(tǒng)操作的業(yè)務(wù)邏輯代碼,并被算法添加到系統(tǒng)操作的代碼框架SO中。接下來,根據(jù)操作契約的后置條件生成系統(tǒng)操作的返回值,并添加到SO中。執(zhí)行完此步驟,當(dāng)前遍歷的操作契約就被成功地轉(zhuǎn)換為系統(tǒng)操作。最后,該系統(tǒng)操作會(huì)被封裝到Web Service類的代碼框架WS中。
算法2 Web Service 類生成算法。
輸入:Tws——Web Service類的轉(zhuǎn)換模板;Tso——系統(tǒng)操作的轉(zhuǎn)換模板;CS——全部操作契約;SSDS——全部系統(tǒng)順序圖。
輸出:Web Service 類。
1. Begin
2. oclSet ← ? ∥創(chuàng)建一個(gè)空集合
3. for ssd ∈ SSDS do
4. op ← ssd.op
5. Generate skeleton WS by Tws
6. for contract ∈ CS do
7. if op = contract.op
8. Generate skeleton SO by Tso
9. TP ← parse(contract)
10. if TP<>null
11. Generate reference
12. end
13. Def ← parse(contract)
14. if Def <> null
15. OCLExp ← parse(Def)
16. oclSet.append(OCLExp)
17. end
18. Pre ← parse(contract)
19. if Pre <> null
20. OCLExp ← parse(Pre)
21. oclSet.append(OCLExp)
22. end
23. Post ← parse(contract)
24. if Post <> null
25. OCLExp ← parse(Post)
26. oclSet.append(OCLExp)
27. end
28. for sub-exp ∈ oclSet do
29. type ← Match(sub-exp) ∥匹配轉(zhuǎn)換規(guī)則
30. code ← Generate(type , sub-exp) ∥依據(jù)轉(zhuǎn)換規(guī)則生成代碼
31. SO ← code
32. end
33. resultCode ←GetResult (Post)
34. SO ← resultCode
35. WS ← SO ∥將系統(tǒng)操作封裝到Web Service類
36. end
37. end
38. end
39. end。
算法3為實(shí)體倉庫的生成算法,它以概念類圖、操作契約以及實(shí)體倉庫的轉(zhuǎn)換模板作為輸入,輸出實(shí)體倉庫的JAVA接口。算法先遍歷概念類圖中的概念類,每執(zhí)行一次遍歷,都會(huì)根據(jù)實(shí)體倉庫的轉(zhuǎn)換模板生成JAVA接口的代碼框架(每個(gè)概念類都要有一個(gè)相對(duì)應(yīng)的實(shí)體倉庫)。其次,遍歷概念類中的關(guān)聯(lián)關(guān)系(關(guān)聯(lián)關(guān)系包括一對(duì)一、一對(duì)多、多對(duì)一),如果當(dāng)前概念類屬于關(guān)聯(lián)關(guān)系中“多”的一方,則根據(jù)此關(guān)聯(lián)關(guān)系生成findByAssociation基本操作,并將此基本操作的返回值設(shè)置為集合類型,否則其返回值會(huì)設(shè)置為對(duì)象類型。然后,遍歷全部操作契約的所有OCL子表達(dá)式,每次遍歷都會(huì)設(shè)置一個(gè)空的集合Set。接下來,分別遍歷子表達(dá)式中的概念類的屬性和關(guān)聯(lián)關(guān)系,如果此屬性和關(guān)聯(lián)關(guān)系屬于當(dāng)前概念類,則將該屬性和關(guān)聯(lián)關(guān)系存入Set集合。接著,將Set集合中的屬性和關(guān)聯(lián)關(guān)系組合在一起,并判斷子表達(dá)式是否包含關(guān)鍵字“any”,如果包含,則生成findByElement這種由多種元素組合而成的基本操作,并將此基本操作的返回值設(shè)置為對(duì)象類型,否則設(shè)置為集合類型。當(dāng)所有OCL子表達(dá)式的遍歷執(zhí)行完成,操作契約中關(guān)于此概念類的基本操作就會(huì)生成完成。最后,把這些基本操作封裝到實(shí)體倉庫的代碼框架ER中。
算法3 實(shí)體倉庫生成算法
輸入:CS——Contracts;ccd——概念類圖;T——實(shí)體倉庫的轉(zhuǎn)換模板。
輸出:Entity Repository。
1. Begin
2. for Class ∈ ccd do
3. Generate skeleton ER byT
4. for association ∈ Class do
5. if IsMultipe(association) = true
6. Generate findManyByAssociation code
7. else
8. Generate findOneByAssociation code
9. end
10. end
11. for sub-exp ∈ CS do
12. Set ←?
13. for attribute ∈ sub-exp do
14. if callClass(attribute) = Class
15. Set ← attribute
16. end
17. end
18. for association∈ sub-exp do
19. if callClass(attribute) = Class
20. Set ← association
21. end
22. end
23. Element ← Connect(Set)
24. if sub-exp include(any)
25. Generate findOneByElement code
26. else
27. Generate findManyByElement code
28. end
29. end
30. ER ← AllCode; ∥將基本操作封裝進(jìn)代碼框架
31. end
32. end。
為了對(duì)本文方法的實(shí)現(xiàn)原理進(jìn)行說明,引入用戶登錄案例來介紹需求模型的組成結(jié)構(gòu)和作用,以及如何將模型轉(zhuǎn)換成代碼。如圖5所示,對(duì)該案例建模得到的需求模型包括4種子模型:用例圖、概念類圖、系統(tǒng)順序圖與操作契約。1)用例圖中有參與者User(表示與系統(tǒng)交互的實(shí)體)和用例loginService(表示系統(tǒng)需要執(zhí)行的服務(wù))。2)概念類圖使用User類和Role類描述系統(tǒng)結(jié)構(gòu),圖中還展示了Role類與User類的關(guān)聯(lián)關(guān)系以及類具有的屬性。3)系統(tǒng)順序圖顯示了4種系統(tǒng)事件與系統(tǒng)的交互順序,表示的含義為:系統(tǒng)對(duì)象User首先執(zhí)行register操作,然后選擇一種登錄方式,打開主界面。4)操作契約封裝了系統(tǒng)對(duì) register() 事件進(jìn)行響應(yīng)和處理的執(zhí)行邏輯,它的組成結(jié)構(gòu)有:簽名(Signature)、Definitions、前置條件(Precondition)、后置條件(Postcondition)。簽名部分包括返回值的類型和3種輸入?yún)?shù):用戶名(userName)、密碼(pwd)、驗(yàn)證碼(code)。Definitions部分定義了2種OCL表達(dá)式:根據(jù)用戶名查詢系統(tǒng)中的對(duì)象user;獲取驗(yàn)證碼str。前置條件中定義了系統(tǒng)運(yùn)行需要滿足的3種條件:user必須不存在,str必須存在,code與str必須一致。后置條件中定義了系統(tǒng)運(yùn)行之后對(duì)象的狀態(tài)變化:首先創(chuàng)建User類的對(duì)象u,然后將輸入?yún)?shù)賦值給對(duì)象u的屬性,最后將u保存到系統(tǒng)中。
圖5 需求模型示例Fig. 5 Requirements model example
生成代碼的示例如圖6所示。系統(tǒng)操作中的“POST”請(qǐng)求類型,由REST生成算法,根據(jù)操作契約的后置條件轉(zhuǎn)換生成;整個(gè)系統(tǒng)操作的方法簽名和方法體,由Web Service 類生成算法,根據(jù)圖5中的操作契約,結(jié)合系統(tǒng)操作的轉(zhuǎn)換模板和匹配到的轉(zhuǎn)換規(guī)則自動(dòng)生成;實(shí)體倉庫由實(shí)體倉庫生成算法,結(jié)合實(shí)體倉庫的轉(zhuǎn)換模板,根據(jù)需求模型中的操作契約和概念類圖轉(zhuǎn)換形成;概念類圖中的User類可以被轉(zhuǎn)換形成圖6中的實(shí)體類。
圖6 生成代碼示例Fig. 6 Generated code example
通過互聯(lián)網(wǎng)檢索開源代碼庫中的源代碼,獲取到了外賣訂單系統(tǒng)(TakeOS)、停車管理系統(tǒng)(Park)、在線教育系統(tǒng)(OE) 3種與日常生活息息相關(guān)的Web Service程序,這3個(gè)程序的功能需求覆蓋面大,可用來驗(yàn)證本文方法建模復(fù)雜用例的能力。此外,合作企業(yè)提供了已投入使用的機(jī)場(chǎng)報(bào)修系統(tǒng)(AMS)的源代碼,代碼中包含了企業(yè)內(nèi)部工作流程的復(fù)雜業(yè)務(wù)邏輯,可用來驗(yàn)證本文方法生成復(fù)雜系統(tǒng)操作的能力。本文將在以下部分介紹4個(gè)案例需求建模和程序生成的結(jié)果。
需求復(fù)雜度用于估算面向?qū)ο蟪绦虻木幊坛杀?,可通過統(tǒng)計(jì)需求模型中參與者(Actor)、用例(UseCase)、系統(tǒng)操作(SO)、實(shí)體類(Entity)以及實(shí)體類之間的關(guān)聯(lián)關(guān)系(Association)的數(shù)量來衡量[21]。因此,本文依據(jù)4個(gè)案例的源代碼反向推導(dǎo)需求模型,然后分別統(tǒng)計(jì)案例的需求復(fù)雜度,最終得到如表2所示的建模結(jié)果: 4個(gè)案例總共包括23個(gè)參與者,117個(gè)用例,194個(gè)系統(tǒng)操作,39個(gè)實(shí)體類,45種類間關(guān)聯(lián)關(guān)系。
表2 需求復(fù)雜度Tab. 2 Requirements complexity
為了說明本文方法可以用來生成哪種類型的系統(tǒng)操作以及生成過程中的錯(cuò)誤處理等問題,以AMS案例的6種功能性需求(如表3所示)為例詳細(xì)介紹系統(tǒng)操作的分析和處理過程。AMS的故障報(bào)修、工單轉(zhuǎn)發(fā)、故障受理、完成維修等需求屬于業(yè)務(wù)邏輯中屬性賦值、查找對(duì)象、設(shè)置引用、創(chuàng)建對(duì)象、刪除對(duì)象、更新對(duì)象等操作的實(shí)現(xiàn)范圍,這類需求完全可以使用OCL語句中的系統(tǒng)狀態(tài)表達(dá)式描述,也符合轉(zhuǎn)換規(guī)則的轉(zhuǎn)換條件,因此可以為這些需求創(chuàng)建操作契約。對(duì)于AMS的附件上傳需求,由于其系統(tǒng)操作的實(shí)現(xiàn)代碼中具有復(fù)雜的IO流操作,而這些操作不能使用OCL語句表示,更無法為此需求創(chuàng)建操作契約,因此,本文方法不適用于實(shí)現(xiàn)此類需求。保存密碼功能需要前端和后端協(xié)同實(shí)現(xiàn),但是因?yàn)楸疚姆椒壳吧胁荒苌蓮?fù)雜的前端界面交互邏輯,而且創(chuàng)建的操作契約只能生成后端執(zhí)行代碼,無法實(shí)現(xiàn)對(duì)該功能的完整描述,所以此類需求也不適合使用本文方法實(shí)現(xiàn)。按照上述同樣的原理或方法,對(duì)4個(gè)案例的系統(tǒng)操作進(jìn)行分析,并通過創(chuàng)建操作契約,總結(jié)出如表4所示的系統(tǒng)操作生成結(jié)果。4個(gè)案例總共建模得到194個(gè)系統(tǒng)操作,為其中的181個(gè)創(chuàng)建了操作契約,占總數(shù)的比例約為93.3%,只有約6.7%的系統(tǒng)操作無法實(shí)現(xiàn)正確的建模和代碼生成。通過對(duì)實(shí)驗(yàn)結(jié)果的分析,需求建模和代碼生成的失敗情況通常是以下情形:1)對(duì)于發(fā)送電子郵件、MD5加密、打印文檔等功能,如果不調(diào)用第三方API,則無法在操作契約中正確指定OCL表達(dá)式;2)如保存密碼等功能可以通過指定操作契約生成后端代碼,但不能為其生成前端交互代碼;3)一些系統(tǒng)操作目前只能通過手工編碼實(shí)現(xiàn),例如附件上傳、發(fā)送語音等功能。
表3 AMS案例需求說明Tab. 3 Requirements specification for AMS case
表4 系統(tǒng)操作的生成結(jié)果Tab. 4 Generation results of system operations
總之,本文選擇的4個(gè)案例都具有相對(duì)復(fù)雜的業(yè)務(wù)邏輯,可用于驗(yàn)證需求建模以及程序生成的不同方面。而4個(gè)案例中93.3%的系統(tǒng)操作可以實(shí)現(xiàn)自動(dòng)生成,證明本文方法自動(dòng)生成的軟件具有很高的實(shí)現(xiàn)程度,同時(shí)也證明本文提出的10種轉(zhuǎn)換規(guī)則結(jié)合RM2PT中定義的26種轉(zhuǎn)換規(guī)則,適用于生成Web Service程序中的大多數(shù)業(yè)務(wù)邏輯代碼。
為了驗(yàn)證生成的系統(tǒng)操作是否正確,需要對(duì)自動(dòng)生成的181個(gè)系統(tǒng)操作進(jìn)行功能性測(cè)試。本文選擇了如表5所示的實(shí)驗(yàn)環(huán)境和參數(shù),并使用Junit單元測(cè)試框架創(chuàng)建了350個(gè)測(cè)試用例,這些測(cè)試用例能夠?qū)崿F(xiàn)對(duì)181個(gè)系統(tǒng)操作的單元測(cè)試。表6為自動(dòng)生成系統(tǒng)操作的測(cè)試結(jié)果,其中GET類型的測(cè)試用例有131個(gè),執(zhí)行成功的數(shù)量為127個(gè),成功率為96.95%;POST類型的測(cè)試用例83個(gè),測(cè)試成功83個(gè),成功率100%;DELETE類型的測(cè)試用例為56個(gè),測(cè)試成功的數(shù)量為40個(gè),成功率為71.43%;PUT類型的測(cè)試用例為80個(gè),測(cè)試成功80個(gè),成功率為100%。測(cè)試用例能夠覆蓋全部的系統(tǒng)操作,并且能夠取得大約94.29%的執(zhí)行成功率,只有5.71%的測(cè)試用例發(fā)生了預(yù)期之外的錯(cuò)誤。
表5 實(shí)驗(yàn)環(huán)境和參數(shù)Tab. 5 Experimental environment and parameter
表6 系統(tǒng)操作的測(cè)試結(jié)果Tab. 6 Testing result of system operations
測(cè)試結(jié)果顯示,測(cè)試失敗的用例主要是GET和DELETE類型。通過分析發(fā)生的錯(cuò)誤信息,發(fā)現(xiàn)測(cè)試失敗的主要原因是:1)當(dāng)數(shù)據(jù)表之間存在外鍵關(guān)聯(lián)時(shí),將無法正常刪除該表中的數(shù)據(jù);2)如果訪問請(qǐng)求中的輸入?yún)?shù)與數(shù)據(jù)表中的類型不匹配,程序?qū)?huì)在運(yùn)行時(shí)發(fā)生異常。因此,使用DELETE類型的請(qǐng)求方法會(huì)引起第一種錯(cuò)誤,可采用軟件開發(fā)中常用的“邏輯刪除”機(jī)制應(yīng)對(duì)此問題。應(yīng)用程序中最常見的數(shù)據(jù)操作是查詢操作,因此使用GET類型的請(qǐng)求方法會(huì)有比較大的概率引發(fā)第二種錯(cuò)誤,可通過修改概念類圖中的屬性類型解決此問題。
綜上所述,測(cè)試用例的運(yùn)行結(jié)果顯示,大約94.29%的測(cè)試用例能夠成功執(zhí)行,這表明從需求模型中自動(dòng)生成的系統(tǒng)操作具有很高的正確率。此外,因?yàn)闇y(cè)試中出現(xiàn)的錯(cuò)誤對(duì)應(yīng)于需求模型中的建模缺陷,所以,通過運(yùn)行測(cè)試用例可以快速定位程序運(yùn)行過程中出現(xiàn)的錯(cuò)誤,并根據(jù)錯(cuò)誤的原因修改需求模型中的建模缺陷,重新生成可靠的應(yīng)用程序。
為了提升Web Service程序的開發(fā)效率,本文提出一種低代碼開發(fā)方法,并取得了較好的實(shí)驗(yàn)效果,但方法仍然存在一些局限性,主要包含如下3點(diǎn):
1)因?yàn)楸疚氖褂玫男枨竽P蛯儆赨ML的受限子集,所以適合生成面向?qū)ο箢愋偷男畔⑾到y(tǒng)。但是本文方法不支持實(shí)時(shí)系統(tǒng)、嵌入式系統(tǒng)和信息物理融合系統(tǒng)的開發(fā),這類系統(tǒng)需要更復(fù)雜的需求模型。
2)本文方法可以提升軟件開發(fā)效率,但是目前在將用戶需求形式化的過程中,采用的是手工編寫操作契約方式,可能會(huì)產(chǎn)生較高的使用成本。
3)案例研究表明大多數(shù)系統(tǒng)操作可以實(shí)現(xiàn)自動(dòng)生成,但是少部分系統(tǒng)操作相對(duì)比較復(fù)雜,無法滿足轉(zhuǎn)換規(guī)則的使用條件,需要以手工編碼的方式實(shí)現(xiàn)。
本文方法目前只針對(duì)Web Service程序的開發(fā),但可以通過優(yōu)化需求模型的組成以使其適用于其他系統(tǒng)的開發(fā)。使用操作契約可能產(chǎn)生較高的成本,未來可考慮從自然語言描述的需求規(guī)格說明中提取操作契約,以便有效降低操作契約的使用成本。對(duì)于存在部分系統(tǒng)操作相對(duì)比較復(fù)雜的情況,可以通過提供第三方API存儲(chǔ)庫的方式解決。
本文提出一種用于Web Service程序開發(fā)的低代碼方法,可直接將需求模型自動(dòng)轉(zhuǎn)換為標(biāo)準(zhǔn)化的Web Service程序。首先,基于RM2PT定義的26條轉(zhuǎn)換規(guī)則,通過引入JPA定義的基本操作,構(gòu)建10條新的轉(zhuǎn)換規(guī)則;其次,通過提取Web Service程序的代碼特征構(gòu)建轉(zhuǎn)換模板;最后,建立用于解析和處理需求模型的轉(zhuǎn)換算法。本文通過4個(gè)案例評(píng)估方法的有效性,實(shí)驗(yàn)結(jié)果表明,約93.3%的系統(tǒng)操作可以實(shí)現(xiàn)自動(dòng)生成。與傳統(tǒng)的軟件開發(fā)方法相比,本文方法不僅能屏蔽軟件實(shí)現(xiàn)的技術(shù)細(xì)節(jié),降低從事軟件開發(fā)工作所需要的技術(shù)門檻,而且還能提高軟件的開發(fā)效率與質(zhì)量。
雖然實(shí)驗(yàn)結(jié)果符合預(yù)期,但是本文方法還存在改進(jìn)的空間,例如編寫正確的OCL操作契約會(huì)帶來一定的使用成本。因此,未來可以從4個(gè)方面繼續(xù)改進(jìn)生成方法,使其更加完善:
1)從需求模型中自動(dòng)生成系統(tǒng)操作的測(cè)試用例,實(shí)現(xiàn)對(duì)需求模型的有效驗(yàn)證。
2)為了給用戶提供多樣化選擇,未來將通過優(yōu)化需求模型的組成使本文方法適用于其他系統(tǒng)的開發(fā)。
3)使用自然語言描述用戶需求,并實(shí)現(xiàn)將自然語言轉(zhuǎn)換成操作契約,讓普通用戶不需要依賴架構(gòu)師和開發(fā)人員的專業(yè)知識(shí)就可以生成目標(biāo)軟件,進(jìn)一步降低技術(shù)難度。
4)通過提供第三方API存儲(chǔ)庫,用戶可直接將第三方API集成到操作契約中,以此實(shí)現(xiàn)復(fù)雜系統(tǒng)操作的自動(dòng)生成。