Share via


方法: UML モデルからファイルを生成する

UML モデルから、プログラム コード、スキーマ、ドキュメント、リソース、およびその他さまざまな種類の成果物を生成できます。 UML モデルからテキスト ファイルを生成するには、テキスト テンプレートを使用すると便利です。 これを使用すると、生成するテキスト内にプログラム コードを埋め込むことができます。

主に 3 つのシナリオがあります。

  • メニュー コマンドからファイルを生成する (またはジェスチャからファイルを生成する)。 UML モデルで使用できる Visual Studio コマンドを定義します。

  • アプリケーションからファイルを生成する。 UML モデルを読み込み、ファイルを生成するアプリケーションを記述します。

  • 設計時に生成する。 モデルを使用してアプリケーションの機能の一部を定義し、コードやリソースなどを Visual Studio ソリューション内で生成します。

このトピックの最後に、テキスト生成を使用する方法について説明します。 詳細については、「コード生成と T4 テキスト テンプレート」を参照してください。

メニュー コマンドからのファイルの生成

UML メニュー コマンド内で、前処理されたテキスト テンプレートを使用することができます。 テキスト テンプレートのコード内、または別の部分クラス内で、図によって表示されるモデルを読み込むことができます。

これらの機能の詳細については、以下のトピックを参照してください。

次の例に示す方法は、いずれかのモデル図から操作を開始する場合に、単一のモデルからテキストを生成する際に適しています。 別のコンテキストでモデルを処理するには、Visual Studio Modelbus を使用してモデルとその要素にアクセスすることを検討してください。

この例を実行するには、Visual Studio 拡張機能 (VSIX) プロジェクトを生成します。 この例で使用されるプロジェクト名は、VdmGenerator です。 source.extension.vsixmanifest ファイルで [コンテンツの追加] をクリックし、型フィールドを MEF コンポーネントと、現在のプロジェクトを参照するソース パスに設定します。 この種類のプロジェクトを設定する方法の詳細については、「方法: モデリング図にメニュー コマンドを定義する」を参照してください。

プロジェクトに、次のコードを含む 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 プロジェクトの一部になります。 別のファイルに、同じクラスの部分宣言をもう 1 つ追加します。 このコードにより、テンプレートが 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 を使用してモデルを読み込み、前のセクションと同じ方法を使用して、モデルをテキスト テンプレートに渡すことができます。 モデルの読み込みの詳細については、「方法: プログラム コードで UML モデルを読み取る」を参照してください。

設計時のファイルの生成

プロジェクトに UML をコードとして解釈する標準的な方法がある場合は、テキスト テンプレートを生成して、UML モデルからプロジェクト内にコードを生成できます。 通常は、UML モデル プロジェクトと、アプリケーション コード向けに 1 つ以上のプロジェクトを含むソリューションがあります。 各コード プロジェクトには、モデルのコンテンツに基づき、プログラム コード、リソース、および構成ファイルを生成するいくつかのテンプレートが含まれていることがあります。 開発者は、ソリューション エクスプローラーのツール バーにある [すべてのテンプレートの変換] をクリックし、すべてのテンプレートを実行できます。 プログラム コードは、通常、部分クラスの形式で生成されるので、手作業で記述された部分と簡単に統合することができます。

このような Visual Studio プロジェクトはテンプレートの形式で配布できるので、チームのすべてのメンバーが、同じ方法でモデルからコードを生成するプロジェクトを生成することができます。 一般的に、テンプレートは、生成コードの事前条件が満たされていることを確認するために、モデルの検証制約を含む拡張機能パッケージの一部となっています。

