Compartir a través de


Cómo: Generar archivos a partir de un modelo UML

A partir de un modelo UML, puede generar código de programa, esquemas, documentos, recursos y otros artefactos de cualquier tipo. Un método práctico para generar archivos de texto a partir de un modelo UML es utilizar plantillas de texto. Estas le permiten incrustar el código de programa dentro del texto que desea generar.

Hay tres escenarios principales:

  • Generar archivos a partir de un comando de menú o gesto. Puede definir un comando de Visual Studio que esté disponible en los modelos UML.

  • Generar archivos a partir de una aplicación. Puede escribir una aplicación que lea los modelos UML y genere archivos.

  • Generar en tiempo de diseño. Puede utilizar un modelo para definir alguna de las funcionalidades de la aplicación y generar código, recursos, etc., dentro de la solución de Visual Studio.

Este tema finaliza con una explicación sobre cómo utilizar la generación de texto. Para obtener más información, vea Generación de código y plantillas de texto T4.

Generar archivos a partir de un comando de menú

Puede utilizar plantillas de texto de preprocesamiento dentro de un comando de menú UML. Dentro del código de la plantilla de texto, o en una clase parcial independiente, puede leer el modelo que ve el diagrama.

Para obtener más información sobre estas características, lea los temas siguientes:

El enfoque mostrado en el siguiente ejemplo es adecuado para generar texto a partir de un único modelo, al iniciar la operación desde uno de los diagramas del modelo. Para procesar un modelo en un contexto independiente, considere el uso de ModelBus de Visual Studio para tener acceso al modelo y sus elementos.

Ejemplo

Para ejecutar este ejemplo, cree un proyecto de extensión de Visual Studio (VSIX). El nombre del proyecto que se utiliza en este ejemplo es VdmGenerator. En el archivo source.extension.vsixmanifest, haga clic en Agregar contenido y establezca el campo de tipo en MEF Component y la ruta de acceso de origen que hace referencia al proyecto actual. Para obtener más información sobre cómo configurar este tipo de proyecto, vea Cómo: Definir un comando de menú en un diagrama de modelado.

Agregue al proyecto un archivo de C# que contiene el siguiente código. Esta clase define un comando de menú que aparecerá en un diagrama de clases 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"; } }
  }
}

El siguiente archivo es la plantilla de texto. Genera una línea de texto para cada clase UML del modelo, y una línea para cada atributo de cada clase. El código para leer el modelo está incrustado en el texto, delimitado por <# ... #>.

Para crear este archivo, en el Explorador de soluciones, haga clic con el botón secundario en el proyecto, elija Agregar y, a continuación, haga clic en Nuevo elemento. Seleccione Plantilla de texto preprocesada. El nombre de archivo de este ejemplo debe ser VdmGen.tt. La propiedad Herramienta personalizada del archivo debe establecerse en TextTemplatingFilePreprocessor. Para obtener más información sobre las plantillas de texto preprocesadas, vea Generación de texto en tiempo de ejecución mediante el uso de plantillas de texto T4 preprocesadas.

<#@ 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 #> 
<#
     }
   }
#>

La plantilla de texto genera una clase parcial de C#, que pasa a formar parte del proyecto de Visual Studio. En un archivo independiente, agregue otra declaración parcial de la misma clase. Este código proporciona la plantilla con acceso al almacén de modelos UML:

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

Para probar el proyecto, presione F5. Se iniciará una nueva instancia de Visual Studio. En esta instancia, abra o cree un modelo UML que contenga un diagrama de clases. Agregue algunas clases al diagrama, y agregue algunos atributos a cada clase. Haga clic con el botón secundario del mouse en el diagrama y, a continuación, haga clic en el comando de ejemplo Generar VDM. El comando crea el archivo C:\Generated.txt. Inspeccione este archivo. Su contenido debe parecerse al siguiente texto, pero mostrará sus propias clases y atributos:

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

Generar archivos a partir de una aplicación

Puede generar archivos a partir de una aplicación que lea un modelo UML. Para este propósito, el método más flexible y sólido de tener acceso al modelo y sus elementos es Modelbus de Visual Studio.

También puede utilizar la API básica para cargar el modelo y pasarlo a plantillas de texto utilizando las mismas técnicas que en la sección anterior. Para obtener más información sobre cómo cargar un modelo, vea Cómo: Leer un modelo UML en el código del programa.

Generar archivos en tiempo de diseño

Si su proyecto tiene un método estándar de interpretar UML como código, puede crear plantillas de texto que le permitan generar código dentro del proyecto a partir de un modelo UML. Normalmente tendría una solución que contiene el proyecto de modelo UML y uno o más proyectos para el código de aplicación. Cada proyecto de código podría contener varias plantillas que generen código de programa, recursos y archivos de configuración, dependiendo del contenido del modelo. El desarrollador puede ejecutar todas las plantillas haciendo clic en Transformar todas las plantillas en la barra de herramientas del Explorador de soluciones. El código de programa se genera normalmente en forma de clases parciales, para facilitar la integración de las partes escritas de forma manual.

