在Rational Software Architect或Rational Software Modeler中使用JET2實現模型驅動架構
簡介:本文向讀者簡短地介紹了模型驅動架構(MDA),以及能將 UML 建模帶到更高層次的 JET2 技 術,也就是基於 Eclipse 的 Java Emitter Template 技術。您可以使用這個模板來將模型轉換成有用的 工件。
引言
讓我們來探討一下,這種改進會帶來什麼好處嗎?
—Beach Boys
如果您就要得到一個項目的初始需求文件,或者能夠精確地估計並 設計進而開發,會帶來什麼好處呢?本文告訴您怎樣將一個在 IBM®Rational®Software Architect 中創建的統一建模語言(UML),轉化成一個動態估計和回饋的引擎。感謝在模型驅動架構 (MDA)、設計、靜態非描述性圖形、模型及圖表方面的巨大進步,現在是該執行它們的時候了。 Rational Software Architect 和 IBM®Rational®Software Modeler 是 Java Emitter Templates (JET)及 UML 建模的主要推動者。本文試著超越傳統的代碼生成技術以產生更加有用的系統 ,例如估計系統。
本文中的 JET 項目描述了以下的任務:
在需求與分析階段進行估計
在設計階段將模型 當做成熟的形式估計
生成對模型的回饋
模型提供了一個深刻老到的觀點,以了解怎樣通過 注釋性的 UML 表達業務問題。一個典型的需求收集包含了以下這些構件:
需求與業務行為通過用 例及交流來表達
活動圖顯示的進程及目標建模
通過結構化的模型表達的信息,例如 Class 和 Component 模型
模型隨著時間的增長而不斷地發展和成熟。一個從需求開始的典型模型可能還 會包括結構化的表達交流的分類模型。一個用例模型就是啟動估計過程的完美場。
關於 JET
Eclipse Modeling Framework(EMF),是 Rational Software Architect 的一個完整部分, Rational Software Modeler 包含了生成源代碼與文件的強大工具。其中有一款工具就是 JET (Java Emitter Templates)。有了 JET,您就可以使用像 JavaServer™Pages (JSP)這樣的語法來書寫 模板並運行它們。
前提條件
本文面向的讀者是那些對 Rational Software Architect 有 著詳細了解的人群。作者所做的一個假設是讀者能夠理解 XML、Java™技術,以及 XPATH 腳本語法 以及語義。
首個轉換:來自用例的項目估計
本文的一部分基於 Capers Jones 在其著作中 所描述的發現,這本書就是《估計軟件成本:將成本估計帶入現實主義》,第二版。您可以查看這本書的 第 7 章,“來自敏捷項目及新環境的手動估計方法”。他論述一個用例代表了大約 35 個功 能點的平均值,35 只是一個大概值,通常會在 15 及 75 個功能點之間進行變動。1500 個功能點的典型 使用(大約 75000 個 Java 聲明),大約需要 42 個用例圖 。單個用例基本上定義了大約 1785 個 Java 聲明的使用模式。
用例估計是 1998-2001 中的基線。大多數的思想領導權來自 1998 年 Schneider 和 Winters 以及 2001 Alistair Cockburn 。本文假設有一個項目團隊想要使用用例點來估計,這些用例點書寫了目標層 次上的用例。因此每一個用例都有一個目標。用戶目標層次上用例的目標,與業務價值的一個單元相類似 。
用例點是角色總體數量、關系以及用例方法學其他方面的集合體,該用例方法學指示了估計的 項目的值,因此組成了 JET2 估計引擎的基礎。在決定用例點的規模以後,就可以投入精力與日程安排的 規則了。
角色與用例的復雜性
每一個用例都代表 35 個功能點的平均值。圖 1 顯示了一 個在 Rational Software Architect 中創建和概述的簡單項目。
圖 1. 用例的范例
您可以使用表 1 來決定用例與角色未經調整的權重值。
表 1. 計算未經調整的用例點 (角色)
角色類型 描述 權衡因素 角色的數量 總體 簡單的 定義良好 API 的外部系統 1 WF * Number 平均的 使用基於協議界面的外部系統:HTTP 或者 TCP/IP 或者數據庫 2 WF * Number 復雜的 人 3 WF * Number 未經調整的角色權衡 總體表 2. 計算未經調整的用例點(用 例)
用例類型 描述 權衡因素 用例的數量 總體 簡單的 1-3 交易 5 WF * Number 平均的 4-7 交易 10 WF * Number 復雜的 最少 7 個交易 15 WF * Number 未經調整的用例權重 總體使用該表來分析一個用例,角色與用例根據主鍵值來進行定型:簡單的,平 均的,或者復雜的。
必須創建一個單獨的 JET 項目以運行 JET 轉換。從 New Project 向導開始 :
從主菜單中,選擇 File > New > Project。
在 JET Transformations 向導中, 選擇 JET Transformation Project。
輸入一個項目名。例如,使用 Estimation。
點擊 Finish。
創建的 JET 含有包含兩個文件的一個模板:
main.jet
dump.jet
因為項目將會處理不同類型的估計,創建兩個單獨的 JET 模板:一個為用例 點分析創建,另一個為類點分析創建。
不同類型的角色與用例的統計包含在 Java 腳本集部分中 :<% %>
代碼會掃描搜索模型中的用例與角色。當代碼在模型中找到它們時,它會檢查構造 型鍵值,並增加相應的遭遇次數,如代碼行 1 所示。
代碼 1. 用例點分析
<%--
// Initialize Variables.
--%>
<%
int countUCComplex = 0;
int countUCAverage = 0;
int countUCSimple = 0;
int countActorComplex = 0;
int countActorAverage = 0;
int countActorSimple = 0;
%>
<%--
// Get the name of the model
--%>
<c:setVariable var="model" select="/Model" />
Project Estimates for Model: <c:get select="$model/@name" />
<% --
// iterate through all packages
--%>
<c:iterate select="//Package" var="package" >
<c:if test = "//Package[self::Package]" >
Packages: <c:get select="$package/@name" />
<c:setVariable select="$package" var="source"/>
<%--
// iterate through all use cases in the package
--%>
<c:iterate select="$package/packagedElement[self::UseCase]" var="useCase" >
Use Case: <c:get select = "$useCase/@name" />
<%-- get the stereotypes --% >
<c:iterate select="$useCase/eAnnotations" var="stype" >
<c:iterate select="$stype/details" var="details" >
<c:setVariable select="$details/@key" var="key"/>
Type: <c:get select = "$key" />
<c:choose>
<c:when test = "$key = 'complex'">
<% ++countUCComplex; %>
</c:when>
<c:when test = "$key = 'average'">
<% ++countUCAverage; %>
</c:when>
<c:otherwise>
<% ++countUCSimple; %>
</c:otherwise>
</c:choose>
</c:iterate>
</c:iterate>
</c:iterate>
<%--
// Iterate through all use case actors in the package
--%>
<c:iterate select="$package/packagedElement[self::Actor]" var="actor" >
Actor: <c:get select = "$actor/@name" />
<%-- get the stereotypes --%>
<c:iterate select="$actor/eAnnotations" var="stype" >
<c:iterate select="$stype/details" var="details" >
<c:setVariable select="$details/@key" var="key"/>
Type: <c:get select = "$key" />
<c:choose>
<c:when test = "$key = 'complex'">
<% ++countActorComplex; %>
</c:when>
<c:when test = "$key = 'average'">
<% ++countActorAverage; %>
</c:when>
<c:otherwise>
<% ++countActorSimple; % >
</c:otherwise>
</c:choose>
</c:iterate>
</c:iterate>
</c:iterate>
</c:if>
</c:iterate>
**Totals**
Simple Use Cases: <%= countUCSimple %>, UC Weight: <%= countUCSimple*5 %>
Average Use Cases: <%= countUCAverage %>, UC Weight: <%= countUCAverage*10 %>
Complex Use Cases: <%= countUCComplex %>, UC Weight: <%= countUCComplex*15 %>
Simple Actors: <%= countActorSimple %>, Actor Weight: <%= countActorSimple*1 %>
Average Actors: <%= countActorAverage %>, Actor Weight: <%= countActorAverage*2 %>
Complex Actors: <%= countActorComplex %>, Actor Weight: <%= countActorComplex*3 %>
計算全部未經調整點及個人時間
全部未經調整點是所有個人部分的總結:
未經調整的用例 點(UUCP) = 未經調整的角色權重(UAW) + 未經調整的用例權重(UUCW),因此:
UUCP = UAW + UUCW
為了計算個人時間,您必須計算技術方面的復雜性及環境,如代碼行 2 所示。
清 單 2. 計算未經調整的用例點
<% int totalWeight = countUCSimple*5 +
countUCAverage*10 +
countUCComplex*15 +
countActorSimple*1 +
countActorAverage*2 +
countActorComplex*3;
%>
Unadjusted Use Case Points = <%= totalWeight %>
假設您安裝了 Technical Complexity Factor 1.075 版本、Experience Factor 0.545 版本及 Experience / Stability Index 7 版本,那麼 現在您就可以按照代碼行 3 中所示的那樣計算個人時間。
清單 3. 未調整的個人時間
<% double personHours = (20 * totalWeight) * 1.075 * 0.545; % >
Unadjusted Person Hours = <%= personHours %>
您可以將 Person Hours 分配給不同的 IBM®Rational Unified Process®(RUP®)階段,如代碼行 4 中的范例所示。
清單 4. 分配給不同的階段
Project Life Cycle
Inception (5%) = <%= personHours*0.05 %> Hours
Elaboration (20%) = <%= personHours*0.2 %> Hours
Construction (65%) = <%= personHours*0.65 %> Hours
Transition (10%) = <%= personHours*0.1 %> Hours
擴展的轉換:類點分析
類點方法提供了系統層次的估計。Class Point 方法 的基本想法最初是由 Costagliola、Ferrucci, Tortora 和 G. Vitiello 提出的。提出這種方法的目的 是,提供一種方法我們可以用來在開發的階段不斷地精化估計,並利用新的信息直到它們可用為止。
因為功能點需要不斷地處理包含用例的規格,所以還可以從中得到一些初步的數據。
用戶 類的識別與歸類
在類點統計的第一步期間,會分析設計規格以識別類並為它們歸類。
決定 類的復雜性
External Methods 告訴您了類界面的規模。這個規模由本地定義公共方法的數量決定 。服務請求提供了不同系統工件聯系的評價方法。它同樣適用於單個類,並由其他類請求的不同服務的數 量決定。
計算總的未調整類點
Class 模型及 Interaction 圖回溯至前面描述的用例,如 圖 2 和圖 3 所示。
圖 2. Class 模型
圖 3. Interaction 圖
表 3. 所請求外部方 法及服務的復雜性及權衡
權衡 復雜性 最多 8 個外 部方法及單個服務請求 簡單的 最少 8 個的單個服務請求 平均的 最多 4 個服務請求上最多 4 個的外部方法 簡單的 最 多 4個服務請求上 5 個到 8 個的外部方法 平均的 最多 4 個服務請求上最 少 8 個外部 方法 高的 4 個或者更多服務請求上最少 4 個外部方法 平均的 所有其他的 高的
接下來的一 步涉及到了從以上描述的各種模型(用例、類模型及顯示信息流的交流圖)中進行的計算。JET 模板中的 代碼行 5 負責進行這項工作。它實現了一個 Java 扇區以存儲需要的數據。文中還描述了在模板和 Java 代碼之間共享數據的一些非常有意思的方法。如代碼行 5 所示,代碼首先通過了 Class 模型,然後通過 了 Interaction 模型,隨後又計算外部方法和服務請求 。
清單 5. 類點分析
<%@jet imports="java.util.*" %>
<%
class Additions {
public String className;
public int nem;
public int nsr;
public String lifeLine;
}
java.util.Vector modelClass = new java.util.Vector();
java.util.Vector l = new java.util.Vector();
% >
<c:setVariable select="/Model" var="modelName" />
Model Name: <c:get select="$modelName/@name" />
<c:iterate select="//Package" var="package">
Package Name: <c:get select="$package/@name" />
<%-- Iteration through all classes --%>
<c:iterate select="$package/packagedElement[self::Class]" var="class" >
Class: <c:get select="$class/@name" />
<c:setVariable var="clName" select="$class/@name" />
<% int nems = 0; %>
<c:iterate select="$class/ownedOperation" var="oper" >
Operation: <c:get select="$oper/@name" />
Visibility: <c:get select = "$oper/@visibility" />
<c:if test="$oper/@visibility='public'" >
<% ++nems; %>
</c:if>
</c:iterate>
<c:if test="$class/ownedOperation">
<%
Additions a = new Additions();
a.className = org.eclipse.jet.xpath.XPathUtil.xpathString
(context.getVariable("clName"));
a.nem = nems;
modelClass.addElement (a);
%>
</c:if>
</c:iterate>
<%-- Iteration through all Interactions --%>
<c:iterate select="$package/packagedElement [self::Collaboration]" var="collab" >
Collaboration: <c:get select="$collab/@name" />
<c:iterate select="$collab/ownedBehavior [self::Interaction]" var="interact" >
Interaction: <c:get select="$interact/@name" />
<c:iterate select="$interact/lifeline" var="lline" >
<c:setVariable var="llName" select="$lline/@name" />
Lifeline: <c:get select="$lline/@name" />
Represents: <c:get select="$lline/represents/@name" />,
<c:get select="$lline/represents/type/@name" />
<c:setVariable var="repClass" select="$lline/represents/type/@name" />
<%
boolean notFound = true;
for (int i = 0; i < modelClass.size(); i++) {
Additions a = (Additions) modelClass.elementAt(i);
String repClassName = org.eclipse.jet.xpath.XPathUtil.xpathString
(context.getVariable("repClass"));
if (repClassName.equals(a.className)) {
a.lifeLine = org.eclipse.jet.xpath.XPathUtil.xpathString
(context.getVariable("llName"));
modelClass.setElementAt(a, i);
notFound = false;
}
}
if (notFound) {
Additions a = new Additions();
a.className = org.eclipse.jet.xpath.XPathUtil.xpathString
(context.getVariable ("repClass"));
a.lifeLine = org.eclipse.jet.xpath.XPathUtil.xpathString
(context.getVariable("llName"));
modelClass.addElement(a);
}
%>
</c:iterate>
<c:iterate select="$interact/fragment" var="frag" >
Fragment (covered): <c:get select="$frag/covered/@name" />
<c:setVariable var="llname" select="$frag/covered/@name" />
<c:if test="$frag/message">
Message: <c:get select="$frag/message/@name" />
Type: <c:get select="$frag/message/@messageSort" />
<c:if test="$frag/message/@messageSort='synchCall'">
<%
String lifeLine = null;
for (int i = 0; i < modelClass.size(); i++) {
Additions a = (Additions) modelClass.elementAt(i);
lifeLine = org.eclipse.jet.xpath.XPathUtil.xpathString
(context.getVariable("llname"));
if (lifeLine.equals(a.lifeLine)) {
++a.nsr;
modelClass.setElementAt(a, i);
}
}
%>
</c:if>
</c:if>
<c:if test="$frag/start">
Start: <c:get select="$frag/start/message/@name" />
</c:if>
<c:if test="$frag/finish">
Finish: <c:get select="$frag/finish/message/@name" />
</c:if>
</c:iterate>
<c:iterate select="$interact/message" var="msg" >
Message: <c:get select="$msg/@name" />
Receive Event: <c:get select="$msg/receiveEvent/message/@name" />
Send Event: <c:get select="$msg/sendEvent/message/@name" />
</c:iterate>
</c:iterate>
</c:iterate>
</c:iterate>
Totals:
<%
int countSimple = 0;
int countAverage = 0;
int countComplex = 0;
for (java.util.Enumeration e = modelClass.elements(); e.hasMoreElements();) {
Additions a = (Additions) e.nextElement();
%>
Class: <%= a.className %>, NEMS <%= a.nem %>, Lifeline <%= a.lifeLine %>,
NSR <%= a.nsr %>
<%
if ((a.nem <= 8) && (a.nsr <= 1)) ++countSimple;
else if ((a.nem <= 4) && (a.nsr >= 2) && (a.nsr <= 3)) ++countSimple;
else if ((a.nem <= 4) && (a.nsr > 3)) ++countAverage;
else if ((a.nem >= 5) && (a.nem <= 8) && (a.nsr >= 2) && (a.nsr <= 3)) ++countAverage;
else if ((a.nem > 8) && (a.nsr >= 2) && (a.nsr <= 3)) ++countAverage;
else ++countComplex;
}
% >
Counts:
Simple: <%= countSimple %>
Average: <%= countAverage %>
Complex: <%= countComplex %>
<%
int tucp = (countSimple * 5) + (countAverage * 8) + (countComplex * 13);
% >
TUCP (Total Unadjusted Class Points) = <%= tucp %>
</c:log>
在您知道全部的未調整點之後,就很容易添加其他的因素以達到未經調 整的私人時間,就像您對用例點分析所做的那樣。
總結及其他的選項
本文帶著您浏覽了 UML 模型的一些關鍵區域,以及怎樣獲取、轉化以及創建添加至智能報告的工件。您可以使用 BIRT (業 務智能和報告)來做相似的獲取工作。