Partager via


Comment : générer des fichiers à partir d'un modèle UML

À partir d'un modèle UML, vous pouvez générer un code de programme, des schémas, des documents, des ressources et d'autres artefacts de tout genre. Une méthode commode permettant de générer des fichiers texte à partir d'un modèle UML consiste à utiliser des modèles de texte. Ces derniers vous permettent d'incorporer un code de programme dans le texte que vous souhaitez générer.

Il existe trois scénarios principaux :

  • Génération de fichiers à partir d'une commande de menu ou d'un mouvement. Vous définissez une commande Visual Studio qui est disponible sur les modèles UML.

  • Génération de fichiers à partir d'une application. Vous écrivez une application qui lit des modèles UML et génère des fichiers.

  • Génération au moment du design. Vous utilisez un modèle pour définir certaines fonctionnalités de votre application et générer du code, des ressources, etc., dans votre solution Visual Studio.

Cette rubrique se termine par une discussion sur la manière d'utiliser la génération de texte. Pour plus d'informations, consultez Génération de code et modèles de texte T4.

Génération de fichiers à partir d'une commande de menu

Vous pouvez utiliser des modèles de texte de prétraitement dans une commande de menu UML. Dans le code du modèle de texte, ou dans une classe partielle distincte, vous pouvez lire le modèle affiché par le diagramme.

Pour plus d'informations sur ces fonctionnalités, lisez les rubriques suivantes :

L'approche illustrée dans l'exemple suivant est appropriée pour la génération de texte à partir d'un modèle unique, lorsque vous initiez l'opération à partir de l'un des diagrammes de modèle. Pour traiter un modèle dans un contexte distinct, envisagez d'utiliser Visual Studio Modelbus pour accéder au modèle et à ses éléments.

Exemple

Pour exécuter cet exemple, créez un projet d'extension Visual Studio (VSIX). Le nom du projet utilisé dans cet exemple est VdmGenerator. Dans le fichier source.extension.vsixmanifest, cliquez sur Ajouter du contenu et affectez au champ de type la valeur Composant MEF et le chemin source référençant le projet actuel. Pour plus d'informations sur la manière de configurer ce type de projet, consultez Comment : définir une commande de menu sur un diagramme de modélisation.

Ajoutez au projet un fichier C# qui contient le code ci-dessous. Cette classe définit une commande de menu qui apparaîtra sur un diagramme de classes 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"; } }
  }
}

Le fichier suivant est le modèle de texte. Il génère une ligne de texte pour chaque classe UML dans le modèle et une ligne pour chaque attribut dans chaque classe. Le code pour la lecture du modèle est incorporé dans le texte, délimité par <# ... #>.

Pour créer ce fichier, dans l'Explorateur de solutions, cliquez avec le bouton droit sur le projet, pointez sur Ajouter, puis cliquez sur Nouvel élément. Sélectionnez Modèle de texte prétraité. Pour cet exemple, le nom de fichier doit être VdmGen.tt. La propriété Outil personnalisé du fichier doit avoir la valeur TextTemplatingFilePreprocessor. Pour plus d'informations sur les modèles de texte prétraités, consultez Génération de texte durant l'exécution à l'aide des modèles de texte 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 #> 
<#
     } 
   }
#>

Le modèle de texte génère une classe partielle C#, qui devient partie intégrante de votre projet Visual Studio. Dans un fichier distinct, ajoutez une autre déclaration partielle de la même classe. Ce code fournit le modèle avec l'accès au magasin de modèles UML :

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
namespace VdmGenerator
{
    public partial class VdmGen
    {
        private IModelStore store;
        public VdmGen(IModelStore s)
        { store = s; }
    }
}

Pour tester le projet, appuyez sur F5. Une nouvelle instance de Visual Studio démarrera. Dans cette instance, ouvrez ou créez un modèle UML qui contient un diagramme de classes. Ajoutez des classes au diagramme et ajoutez des attributs à chaque classe. Cliquez avec le bouton droit dans le diagramme, puis cliquez sur l'exemple de commande Generate VDM. La commande crée le fichier C:\Generated.txt. Inspectez ce fichier. Son contenu doit ressembler au texte suivant, mais il répertoriera vos propres classes et attributs :