Un proyecto de Visual Studio de este tipo se puede distribuir en forma de una plantilla, para que cada miembro de un equipo pueda crear proyectos que generen código a partir de un modelo de la misma manera. Normalmente, la plantilla forma parte de un paquete de extensión que incluye restricciones de validación en el modelo para asegurarse de que se cumplen las condiciones previas del código de generación.

Esquematizar el procedimiento para generar archivos

  • Para agregar una plantilla a un proyecto, seleccione Plantilla de texto en el cuadro de diálogo de Agregar nuevo archivo. Puede agregar una plantilla a la mayoría de los tipos de proyecto, pero no a los proyectos de modelado.

  • La propiedad Herramienta personalizada del archivo de plantilla debe establecerse en TextTemplatingFileGenerator y la extensión de nombre de archivo debe ser .tt.

  • La plantilla debe tener al menos una directiva de salida:

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

    Establezca el campo de extensión según el lenguaje de su proyecto.

  • Para permitir que el código de generación de su plantilla tenga acceso al modelo, escriba directivas <#@ assembly #> para los ensamblados necesarios para leer un modelo UML. Utilice ModelingProject.LoadReadOnly() para abrir el modelo. Para obtener más información, vea Cómo: Leer un modelo UML en el código del programa.

  • La plantilla se ejecuta al guardarla y hacer clic en Transformar todas las plantillas en la barra de herramientas del Explorador de soluciones.

  • Para obtener más información sobre este tipo de plantilla, vea Generación de código en tiempo de diseño usando las plantillas de texto T4.

  • En un proyecto típico, tendrá varias plantillas que generan diferentes archivos a partir del mismo modelo. La primera parte de cada plantilla será la misma. Para reducir esta duplicación, traslade las partes comunes a un archivo de texto independiente y, a continuación, invóquelo utilizando la directiva <#@include file="common.txt"#> en cada plantilla.

  • También puede definir un procesador de directivas especializado que permita proporcionar parámetros al proceso de generación de texto. Para obtener más información, vea Personalizar la transformación de texto T4.

Ejemplo

En este ejemplo se genera una clase de C# para cada clase UML del modelo de origen.

Para configurar una solución de Visual Studio para este ejemplo

  1. Cree un diagrama de clases UML en un proyecto de modelado en una nueva solución.

    1. En el menú Arquitectura, haga clic en Nuevo diagrama.

    2. Seleccione Diagrama de clases UML.

    3. Siga las indicaciones para crear una nueva solución y un nuevo proyecto de modelado.

    4. Agregue algunas clases al diagrama arrastrando la herramienta de clases UML desde el cuadro de herramientas.

    5. Guarde el archivo.

  2. Cree un proyecto de C# o Visual Basic en la misma solución.

    • En el Explorador de soluciones, haga clic con el botón secundario en la solución, seleccione Agregar y, a continuación, haga clic en Nuevo proyecto. En Plantillas instaladas, haga clic en Visual Basic o Visual C# y, a continuación, seleccione un tipo de proyecto como Aplicación de consola.
  3. Agregue un archivo de texto sin formato al proyecto de C# o Visual Basic. Este archivo contendrá código que se comparte si desea escribir varias plantillas de texto.

    • En el Explorador de soluciones, haga clic con el botón secundario del mouse en el proyecto, elija Agregar y, a continuación, haga clic en Nuevo elemento. Seleccione Archivo de texto.

    Inserte el texto que se muestra en la siguiente sección.

  4. Agregue un archivo de plantilla de texto al proyecto de C# o Visual Basic.

    • En el Explorador de soluciones, haga clic con el botón secundario del mouse en el proyecto, elija Agregar y, a continuación, haga clic en Nuevo elemento. Seleccione Plantilla de texto.

    Inserte el código que sigue en el archivo de plantilla de texto.

  5. Guarde el archivo de plantilla de texto.

  6. Inspeccione el código en el archivo subsidiario. Debe contener una clase para cada clase UML del modelo.

    1. En un proyecto de Visual Basic, haga clic en Mostrar todos los archivos en la barra de herramientas del Explorador de soluciones.

    2. Expanda el nodo de archivo de plantilla en el Explorador de soluciones.

Contenido del archivo de texto compartido

En este ejemplo, el archivo se denomina SharedTemplateCode.txt y está en la misma carpeta que las plantillas de texto.

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

Contenido del archivo de plantilla de texto

El siguiente texto se coloca en el archivo .tt. En este ejemplo se generan clases en un archivo de C# a partir de las clases UML del modelo. Sin embargo, puede generar archivos de cualquier tipo. El lenguaje del archivo generado no está relacionado con el lenguaje en el que está escrito el código de la plantilla de texto.

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

Cómo utilizar la generación de texto

La potencia real del modelado se obtiene al utilizar modelos para diseñar en el nivel de los requisitos o la arquitectura. Puede utilizar plantillas de texto para realizar algunas de las tareas de convertir ideas de alto nivel en código. En muchos casos, esto no conduce a una correspondencia unívoca entre los elementos de los modelos UML y las clases u otros elementos del código de programa.