ファイルを生成するためのプロシージャ概要

  • プロジェクトにテンプレートを追加するには、[新規ファイルの追加] ダイアログ ボックスの [テキスト テンプレート] をクリックします。 ほとんどのタイプのプロジェクトにテンプレートを追加できますが、モデリング プロジェクトには追加できません。

  • テンプレート ファイルの "カスタム ツール" プロパティは TextTemplatingFileGenerator で、ファイル名の拡張子は .tt です。

  • テンプレートには、少なくとも出力ディレクティブが必要です。

    <#@ output extension=".cs" #>

    プロジェクトの言語に従い、拡張機能フィールドを設定します。

  • テンプレートの生成コードがモデルにアクセスできるようにするには、UML モデルを読み込むために必要なアセンブリに対して <#@ assembly #> ディレクティブを記述します。 ModelingProject.LoadReadOnly() を使用し、モデルを開きます。 詳細については、「方法: プログラム コードで UML モデルを読み取る」を参照してください。

  • テンプレートは、保存されたときと、ソリューション エクスプローラーのツール バーの [すべてのテンプレートの変換] をクリックしたときに実行されます。

  • この種類のテンプレートの詳細については、「T4 テキスト テンプレートを使用したデザイン時コード生成」を参照してください。

  • 一般的なプロジェクトには、同じモデルから異なるファイルを生成するテンプレートがいくつかあります。 どのテンプレートでも、最初の部分は同じです。 このような重複を軽減するために、共通部分を別のテキスト ファイルに移動し、各テンプレートのディレクティブ <#@include file="common.txt"#> を使用して呼び出します。

  • また、テキスト生成処理にパラメーターを指定する、特殊なディレクティブ プロセッサも定義できます。 詳細については、「T4 テキスト変換のカスタマイズ」を参照してください。

この例では、ソース モデルの各 UML クラスに対して C# クラスを生成します。

この例で Visual Studio ソリューションを設定するには

  1. 新しいソリューションのモデリング プロジェクトに、UML クラス図を生成します。

    1. [アーキテクチャ] メニューの [新しい図] をクリックします。

    2. [UML クラス図] をクリックします。

    3. プロンプトに従い、新しいソリューションとモデリング プロジェクトを生成します。

    4. UML クラス ツールをツールボックスからドラッグし、図にクラスをいくつか追加します。

    5. ファイルを保存します。

  2. 同じソリューションに、C# または Visual Basic プロジェクトを生成します。

    • ソリューション エクスプローラーでソリューションを右クリックし、[追加] をポイントして [新しいプロジェクト] をクリックします。 [インストールされたテンプレート][Visual Basic] または [Visual C#] をクリックし、[コンソール アプリケーション] などのプロジェクトの種類を選択します。
  3. C# または Visual Basic プロジェクトにプレーンテキスト ファイルを追加します。 このファイルには、複数のテキスト テンプレートを記述するときに共有されるコードが含まれています。

    • ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] をポイントして、[新しい項目] をクリックします。 [テキスト ファイル] をクリックします。

    次のセクションに示すテキストを挿入します。

  4. C# または Visual Basic プロジェクトにテキスト テンプレート ファイルを追加します。

    • ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] をポイントして、[新しい項目] をクリックします。 [テキスト テンプレート] をクリックします。

    下に示すコードを、テキスト テンプレート ファイルに挿入します。

  5. テキスト テンプレート ファイルを保存します。

  6. 従属ファイルのコードを精査します。 このコードには、モデルの各 UML クラスのクラスが含まれている必要があります。

    1. Visual Basic プロジェクトで、ソリューション エクスプローラーのツール バーの [すべてのファイルの表示] をクリックします。

    2. ソリューション エクスプローラーで、テンプレート ファイル ノードを展開します。

共有テキスト ファイルのコンテンツ