Type Class1 ::
          Attribute1 : int 
          Attribute2 : string 
Type Class2 :: 
          Attribute3 : string 

Génération de fichiers à partir d'une application

Vous pouvez générer des fichiers à partir d'une application qui lit un modèle UML. Dans cet objectif, la méthode la plus flexible et fiable pour accéder au modèle et à ses éléments correspond à Visual Studio Modelbus.

Vous pouvez également utiliser l'API de base pour charger le modèle et passer le modèle aux modèles de texte au moyen des techniques présentées dans la section précédente. Pour plus d'informations sur le chargement d'un modèle, consultez Comment : lire un modèle UML dans le code de programme.

Génération de fichiers au moment du design

Si votre projet dispose d'une méthode standard pour interpréter les données UML en tant que code, vous pouvez créer des modèles de texte qui vous permettent de générer du code dans votre projet à partir d'un modèle UML. En général, vous disposez d'une solution qui contient le projet de modèle UML et d'un ou plusieurs projets pour le code d'application. Chaque projet de code peut contenir plusieurs modèles qui génèrent un code de programme, des ressources et des fichiers de configuration, en fonction du contenu du modèle. Le développeur peut exécuter tous les modèles en cliquant sur le bouton Transformer tous les modèles dans la barre d'outils Explorateur de solutions. Le code de programme est généré habituellement sous la forme de classes partielles, pour faciliter l'intégration des parties écrites manuellement.

Un projet Visual Studio de ce genre peut être distribué sous la forme d'un modèle, afin que chaque membre d'une équipe soit en mesure de créer des projets qui génèrent de la même façon du code à partir d'un modèle. En général, le modèle fait partie d'un package d'extension qui inclut des contraintes de validation sur le modèle pour garantir le respect des conditions préalables du code de génération.

Procédure permettant de générer des fichiers

  • Pour ajouter un modèle à un projet, sélectionnez Modèle de texte dans la boîte de dialogue Ajouter un nouveau fichier. Vous pouvez ajouter un modèle à la plupart des types de projet, mais pas aux projets de modélisation.

  • La propriété Outil personnalisé du fichier modèle doit avoir la valeur TextTemplatingFileGenerator et l'extension de nom de fichier doit être .tt.

  • Le modèle doit avoir au moins une directive de sortie :

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

    Définissez le champ d'extension en fonction du langage de votre projet.

  • Pour permettre au code de génération dans votre modèle d'accéder au modèle, écrivez les directives <#@ assembly #> pour les assemblys requises pour lire un modèle UML. Utilisez ModelingProject.LoadReadOnly() pour ouvrir le modèle. Pour plus d'informations, consultez Comment : lire un modèle UML dans le code de programme.

  • Le modèle est exécuté lorsque vous l'enregistrez et lorsque vous cliquez sur le bouton Transformer tous les modèles dans la barre d'outils Explorateur de solutions.

  • Pour plus d'informations sur ce type de modèle, consultez Génération de code durant la conception à l'aide de modèles de texte T4.

  • Dans un projet standard, vous disposez de plusieurs modèles qui génèrent des fichiers différents à partir du même modèle. La première partie de chaque modèle est la même. Pour réduire cette duplication, déplacez les parties communes vers un fichier texte distinct, puis appelez ce dernier en utilisant la directive <#@include file="common.txt"#> dans chaque modèle.

  • Vous pouvez également définir un processeur de directive spécialisé qui vous permet de fournir des paramètres au processus de génération de texte. Pour plus d'informations, consultez Personnalisation d'une transformation de texte T4.

Exemple

Cet exemple génère une classe C# pour chaque classe UML dans le modèle source.

