姜玨 祝義 王瑋 印嬋 張穎
摘要:針對(duì)面向數(shù)據(jù)流的軟件設(shè)計(jì)方法缺乏有效的數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)換工具問(wèn)題,提出了面向數(shù)據(jù)流的軟件設(shè)計(jì)方法并開(kāi)發(fā)出了相應(yīng)工具。首先,討論了對(duì)用戶(hù)繪制的數(shù)據(jù)流圖進(jìn)行存儲(chǔ)的方法;其次,給出了數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖轉(zhuǎn)換的系列算法;最后,通過(guò)Java語(yǔ)言實(shí)現(xiàn)了數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖轉(zhuǎn)換的原型系統(tǒng)。實(shí)驗(yàn)表明該系統(tǒng)能夠根據(jù)用戶(hù)輸入的數(shù)據(jù)流圖生成滿(mǎn)足用戶(hù)需求的軟件結(jié)構(gòu)圖,從而能夠從根本上提高面向數(shù)據(jù)流的軟件設(shè)計(jì)方法的效率。
關(guān)鍵詞:數(shù)據(jù)流;軟件結(jié)構(gòu)圖;轉(zhuǎn)換;原型系統(tǒng);功能性測(cè)試
中圖分類(lèi)號(hào):TP311? ? ? 文獻(xiàn)標(biāo)識(shí)碼:A
文章編號(hào):1009-3044(2020)32-0086-04
Abstract: Aim to data flow-oriented design lacks the transformation tool from dataflow diagram to software architecture diagram, this paper proposes data flow-oriented software design method and develops the corresponding tool. Firstly, a method is discussed to store data flow diagram which is input by the user. Secondly, a series of algorithms is given for the transformation from the data flow diagram to a software architecture diagram. Lastly, a prototype system is implemented for transforming the data flow diagram to a software architecture diagram. The experiments show that the software architecture diagram can be generated according to the dataflow diagram which is input by users, so it can improve the efficiency of data flow-oriented method.
Key words: data flow; software structure diagram; transformation; prototype system; functional test
1 背景
需求階段和設(shè)計(jì)階段是軟件生命周期的兩個(gè)重要階段:需求階段主要關(guān)注如何描述問(wèn)題空間,而設(shè)計(jì)階段則主要關(guān)注如何描述解空間[1-2]。自軟件危機(jī)以來(lái),軟件的規(guī)模愈來(lái)愈大,需求到設(shè)計(jì)階段的重要性也越來(lái)越高,設(shè)計(jì)的失誤與變動(dòng)往往需要很大的代價(jià)去維護(hù)[3]。在進(jìn)行大型軟件開(kāi)發(fā)時(shí),需求和設(shè)計(jì)階段需要進(jìn)行反復(fù)迭代與驗(yàn)證,直到設(shè)計(jì)能很好地支撐后續(xù)的軟件開(kāi)發(fā)[4-5]。
在面向數(shù)據(jù)流設(shè)計(jì)的方法中,數(shù)據(jù)流圖(Data Flow Diagram, DFD)到軟件結(jié)構(gòu)圖(Software Structure Diagram, SSD)的轉(zhuǎn)換是舉足輕重的環(huán)節(jié),數(shù)據(jù)流圖和軟件結(jié)構(gòu)圖分別是需求階段和設(shè)計(jì)階段的制品,對(duì)后續(xù)的軟件開(kāi)發(fā)十分重要[6-7]。但是目前未知,簡(jiǎn)單易用的轉(zhuǎn)換工具還沒(méi)有開(kāi)發(fā)出來(lái),導(dǎo)致數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)換迄今為止還是建立在設(shè)計(jì)人員的設(shè)計(jì)經(jīng)驗(yàn)上,以人為的方式進(jìn)行著[8-9]。
數(shù)據(jù)流圖分為事務(wù)型和變換型,事務(wù)型的轉(zhuǎn)換比較簡(jiǎn)單,并且在實(shí)際中變換型的數(shù)據(jù)流圖使用更為廣泛,所以本文將主要針對(duì)變換型的數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)換展開(kāi)研究,并給出行之有效的分析與設(shè)計(jì)方法,在此基礎(chǔ)上開(kāi)發(fā)一個(gè)原型系統(tǒng),以期為設(shè)計(jì)和開(kāi)發(fā)高質(zhì)量的現(xiàn)代大型復(fù)雜軟件系統(tǒng)提供堅(jiān)實(shí)的理論基礎(chǔ)和相應(yīng)的工具支持。
本文將研究數(shù)據(jù)流圖到結(jié)構(gòu)圖的轉(zhuǎn)換,從而實(shí)現(xiàn)面向數(shù)據(jù)流的軟件設(shè)計(jì)方法,并在此基礎(chǔ)上設(shè)計(jì)相應(yīng)的轉(zhuǎn)換工具。
2 系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
基于模型轉(zhuǎn)換的數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)換系統(tǒng)的開(kāi)發(fā)過(guò)程分為以下幾個(gè)階段。
2.1 需求分析
2.1.1 數(shù)據(jù)流圖的繪制存儲(chǔ)
數(shù)據(jù)流圖是一種圖形化技術(shù),是系統(tǒng)邏輯功能的圖形表示,它描繪信息流和數(shù)據(jù)從輸入移動(dòng)到輸出的過(guò)程中所經(jīng)受的變換。包含4種基本符號(hào),正方形(或立方體)表示數(shù)據(jù)的源點(diǎn)和終點(diǎn),圓形(或圓角矩形)代表變換數(shù)據(jù)的處理,兩條平行橫線(xiàn)(或開(kāi)口矩形)代表數(shù)據(jù)存儲(chǔ),箭頭表示數(shù)據(jù)流,即特定數(shù)據(jù)的流動(dòng)方向[10-11]。
為了完成數(shù)據(jù)流圖的繪制,系統(tǒng)需要包含繪制模塊,提供繪制面板以及繪制以上4種基本符號(hào)的方法,同時(shí)實(shí)現(xiàn)對(duì)圖形的細(xì)化處理,實(shí)現(xiàn)以下功能:
1)對(duì)選定圖形進(jìn)行拖動(dòng);
2)對(duì)選定圖形進(jìn)行大小調(diào)整;
3)對(duì)選定圖形進(jìn)行添加文字。
此外,為了降低系統(tǒng)的耦合度,并且保證繪制的數(shù)據(jù)流圖能夠持久使用,還需實(shí)現(xiàn)將繪制的數(shù)據(jù)流圖,以一定的形式保存到本地的功能。
2.1.2 數(shù)據(jù)流圖轉(zhuǎn)換為軟件結(jié)構(gòu)圖
設(shè)計(jì)轉(zhuǎn)換算法,讀取本地保存的數(shù)據(jù)流圖,并進(jìn)行解析,實(shí)現(xiàn)與代碼進(jìn)行對(duì)接,完成數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)換,保存轉(zhuǎn)換后的數(shù)據(jù)到本地。
2.1.3 顯示軟件結(jié)構(gòu)圖
實(shí)現(xiàn)顯示和修改軟件結(jié)構(gòu)圖功能。反編譯轉(zhuǎn)換后的數(shù)據(jù),通過(guò)繪制模塊,將轉(zhuǎn)換算法輸出的數(shù)據(jù)進(jìn)行映射顯示,得到軟件結(jié)構(gòu)圖,同時(shí),用戶(hù)可對(duì)得到的軟件結(jié)構(gòu)圖進(jìn)行修改調(diào)整,得到合適的軟件結(jié)構(gòu)圖,最后還需實(shí)現(xiàn)將軟件結(jié)構(gòu)圖以一定的形式保存到本地的功能。
2.2 原型系統(tǒng)設(shè)計(jì)
2.2.1 輸入模塊
輸入模塊采用主流的繪畫(huà)界面設(shè)計(jì),用戶(hù)可以用拖曳圖形的方式自由繪制數(shù)據(jù)流圖,繪圖界面會(huì)為用戶(hù)提供基本繪制圖形??蚣軐?shí)現(xiàn)了拖曳、縮放、旋轉(zhuǎn)、自定義屬性等基礎(chǔ)操作,開(kāi)發(fā)者只用關(guān)心圖表繪畫(huà)實(shí)現(xiàn)即可。
由于系統(tǒng)無(wú)法對(duì)處理元素進(jìn)行細(xì)分,因此在繪圖界面需要用戶(hù)自主選擇處理元素是輸入子元素,處理子元素還是輸出子元素。在用戶(hù)繪制數(shù)據(jù)流圖時(shí),每新建的一個(gè)結(jié)點(diǎn)或邊,都會(huì)隨機(jī)生成ID,用于區(qū)分每個(gè)元素,便于后臺(tái)轉(zhuǎn)換處理。
輸入模塊將用戶(hù)繪制的數(shù)據(jù)流圖以json的格式保存至本地,轉(zhuǎn)換模塊只需讀取解析本地的json文件進(jìn)行處理,前后臺(tái)的工作任務(wù)相對(duì)較為獨(dú)立。
輸入模塊的繪制流程圖如圖1所示。
2.2.2 轉(zhuǎn)換模塊
轉(zhuǎn)換模塊采用JAVA語(yǔ)言,根據(jù)數(shù)據(jù)流圖的json對(duì)象編寫(xiě)一個(gè)實(shí)體類(lèi)用于接收整個(gè)數(shù)據(jù)流圖,此類(lèi)中包含所有結(jié)點(diǎn)和邊的信息、隨機(jī)生成的ID號(hào)。掃描類(lèi)對(duì)象的所有節(jié)點(diǎn)后,構(gòu)造出對(duì)應(yīng)十字鏈表的所有包含源結(jié)點(diǎn)信息的新結(jié)點(diǎn),同時(shí)根據(jù)類(lèi)對(duì)象中所有邊結(jié)點(diǎn)的弧頭弧尾信息構(gòu)造十字鏈表。將存儲(chǔ)著整張數(shù)據(jù)流圖的十字鏈表轉(zhuǎn)換成樹(shù)后,以樹(shù)的形式存儲(chǔ)軟件結(jié)構(gòu)圖并將其投影至用戶(hù)界面。
2.2.3 輸出模塊
輸出模塊的過(guò)程是輸入模塊的逆序執(zhí)行過(guò)程,將軟件結(jié)構(gòu)圖對(duì)應(yīng)的樹(shù)轉(zhuǎn)換的對(duì)象反編譯后以json文件的形式保存本地。在此過(guò)程中,根據(jù)類(lèi)對(duì)象中每個(gè)結(jié)點(diǎn)的信息計(jì)算出它們的投影坐標(biāo),后續(xù)任務(wù)按照順序執(zhí)行即可。用戶(hù)可以在前臺(tái)讀取文件,進(jìn)一步顯示軟件結(jié)構(gòu)圖。
2.2.4 編碼實(shí)現(xiàn)
1)轉(zhuǎn)換模塊核心代碼:
構(gòu)造十字鏈表
public static OLGraph getOLGraph(DFD dfd){
OLGraph graph = new OLGraph();
graph.setNodeNum(dfd.getNodes().size());
graph.setArcNum(dfd.getLines().size());
Map
for (int i = 0; i < dfd.getNodes().size() ; i++) {
NodesEntity nodeEntity = dfd.getNodes().get(i);
Node node = new Node(nodeEntity.getId(),nodeEntity.getText(),ClassifyType.getType(nodeEntity),null,null);
graph.getNodes().add(node);
map.put(node.getId(),node);
if(node.getType().equals("origin"))
graph.getOrigins().add(node);
}
for (int i = 0; i < dfd.getLines().size(); i++) {
LinesEntity lineEntity = dfd.getLines().get(i);
Node tail = map.get(lineEntity.getFrom().getId());
Node head = map.get(lineEntity.getTo().getId());
Arc arc = new Arc(tail,head,null,null,lineEntity.getText());
構(gòu)造出度 將這條邊放在相同弧尾的邊節(jié)點(diǎn)后
Arc tailArc = tail.getFirstout();
Arc tailLink = null;
if(tailArc==null) {
tail.setFirstout(arc);
}
else{
tailLink = tailArc.getTlink();
while (tailLink!=null){
tailArc=tailLink;
tailLink=tailLink.getTlink();
}
tailArc.setTlink(arc);
}
構(gòu)造入度 將這條邊放在相同弧尾的邊節(jié)點(diǎn)后
Arc headArc = head.getFirstin();
Arc headLink = null;
if(headArc==null) {
head.setFirstin(arc);
}
else{
headLink = headArc.getHlink();
while (headLink!=null){
headArc=headLink;
headLink=headLink.getHlink();
}
headArc.setHlink(arc);
}
}
return graph;
}
十字鏈表轉(zhuǎn)換成樹(shù)
public static ChildTree getTree(OLGraph olGraph) {
第一遍遍歷構(gòu)造input模塊
for (Node origin : olGraph.getOrigins()) {
拿到input頭節(jié)點(diǎn)
CTNode pre = tree.getNodes().get(1);
DFSTraverse1(pre, origin);
}
第二遍遍歷構(gòu)造process模塊和output模塊
set = new HashSet<>();
for (Node node : input) {
pre1是process頭節(jié)點(diǎn) pre2是output頭節(jié)點(diǎn)
CTNode pre1 = tree.getNodes().get(2);
CTNode pre2 = tree.getNodes().get(3);
DFSTraverse2(pre1, pre2, node);
}
逆置樹(shù)的input模塊的結(jié)點(diǎn)
ReverInput(tree);
return tree;
}
2)輸出模塊核心代碼
input模塊的所有節(jié)點(diǎn)構(gòu)造
Queue
queue.add(input);
int inputLayer = -1 ;? ? 當(dāng)前遍歷到input的層數(shù)
int inputIndex = 0 ;? ? 當(dāng)前遍歷到該層的第幾個(gè)
while (!queue.isEmpty()){
int size = queue.size();
inputLayer++;
inputIndex = 0;
int layer = inputLayerCount.get(inputLayer);? 當(dāng)前層數(shù)的節(jié)點(diǎn)個(gè)數(shù)
int start = 0;
if(layer!=inputMaxLayer)
start = (inputX - inputWidth/2) +( (inputMaxLayer - layer) * 150 + (? (inputMaxLayer - layer) * 50 ) )/ 2 + 75;
else
start = inputX - inputWidth/2 + 75;
for(int i = 0 ; i < size ;i++){
CTNode node = queue.poll();
if(node.getId().equals(input.getId())){
NodesEntity inputEntity = getNodeEntity(inputX, inputY, tree.getNodes().get(1));
dfd.getNodes().add(inputEntity);
map.put(inputEntity.getId(),inputEntity);
CTArc child = node.getFirstChild();
while(child != null){
queue.add(child.getTo());
child = child.getLink();
}
}
else {
inputIndex++;
int? x = start + (inputIndex - 1) * 150 + (inputIndex - 1) * 50;
int y = inputLayer * 150 + inputY ;
NodesEntity nodeEntity = getNodeEntity(x, y, node);
dfd.getNodes().add(nodeEntity);
map.put(nodeEntity.getId(),nodeEntity);
CTArc child = node.getFirstChild();
while(child != null){
queue.add(child.getTo());
child = child.getLink();
}
}
}
}
構(gòu)造邊節(jié)點(diǎn)
Set
for (CTArc ctArc : arcSet) {
dfd.getLines().add(getLineEntity(ctArc));
}
2.3 系統(tǒng)測(cè)試結(jié)果
圖2是一個(gè)銀行存取款系統(tǒng)的數(shù)據(jù)流圖。
經(jīng)過(guò)原型系統(tǒng)的轉(zhuǎn)換后,圖3軟件結(jié)構(gòu)圖如下所示。
3 結(jié)束語(yǔ)
本文主要研究了數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)化,并開(kāi)發(fā)一個(gè)原型系統(tǒng)。首先我們了解了數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)換方法研究現(xiàn)狀和相關(guān)背景。然后,我們對(duì)此項(xiàng)目進(jìn)行了需求分析。接下來(lái),我們?cè)谙到y(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)過(guò)程中主要分析了輸入、轉(zhuǎn)換、輸出三大模塊的內(nèi)容,并且展示了相關(guān)核心代碼。最后,我們對(duì)已完成的原型系統(tǒng)進(jìn)行功能性測(cè)試。測(cè)試結(jié)果正確,我們展示了測(cè)試所用的數(shù)據(jù)流圖和得出的軟件結(jié)構(gòu)圖。此測(cè)試結(jié)果證明了本文實(shí)現(xiàn)了從數(shù)據(jù)流圖到軟件結(jié)構(gòu)圖的轉(zhuǎn)化。
參考文獻(xiàn):
[1] 祝義.嵌入式軟件需求規(guī)約到軟件體系結(jié)構(gòu)模型的轉(zhuǎn)換研究[D].南京:南京航空航天大學(xué),2011.
[2] 梅宏,申峻嶸.軟件體系結(jié)構(gòu)研究進(jìn)展[J].軟件學(xué)報(bào),2006,17(6):1257-1275.
[3] Svetinovic D.Architecture-level requirements specification[J].Second International Workshop from Software Requirements to Architectures (STRAW'03),2003:14-19.
[4] Hong M.A complementary approach to requirements engineering—software architecture orientation[J].ACM SIGSOFT Software Engineering Notes,2000,25(2):40-45.
[5] Hofmeister C,Nord R,Soni D.Applied Software Architecture[M].Boston: Addison-Wesley Professional, 2010.
[6] Zhang W,Mei H,Zhao H Y,et al.Transformation from CIM to PIM:A feature-oriented component-based approach[M]//Model Driven Engineering Languages and Systems.Berlin,Heidelberg:Springer Berlin Heidelberg,2005:248-263.
[7] Medvidovic N,Dashofy E M,Taylor R N.The role of middleware in architecture-based software development[J].International Journal of Software Engineering and Knowledge Engineering,2003,13(4):367-393.
[8] Rajasree M S,Reddy P K,Janakiram D.Pattern oriented software development: Moving seamlessly from requirements to architecture[C]//Proc. of the 2nd International Software Requirements to Architectures Workshop, 2013:54-60.
[9] Brandozzi M,Perry D E.From goal-oriented requirements to architectural prescriptions: The preskriptor process[C]//Proc. of the 2nd International Software Requirements to Architectures Workshop, 2013: 107-113.
[10] Shao W Z. Object Oriented System Analysis[M]. Beijing: Tsinghua University Press, 2008.
[11] Buhr R J A.Use case maps as architectural entities for complex systems[J].IEEE Transactions on Software Engineering,1998,24(12):1131-1155.
【通聯(lián)編輯:謝媛媛】