Además, la transformación depende de su dominio del problema; no hay ninguna asignación universal entre los modelos y el código.

A continuación se incluyen algunos ejemplos de la generación de código a partir de modelos:

  • Líneas de producto. Fabrikam, Inc. crea e instala sistemas de control del equipaje en los aeropuertos. La mayor parte del software es muy similar entre una instalación y la siguiente, pero la configuración del software depende de qué maquinaria de control de equipaje está instalada y de cómo están interconectadas estas partes mediante cintas transportadoras. Al principio de un contrato, los analistas de Fabrikam estudian los requisitos con la administración del aeropuerto y capturan el plan de hardware utilizando un diagrama de actividades UML. A partir de este modelo, el equipo de desarrollo genera los archivos de configuración, el código de programa, los planes y los documentos de usuario. Completan el trabajo agregando elementos manualmente y ajustando el código. Cuando ganan experiencia tras un trabajo, amplían el ámbito del material generado.

  • Modelos. Los desarrolladores de Contoso, Ltd. suelen compilar sitios web, y diseñan el esquema de navegación mediante diagramas de clases UML. Una clase representa cada página web, y las asociaciones representan vínculos de navegación. Los desarrolladores generan gran parte del código de un sitio web a partir del modelo. Cada página web corresponde a varias clases y entradas del archivo de recursos. Este método tiene la ventaja de que la construcción de cada página se ajusta a un único modelo, lo cual lo hace más confiable y flexible que el código escrito a mano. El modelo se utiliza en las plantillas de generación, mientras que el modelo UML se utiliza para capturar aspectos variables.

  • Esquemas. Humongous Insurance tiene miles de sistemas en todo el mundo. Estos sistemas utilizan diferentes bases de datos, lenguajes e interfaces. El equipo de arquitectura central publica internamente modelos de conceptos y procesos comerciales. A partir de estos modelos, los equipos locales generan partes de sus esquemas de intercambio y base de datos, declaraciones en código de programa, etc. La presentación gráfica de los modelos ayuda a los equipos a estudiar las propuestas. Los equipos crean varios diagramas que muestran subconjuntos del modelo que se aplican a diferentes áreas comerciales. También utilizan colores para resaltar las áreas que deben cambiar.

Técnicas importantes para generar artefactos

En los ejemplos anteriores, los modelos se utilizan para diversos propósitos comerciales, y la interpretación de elementos de modelado, como clases y actividades, varían de una aplicación a otra. Las siguientes técnicas son útiles al generar artefactos a partir de los modelos.

  • Perfiles. Incluso dentro de un área comercial, la interpretación de un tipo de elemento puede variar. Por ejemplo en un diagrama de sitio web, algunas clases podrían representar páginas web y otras representan bloques de contenido. Para facilitar que los usuarios registren estas distinciones, defina estereotipos. Los estereotipos también permiten adjuntar propiedades adicionales que se aplican a elementos de ese tipo. Los estereotipos se empaquetan dentro de perfiles. Para obtener más información, vea Cómo: Definir un perfil para ampliar UML.

    En el código de plantilla, es fácil tener acceso a los estereotipos que se definen en un objeto. Por ejemplo:

    public bool HasStereotype(IClass c, string profile, string stereo)
    { return c.AppliedStereotypes.Any
       (s => s.Profile == profile && s.Name == stereo ); }
    
  • Modelos restringidos. No todos los modelos que puede crear son válidos para todos los propósitos. Por ejemplo, en los modelos de control de equipaje de Fabrikam, sería incorrecto tener un mostrador de facturación sin una cinta transportadora de salida. Puede definir funciones de validación que ayuden a los usuarios a cumplir estas restricciones. Para obtener más información, vea Cómo: Definir restricciones de validación para modelos UML.

  • Conservar los cambios manuales. Solo algunos archivos de la solución se pueden generar a partir de un modelo. En la mayoría de los casos, debe poder agregar o ajustar el contenido generado a mano. Sin embargo, es importante que estos cambios manuales se conserven cuando se ejecute de nuevo la transformación de plantillas.

    Si sus plantillas generan código en lenguajes .NET, deben generar clases parciales para que los desarrolladores puedan agregar métodos y código. También resulta útil generar cada clase como un par: una clase base abstracta que contiene los métodos y una clase heredera que solo contiene el constructor. Esto permite a los desarrolladores reemplazar los métodos. Para permitir que se reemplace la inicialización, se realiza en un método independiente, en lugar de en los constructores.

    Si una plantilla genera XML y otros tipos de salida, puede que sea más difícil separar el contenido manual del contenido generado. Un método es crear una tarea en el proceso de compilación que combine dos archivos. Otro método es que los desarrolladores ajusten una copia local de la plantilla generadora.

  • Mover código a ensamblados independientes. No es recomendable escribir grandes cuerpos de código en las plantillas. Es preferible que el contenido y el cálculo sean independientes, y las plantillas de texto no son adecuadas para editar código.

    En su lugar, si tiene que realizar numerosos cálculos para generar texto, compile esas funciones en un ensamblado independiente y llame a sus métodos desde la plantilla.