Pour configurer une solution Visual Studio pour cet exemple

  1. Créez un diagramme de classes UML dans un projet de modélisation, dans une nouvelle solution.

    1. Dans le menu Architecture, cliquez sur Nouveau diagramme.

    2. Sélectionnez Diagramme de classes UML.

    3. Suivez les invites pour créer une nouvelle solution et un nouveau projet de modélisation.

    4. Ajoutez des classes au diagramme en faisant glisser l'outil Classe UML à partir de la boîte à outils.

    5. Enregistrez le fichier.

  2. Créez un projet C# ou Visual Basic dans la même solution.

    • Dans l'Explorateur de solutions, cliquez avec le bouton droit sur la solution, pointez sur Ajouter, puis cliquez sur Nouveau projet. Sous Modèles installés, cliquez sur Visual Basic ou sur Visual C#, puis sélectionnez un type de projet tel qu'Application console.
  3. Ajoutez un fichier texte brut au projet C# ou Visual Basic. Ce fichier contiendra du code partagé si vous souhaitez écrire plusieurs modèles de texte.

    • Dans l'Explorateur de solutions, cliquez avec le bouton droit sur le projet, pointez sur Ajouter, puis cliquez sur Nouvel élément. Sélectionnez Fichier texte.

    Insérez le texte qui figure dans la section suivante.

  4. Ajoutez un fichier modèle de texte au projet C# ou Visual Basic.

    • Dans l'Explorateur de solutions, cliquez avec le bouton droit sur le projet, pointez sur Ajouter, puis cliquez sur Nouvel élément. Sélectionnez Modèle de texte.

    Insérez le code qui suit dans le fichier modèle de texte.

  5. Enregistrez le fichier modèle de texte.

  6. Inspectez le code dans le fichier subsidiaire. Il doit contenir une classe pour chaque classe UML dans le modèle.

    1. Dans un projet Visual Basic, cliquez sur Afficher tous les fichiers dans la barre d'outils Explorateur de solutions.

    2. Développez le nœud du fichier modèle dans l'Explorateur de solutions.

Contenu du fichier texte partagé

Dans cet exemple, le fichier est appelé SharedTemplateCode.txt et se trouve dans le même dossier que les modèles de texte.

<# /* 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;
  }
}
#>

Contenu du fichier modèle de texte

Le texte suivant est placé dans le fichier .tt. Cet exemple génère des classes dans un fichier C# à partir des classes UML dans le modèle. Toutefois, vous pouvez générer des fichiers de tout type. Le langage du fichier généré n'est pas lié au langage dans lequel le code du modèle de texte est écrit.

<#@include file="SharedTemplateCode.txt"#>
<#@ output extension=".cs" #>
namespace Test 
{
<#
      foreach (IClass c in ModelStore.AllInstances<IClass>())
      {
#>
   public partial class <#=c.Name#>
   {   }
<#
      }
#>
}

Comment utiliser la génération de texte

La véritable puissance de la modélisation se manifeste lorsque vous utilisez des modèles pour concevoir au niveau des spécifications ou de l'architecture. Vous pouvez utiliser des modèles de texte pour effectuer une partie du travail de conversion des idées de haut niveau en code. Dans de nombreux cas, cela ne mène pas à une correspondance un-à-un entre les éléments dans les modèles et les classes UML ou dans d'autres parties du code de programme.

En outre, la transformation dépend de votre domaine de problème ; il n'existe aucun mappage universel entre les modèles et le code.

Voici quelques exemples de génération de code à partir de modèles :

  • Gammes de produits. Fabrikam, Inc. construit et installe des systèmes de manutention de bagages d'aéroport. La plus grande partie du logiciel est très similaire d'une installation à une autre, mais la configuration du logiciel dépend des machines de manutention des bagages installées et de la manière dont ces éléments sont reliés par des tapis roulants. En amont d'un contrat, les analystes de Fabrikam discutent des impératifs à respecter avec la direction de l'aéroport et capturent le plan matériel à l'aide d'un diagramme d'activités UML. À partir de ce modèle, l'équipe de développement génère des fichiers de configuration, un code de programme, des plans et des documents utilisateur. Elle termine sa tâche en effectuant des ajouts et des réglages manuels dans le code. Au fur et à mesure qu'ils acquièrent de l'expérience, tâche après tâche, les analystes étendent la portée des matériaux générés.

  • Motifs. Les développeurs de Contoso, Ltd créent souvent des sites Web et conçoivent le schéma de navigation à l'aide de diagrammes de classes UML. Chaque page Web est représentée par une classe et les associations représentent des liens de navigation. Les développeurs génèrent une grande partie du code d'un site Web à partir du modèle. Chaque page Web correspond à plusieurs classes et entrées de fichier de ressources. L'avantage de cette méthode est que la construction de chaque page se conforme à un motif unique, ce qui la rend plus fiable et flexible qu'avec du code écrit à la main. Le motif se trouve dans les modèles de génération, tandis que le modèle est utilisé pour capturer les aspects variables.

  • Schémas. Humongous Insurance possède des milliers de systèmes dans le monde. Ces systèmes utilisent des bases de données, des langues et des interfaces différentes. L'équipe chargée de l'architecture centrale publie en interne des modèles de concepts et de processus d'entreprise. À partir de ces modèles, les équipes locales génèrent une partie de leurs schémas d'échange et de base de données, des déclarations dans le code de programme, etc. La présentation graphique des modèles aide les équipes à traiter les différentes propositions. Les équipes créent plusieurs diagrammes qui montrent des sous-ensembles du modèle qui s'appliquent aux différents domaines d'activité. Ils utilisent également des couleurs pour signaler les domaines sujets à modification.