この例では、ファイルの名前は 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 クラス図を使用し、Web サイトの構築や、ナビゲーション スキームの設計を行っています。 各 Web ページはクラスによって表され、アソシエーションがナビゲーション リンクを表します。 開発者は、モデルから Web サイトのコードの大部分を生成します。 それぞれの Web ページは、いくつかのクラスとリソース ファイルのエントリに対応しています。 この方法には、各ページの構築は単一のパターンに準拠するという利点があるので、手入力されたコードよりも信頼性と柔軟性が高くなります。 パターンは、生成するテンプレートにあり、モデルは可変性のある部分を取り込むために使用されます。

  • スキーマ。 Humongous Insurance は、世界中で数千ものシステムを運用しています。 これらのシステムでは、異なるデータベース、言語、およびインターフェイスが使用されています。 中央のアーキテクチャ チームが、業務概念とプロセスのモデルを内部的に公開します。 これらのモデルから、ローカル チームがデータベースの部分を生成し、スキーマやプログラム コードの宣言などをやり取りします。 モデルをグラフィカル表示すると、チームで提案について話し合う際に役立ちます。 チームは、さまざまな業務分野に適用されるモデルのサブセットを示す、複数の図を生成します。 また、変更の対象となる分野を色分けして強調表示します。

成果物を生成するための重要な方法

前の例では、ビジネスによって異なるさまざまな目的でモデルが使用されており、クラスやアクティビティなどのモデリング要素の解釈は、アプリケーションごとに異なります。 モデルから成果物を生成するには、次の方法が便利です。

  • プロファイル。 1 つの業務分野内でも、要素型の解釈が異なることがあります。 たとえば、Web サイト図では、いくつかのクラスが Web ページを表し、他のクラスがコンテンツ ブロックを表すことがあります。 このような違いをユーザーが簡単に記録できるようにするために、ステレオタイプを定義します。 ステレオタイプを使用すると、その種類の要素に適用される追加プロパティをアタッチすることもできます。 ステレオタイプは、プロファイル内にパッケージ化されます。 詳細については、「方法: プロファイルを定義して UML を拡張する」を参照してください。

    テンプレート コードでは、オブジェクトで定義されているステレオタイプに簡単にアクセスすることができます。 次に例を示します。

    public bool HasStereotype(IClass c, string profile, string stereo)
    { return c.AppliedStereotypes.Any
       (s => s.Profile == profile && s.Name == stereo ); }
    
  • 制約されたモデル。 生成できるすべてのモデルが、どの目的にも有効であるとは限りません。 たとえば、Fabrikam の空港の手荷物取り扱いモデルでは、チェックイン デスクに出発荷物用のコンベヤが必要です。 ユーザーがこのような制約を確認できるよう、検証関数を定義できます。 詳細については、「方法: UML モデルの検証制約を定義する」を参照してください。

  • 手動での変更を保持する。 モデルからは、すべてのソリューション ファイルを生成できるわけではありません。 ほとんどの場合は、生成されたコンテンツを手動で追加、または調整できるようにする必要があります。 ただし、このような手動での変更は、テンプレート変換が再実行されたときに維持されることが重要です。

    テンプレートが .NET 言語でコードを生成する場合は、開発者がメソッドとコードを追加できるよう、部分クラスを生成する必要があります。 また、各クラスを、メソッドを含む抽象基本クラス、およびコンストラクターのみを含む継承クラスのペアとして生成すると便利です。 これにより、開発者はメソッドをオーバーライドできます。 初期化をオーバーライドするには、コンストラクターではなく、別のメソッドで行います。

    テンプレートが XML およびその他の種類の出力を生成する場合は、手動コンテンツを生成されたコンテンツとは別に保持することがより困難になる可能性があります。 1 つの方法としては、2 つのファイルを組み合わせるビルド処理にタスクを生成します。 また、開発者が、生成されるテンプレートのローカル コピーを調整するという方法もあります。

  • 別のアセンブリにコードを移動する。 テンプレートに含めるコードの本体は、大きくしすぎないでください。 生成されたコンテンツは、計算とは別にしておいてください。テキスト テンプレートでは、コードの編集は完全にサポートされていません。

    代わりに、多くの計算を実行してテキストを生成する必要がある場合は、これらの関数を別のアセンブリに構築し、テンプレートからメソッドを呼び出します。