HOW TO:從 UML 模型產生檔案
從 UML 模型可以產生程式碼、結構描述、文件、資源和任何類型的其他成品。 從 UML 模型產生文字檔的便利方法之一,是使用文字範本。 這些方法可讓您將程式碼內嵌到您要產生的文字中。
主要情節共有三種:
從功能表命令或軌跡產生檔案。 您可以定義可在 UML 模型上使用的 Visual Studio 命令。
從應用程式產生檔案。 您可以撰寫可讀取 UML 模型及產生檔案的應用程式。
在設計階段產生。 您可以使用模型來定義某些應用程式功能,並在您的 Visual Studio 方案中產生程式碼、資源等項目。
本主題的末段將討論如何使用文字產生。 如需詳細資訊,請參閱程式碼產生和 T4 文字範本。
從功能表命令產生檔案
您可以使用 UML 功能表命令中的前置處理文字範本。 在文字範本的程式碼內,或是個別部分類別中,您可以讀取由圖表所檢視的模型。
如需這些功能的詳細資訊,請閱讀下列主題:
當您從其中一個模型圖表啟始作業時,下列範例所示範的方法可讓您從單一模型產生文字。 若要處理個別內容中的模型,請考慮使用 Visual Studio Modelbus 來存取模型及其項目。
範例
若要執行此範例,請建立 Visual Studio Extension (VSIX) 專案。 此範例中使用的專案名稱為 VdmGenerator。 在 source.extension.vsixmanifest 檔案中按一下 [加入內容],然後將類型欄位設為 [MEF 元件] 以及參考目前專案的來源路徑。 如需如何設定此專案類型的詳細資訊,請參閱HOW TO:在模型圖表上定義功能表命令。
在專案中加入包含下列程式碼的 C# 檔案。 此類別會定義將出現在 UML 類別圖表上的功能表命令。
using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
namespace VdmGenerator
{
[Export(typeof(ICommandExtension))]
[ClassDesignerExtension]
public class GenerateVdmFromClasses : ICommandExtension
{
[Import] public IDiagramContext DiagramContext { get; set; }
public void Execute(IMenuCommand command)
{
// Initialize the template with the Model Store.
VdmGen generator = new VdmGen(
DiagramContext.CurrentDiagram.ModelStore);
// Generate the text and write it.
System.IO.File.WriteAllText
(System.IO.Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.Desktop),
"Generated.txt")
, generator.TransformText());
}
public void QueryStatus(IMenuCommand command)
{
command.Enabled = command.Visible = true;
}
public string Text
{ get { return "Generate VDM"; } }
}
}
下列檔案為文字範本。 它會為模型中的每個 UML 類別產生一行文字,並為每個類別中的各個屬性產生一行文字。 用以讀取模型的程式碼會內嵌在文字中,以 <# ... #> 分隔。
若要建立此檔案,請以滑鼠右鍵按一下 [方案總管] 中的專案,指向 [加入],然後按一下 [新增項目]。 選取 [前置處理過的文字範本]。 此範例的檔案名稱應為 VdmGen.tt。 此檔案的 [自訂工具] 屬性應為 [TextTemplatingFilePreprocessor]。 如需前置處理文字範本的詳細資訊,請參閱使用前置處理過的 T4 文字範本在執行階段產生文字。
<#@ import namespace="Microsoft.VisualStudio.Uml.Classes" #>
<#
foreach (IClass classElement in store.AllInstances<IClass>())
{
#>
Type <#= classElement.Name #> ::
<#
foreach (IProperty attribute in classElement.OwnedAttributes)
{
#>
<#= attribute.Name #> : <#=
attribute.Type == null ? ""
: attribute.Type.Name #>
<#
}
}
#>
文字範本會產生 C# 部分類別,這會成為您 Visual Studio 專案的一部分。 在個別檔案中,加入相同類別的其他 partial 修飾詞。 此程式碼會為範本提供 UML 模型存放區的存取權:
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
namespace VdmGenerator
{
public partial class VdmGen
{
private IModelStore store;
public VdmGen(IModelStore s)
{ store = s; }
}
}
若要測試專案,請按 F5。 Visual Studio 的新執行個體隨即啟動。 在此執行個體中,開啟或建立包含類別圖表的 UML 模型。 將部分類別加入至圖表,並將部分屬性加入至每個類別。 在圖表中按一下滑鼠右鍵,然後按一下範例命令 [Generate VDM]。 此命令會建立檔案 C:\Generated.txt。 檢查此檔案。 其內容應類似於下列文字,但同時會列出您自己的類別和屬性:
Type Class1 ::
Attribute1 : int
Attribute2 : string
Type Class2 ::
Attribute3 : string
從應用程式產生檔案
您可以從可讀取 UML 模型的應用程式來產生檔案。 執行此作業時,使用 Visual Studio Modelbus 存取模型及其項目,是最有彈性而建全的方法。
您也可以使用基本 API 來載入模型,並使用上一節所說明的技術將模型傳遞至文字範本。 如需載入模型的詳細資訊,請參閱 HOW TO:讀取程式碼中的 UML 模型。
在設計階段產生檔案
如果您的專案具有將 UML 解譯成程式碼的標準方法,您可以建立文字範本,以便在您的專案中從 UML 模型產生程式碼。 您通常會有一個包含 UML 模型專案的方案,以及應用程式程式碼的一個或多個專案。 每個程式碼專案都可能包含數個會產生程式碼、資源和組態檔的範本,視模型的內容而定。 開發人員只要按一下 [方案總管] 工具列中的 [轉換所有範本],即可執行所有範本。 程式碼通常會以部分類別的形式產生,以便整合手動撰寫的部分。
此類型的 Visual Studio 專案可透過範本形式散發,而使每個小組成員皆可以相同方式建立從模型產生程式碼的專案。 範本通常是擴充套件的一部分,其中包含模型的驗證條件約束,以確保能夠符合產生程式碼的前置條件。
產生檔案的概略程序
若要在專案中加入範本,請在 [加入新檔案] 對話方塊中選取 [文字範本]。 大部分的專案類型都可加入範本,但模型專案則否。
範本檔的 [自訂工具] 屬性應為 TextTemplatingFileGenerator,副檔名應為 .tt。
範本至少應有一個輸出指示詞:
<#@ output extension=".cs" #>
請根據專案的語言來設定副檔名欄位。
若要讓您的範本所產生的程式碼能夠存取模型,請撰寫組件讀取 UML 模型所需的 <#@ assembly #> 指示詞。 請使用 ModelingProject.LoadReadOnly() 開啟模型。 如需詳細資訊,請參閱 HOW TO:讀取程式碼中的 UML 模型。
範本會在您儲存時以及按一下 [方案總管] 工具列中的 [轉換所有範本] 時執行。
如需此範本類型的詳細資訊,請參閱使用 T4 文字範本在設計階段產生程式碼。
在一般專案中,您會有數個可從相同模型產生不同檔案的範本。 每個範本的第一個部分都會相同。 若要減少此重複性,請將通用部分移至個別的文字檔中,然後使用每個範本中的指示詞 <#@include file="common.txt"#> 加以叫用。
您也可以定義特製化的指示詞處理器,以便將參數提供給文字產生程序。 如需詳細資訊,請參閱自訂 T4 文字轉換。
範例
此範例會為來源模型中的每個 UML 類別產生一個 C# 類別。
若要設定此範例的 Visual Studio 方案
在新的方案中,建立模型專案中的 UML 類別圖表。
按一下 [架構] 功能表中的 [新增圖表]。
選取 [UML 類別圖表]。
依照提示建立新的方案和模型專案。
從工具箱中拖曳 [UML 類別] 工具,以將部分類別加入至圖表。
儲存檔案。
在相同的方案中建立 C# 或 Visual Basic 專案。
- 在 [方案總管] 中,以滑鼠右鍵按一下方案、指向 [加入],然後按一下 [新增專案]。 在 [已安裝的範本] 下方按一下 [Visual Basic] 或 [Visual C#],然後選取專案類型,如 [主控台應用程式]。
將純文字檔加入至 C# 或 Visual Basic 專案。 此檔案將會包含您要撰寫數個文字範本時所共用的程式碼。
- 在 [方案總管] 中,以滑鼠右鍵按一下專案,並指向 [加入],然後按一下 [新增項目]。 選取 [文字檔]。
插入下列區段中顯示的文字。
將文字範本檔加入至 C# 或 Visual Basic 專案。
- 在 [方案總管] 中,以滑鼠右鍵按一下專案,並指向 [加入],然後按一下 [新增項目]。 選取 [文字範本]。
將後續的程式碼插入文字範本檔中。
儲存文字範本檔。
檢查附屬檔案中的程式碼。 其中應包含模型中每個 UML 類別的類別。
在 Visual Basic 專案中,按一下 [方案總管] 工具列中的 [顯示所有檔案]。
展開 [方案總管] 中的範本檔節點。
共用文字檔的內容
在此範例中,此檔案的名稱是 SharedTemplateCode.txt,其所在資料夾與文字範本相同。
<# /* Common material for inclusion in my model templates */ #>
<# /* hostspecific allows access to the Visual Studio API */ #>
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="Microsoft.VisualStudio.Uml.Interfaces.dll"#>
<#@ assembly name="Microsoft.VisualStudio.ArchitectureTools.Extensibility.dll"#>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.Uml.Classes" #>
<#@ import namespace="Microsoft.VisualStudio.ArchitectureTools.Extensibility" #>
<#@ import namespace="Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml" #>
<#+ // Note this is a Class Feature Block
///<summary>
/// Text templates are run in a common AppDomain, so
/// we can cache the model store that we find.
///</summary>
private IModelStore StoreCache
{
get { return AppDomain.CurrentDomain.GetData("ModelStore") as IModelStore; }
set { AppDomain.CurrentDomain.SetData("ModelStore", value); }
}
private bool CacheIsOld()
{
DateTime? dt = AppDomain.CurrentDomain
.GetData("latestAccessTime") as DateTime?;
DateTime t = dt.HasValue ? dt.Value : new DateTime();
DateTime now = DateTime.Now;
AppDomain.CurrentDomain.SetData("latestAccessTime", now);
return now.Subtract(t).Seconds > 3;
}
///<summary>
/// Find the UML modeling project in this solution,
/// and load the model.
///</summary>
private IModelStore ModelStore
{
get
{
// Avoid loading the model for every template:
if (StoreCache == null || CacheIsOld())
{
// Use Visual Studio API to find modeling project:
EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
.GetService(typeof(EnvDTE.DTE));
EnvDTE.Project project = null;
foreach (EnvDTE.Project p in dte.Solution.Projects)
{
if (p.FullName.EndsWith(".modelproj"))
{
project = p;
break;
}
}
if (project == null) return null;
// Load UML model into this AppDomain
// and access model store:
IModelingProjectReader reader =
ModelingProject.LoadReadOnly(project.FullName);
StoreCache = reader.Store;
}
return StoreCache;
}
}
#>
文字範本檔的內容
.tt 檔案中會有下列文字。 此範例會從模型中的 UML 類別,產生 C# 檔案的類別。 但您可以產生任何類型的檔案。 產生的檔案所使用的語言,與撰寫文字範本程式碼的語言無關。
<#@include file="SharedTemplateCode.txt"#>
<#@ output extension=".cs" #>
namespace Test
{
<#
foreach (IClass c in ModelStore.AllInstances<IClass>())
{
#>
public partial class <#=c.Name#>
{ }
<#
}
#>
}
如何使用文字產生
當您使用模型在需求或架構的層級上進行設計時,才是模型真正發揮功能的時候。 您可以將文字範本運用在某些將高階概念轉換成程式碼的工作上。 在許多情況下,這都無法達成 UML 模型中的項目與程式碼的類別或其他組件之間的一對一對應。
此外,轉換情形也會隨您的問題網域而不同;模型與程式碼之間並沒有通用的對應。
以下是從模型產生程式碼的一些範例:
產品線。 Fabrikam, Inc. 是一家建置及安裝機場行李處理系統的公司。 許多軟體的安裝流程都十分類似,但軟體組態則須取決於安裝的行李處理機器,以及輸送帶連結這些組件的方式。 在合約開始時,Fabrikam 的分析人員首先討論機場管理的需求,然後使用 UML 活動圖表勾勒出硬體計劃。 開發小組透過此模型產生了組態檔、程式碼、計劃和使用者文件。 他們對程式碼進行手動附加和調整,完成工作。 他們藉由工作累積經驗,進而擴充產生的內容範圍。
模式。 Contoso, Ltd 的開發人員經常建置網站,並使用 UML 類別圖表設計巡覽配置。 每個網頁各由一個類別所代表,而關聯性則代表巡覽連結。 開發人員從模型產生了網站的許多程式碼。 每個網頁各對應於數個類別和資源檔項目。 此方法的好處是,每個網頁的建構均符合單一模式,因而能達到優於手寫程式碼的可靠性和彈性。 模式係用以產生範本,模型則用以制定可變層面。
結構描述。 Humongous Insurance 在全球各地擁有數以千計的系統。 這些系統使用不同的資料庫、語言和介面。 中央架構小組負責從內部發行企業概念和流程的模型。 地區小組可利用這些模型,產生其資料庫與交換結構描述的組件、程式碼中的宣告等項目。 模型的圖形表示可協助小組討論相關提案。 小組可建立多份圖表,以顯示適用於不同企業區域之模型的子集。 他們也可使用不同的顏色凸顯可能會變更的區域。
重要的成品產生技術
在前幾個範例中,模型運用在各種隨企業類型而變動的用途上,且模型項目 (如類別和活動) 的定義皆隨著應用程式而不同。 以下是您從模型產生成品時有用的技術。
設定檔。 即使在單一企業區域中,項目型別的解譯也可能會變動。 以網站圖表為例,有些類別可能代表網頁,有些則代表內容區塊。 為了方便使用者記下這些不同之處,請定義造型。 造型也可讓您附加適用於該類型項目的其他屬性。 造型可封裝在設定檔中。 如需詳細資訊,請參閱HOW TO:定義要擴充 UML 的設定檔。
在範本程式碼中,您可以輕鬆地存取對物件定義的造型。 例如:
public bool HasStereotype(IClass c, string profile, string stereo) { return c.AppliedStereotypes.Any (s => s.Profile == profile && s.Name == stereo ); }
具有條件約束的模型。 您所能建立的模型,並非全都適用於各種用途。 以 Fabrikam 的機場行李模型為例,有登機登記櫃台而沒有連外輸送帶,就是錯誤的模型。 您可以定義驗證函式,以協助使用者檢視這些條件約束。 如需詳細資訊,請參閱HOW TO:定義 UML 模型的驗證條件約束。
保留手動變更。 只有部分方案檔可從模型產生。 在大部分的情況下,您都必須能夠手動加入或調整產生的內容。 但在重新執行範本轉換時,保留這些手動變更就顯得十分重要了。
若您的範本是以 .NET 語言產生程式碼,則應產生部分類別,讓開發人員能夠加入方法和程式碼。 以成對的形式產生各類別也有其效用:包含方法的抽象基底類別,和僅包含建構函式的繼承類別。 這可讓開發人員覆寫方法。 若要能夠覆寫初始設定,則應在個別方法中執行覆寫,而不是在建構函式中。
若範本產生了 XML 和其他類型的輸出,要將手動內容保留在產生的內容以外,可能會難得多。 其中一種方法是在建置流程中建立會結合兩個檔案的工作。 另一個方法是讓開發人員調整產生範本的本機複本。
將程式碼移至個別組件中。 建議您不要在範本中撰寫龐大的程式碼主體。 最好讓產生的內容與計算分開,而文字範本並不充分支援程式碼編輯。
如果您必須執行大量計算以產生文字,請在個別的組件中建置這些函式,並從範本呼叫其方法。