Techniques importantes pour générer des artefacts

Dans les exemples précédents, les modèles sont utilisés à diverses fins propres aux entreprises et l'interprétation des éléments de modélisation, tels que les classes et les activités, varie d'une application à une autre. Les techniques suivantes sont utiles lorsque vous générez des artefacts à partir de modèles.

  • Profils. Même au sein d'un domaine d'activité, l'interprétation d'un type d'élément peut varier. Par exemple, dans un diagramme de site Web, certaines classes peuvent représenter des pages Web, alors que d'autres représentent des blocs de contenu. Pour aider les utilisateurs à relever ces distinctions, définissez des stéréotypes. Les stéréotypes permettent également de joindre des propriétés supplémentaires qui s'appliquent aux éléments de ce genre. Les stéréotypes sont intégrés dans des profils. Pour plus d'informations, consultez Comment : définir un profil pour étendre UML.

    Dans le code d'un modèle, il est facile d'accéder aux stéréotypes définis sur un objet. Par exemple :

    public bool HasStereotype(IClass c, string profile, string stereo)
    { return c.AppliedStereotypes.Any
       (s => s.Profile == profile && s.Name == stereo ); }
    
  • Modèles contraints. Les modèles que vous pouvez créer ne sont pas tous valides pour chaque objectif. Par exemple, dans les modèles pour bagages d'aéroport de Fabrikam, il ne serait pas correct d'avoir un comptoir d'enregistrement sans tapis roulant pour emporter les bagages. Vous pouvez définir des fonctions de validation qui aideront les utilisateurs à respecter ces contraintes. Pour plus d'informations, consultez Comment : définir des contraintes de validation pour les modèles UML.

  • Conserver les modifications manuelles. Uniquement certains fichiers solution peuvent être générés à partir d'un modèle. Dans la plupart des cas, vous devez être en mesure d'ajouter ou d'ajuster manuellement le contenu généré. Toutefois, il est important que ces modifications manuelles soient conservées lorsque la transformation de modèle est exécutée de nouveau.

    Là où vos modèles génèrent du code dans les langages .NET, ils doivent générer des classes partielles, afin que les développeurs puissent ajouter des méthodes et du code. Il est également utile de générer chaque classe sous la forme d'une paire : une classe de base abstraite qui contient les méthodes et une classe héritière qui contient uniquement le constructeur. Cela permet aux développeurs de substituer les méthodes. Pour autoriser la substitution de l'initialisation, il faut procéder dans une méthode distincte et non pas dans les constructeurs.

    Là où un modèle génère des données XML et d'autres types de sortie, il peut être plus difficile de garder le contenu manuel séparé du contenu généré. Une méthode consiste à créer une tâche dans le processus de génération qui associe deux fichiers. Une autre méthode consiste à ce que les développeurs ajustent une copie locale du modèle de génération.

  • Déplacer le code dans des assemblys distincts. Nous ne recommandons pas l'écriture de corps de code volumineux dans les modèles. Il est préférable de garder le contenu généré à l'écart des calculs, et les modèles de texte ne sont pas bien pris en charge pour la modification du code.

    À la place, si vous devez exécuter des calculs substantiels pour générer du texte, construisez ces fonctions dans un assembly distinct et appelez ses méthodes à partir du modèle.