Archive for the 'Artículos en Español' Category

Una mirada cercana al SmartPartPlaceholder

Ya hace un tiempo que venía esbozando la idea de postear sobre el SmartPartPlaceholder. Es un control útil, pero es otro concepto acerca del cual tenemos poca información. Así que decidí tomarme mi tiempo para ver que se podía hacer con este control, y como eso sucedía.

Basicamente, la forma en que podemos usar el placeholder es bastante simple:

Agregamos el placeholder en una vista o formulario y le ponemos algun ID a la propiedad SmartPartName. Luego, nos aseguramos de haber agregado una vista con dicho ID en un WorkItem antes de agregar la vista del placeholder, de modo tal que el placeholder la encuentre y la muestre.

Tan simple como parece, no? :P

Pero, como sucede eso? Eso es todo lo que podemos hacer con el placeholeder? Por qué la vista con el placeholder está encapsulada en un WorkItem en el SmartPart Quickstart?

Todas estas preguntas tienen una respuesta, y de eso tratará este artículo.

Los Actores

Primero, comenzaré describiendo las diferentes clases que trabajan juntas para lograr "la mágia detrás de escenas".

La interfaz ISmartPartPlaceholder

SmartPartPlaceholder-SPPHInterface

La interfaz ISmartPartPlaceholder contiene solo dos propiedades que son implementadas por la clase del control SmartPartPlaceholder. Estas propiedades son:

  • SmartPartName. Esta propiedad obtiene o establece el nombre por defecto de la SmartPart contenida en el WorkItem actual.
  • SmartPart. Esta propiedad obtiene o establec la SmartPart contenida por el placeholder.

La clase SmartPartPlaceholder

SmartPartPlaceholder-SPPHClass

Aquí está el personaje principal! La clase que tiene el papel principal en la obra de este artículo! :P

Afortunadamente, no es una clase compleja. Consiste de un constructor, un método que dibuja el borde en tiempo de diseño, las dos propiedades implementadas de la interfaz  ISmartPartPlaceholder, y un método que dispara un evento cuando la SmartPart se muestra.

Entonces, veámoslos en detalle:

  • El constructor del SmartPartPlaceholder establece el estilo del placeholder para soportar color de fondo transparente. Luego, establece el fondo del placeholder en transparente.
  • El método protegido OnPaint(PaintEventArgs e) dibuja el rectángulo intermintente que forma el borde del SmartPartPlaceholder en tiempo de diseño.
  • La propiedad SmartPartName es usada por la estrategia ControlSmartPartStrategy para obtener la SmartPart (cuyo ID concuerda con la propiedad SmartPartName) del WorkItem actual para mostrarla en el placeholder. NOTA: El valor de esta propiedad no puede ser nulo, de lo contrario se lanzará una excepción.
  • La propiedad SmartPart muestra la SmartPart, que obtiene como valor, en el placeholder. Verifica que el valor establecido no sea nulo y que sea del tipo Control(de lo contrario se lanzará una excepción). Una vez hecho ésto, castea el valor a Control, establece su propiedad Dock en DockStyle.Fill y llama a su método Show(). Luego, la colección Controls del placeholder es vaciada y la SmartPart casteada es agregada a esta colección. Finalmente, se llama al método OnSmartPartShown, enviando la SmartPart como argumento.
  • El método privado OnSmartPartShown recibe un objetco como argumento (en efecto, la SmartPart que recién ha sido mostrada en el placeholder). Verifica si el evento SmartPartShown tiene algun manejador (handler) adherido, y, si es así, dispara el evento con el placeholder como sender y una nueva instancia de la clase SmartPartPlaceHolderEventArgs como argumento del evento (esta instancia contiene la SmartPart, en su propiedad SmartPart de solo lectura, que fue enviada como argumento en su constructor).

La clase SmartPartPlaceHolderEventArgs

La clase SmartPartPlaceHolderEventArgs tiene un constructor que recibe un objeto como argumento (la SmartPart recientemente mostrada en el placeholder). Este constructor verifica que el argumento que recibe no sea nulo (de lo contrario se lanzará una excepción) y establece el valor de un miembro privado (llamado “smartPart”) con este argumento. Esta clase tambien contiene una propiedad de solo lectura llamada SmartPart (del tipo object) que puede ser utilizada para obtener la SmartPart que recién fue mostrada en el placeholder.

La clase ControlSmartPartStrategy

La clase ControlSmartPartStrategy es una estrategia de constructor (builder strategy) que se encarga de detectar el SmartPartPlaceholder cuando un control pasa a través del pipeline de ObjectBuilder (NOTA: la estrategia también busca Workspaces y SmartParts). Una vez que se detecta el placeholder, la estrategia verifica su propiedad SmartPartName, de modo tal que obtenga la SmartPart, cuyo ID coincide con la propiedad SmartPartName, de la colección Items del WorkItem actual. Habiendo obtenido la SmartPart, establece el valor de la propiedad SmartPart del placeholder con el valor obtenido. Si el valor obtenido es nulo, no se establece el valor de la propiedad SmartPart; es por ésto que ninguna excepción se dispará cuando no se encuentra ninguna SmartPart. La estrategia también agrega el placeholder a la coleción Items del WorkItem actual, con el nombre del placeholder como su ID (si el placeholder no tiene nombre, se establece una GUID en su lugar).

Aquí está el código del método ReplaceIfPlaceHolder(WorkItem workItem, Control control) que realiza lo que se descrivió anteriormente:

private void ReplaceIfPlaceHolder(WorkItem workItem, Control control)

{

    ISmartPartPlaceholder placeholder = control as ISmartPartPlaceholder;

    if (placeholder != null)

    {

        Control replacement = workItem.Items.Get<Control>(placeholder.SmartPartName);

        if (replacement != null)

            placeholder.SmartPart = replacement;

    }

}

La Obra

El siguiente diagrama de secuencia muestra las interacciones descriptas en la sección “Los Actores”. Por razones de simplicidad, algunas intreacciones están comentadas como notas.

smartpartplaceholder-interactions-esp

Encapsulando placeholders en WorkItems?

Como se mostró en la sección “Los Actores”, la clase ControlSmartPartStrategy agregaba el placeholder al WorkItem actual con su nombre como ID. Por ende, si uno agrega dos instancias de la vista que contiene el placeholder al mismo WorkItem, se obtendrá una excepción indicando que ya hay un objeto con dicho ID. Ésta es una de las razones por las que los placeholders son encapsulados en diferentes WorkItems hijo.

La otra razón se relaciona con la vista que será mostrada en el placeholder. Como dije antes, la propiedad SmartPartName del placeholder no puede ser nula (o se obtendrá una excepción). Para lograr que el placeholder trabaje con esta propiedad, se debe agregar una vista con algún ID al WorkItem antes de agregar la vista del placeholder. Pero, si se agregan dos vistas con el mismo ID, se obtendrá una excepción debido a que ya hay un objeto con el mismo ID. Es por ésto que, en el SmartPart Quickstart, la vista mostrada en el placeholder y la vista con placeholder son encapsuladas en un WorkItem hijo.

Sin embargo, este tipo de encapsulamiento es necesario si alguna de estas razones descriptas arriba pueden suceder.

Tip

Una forma útil de utilizar el SmartPartPlaceholder es como un “DeckWorkspace reducido” o un “DeckWorkspace ligero”. Antes de comenzar a describir ésto, permitanme aclarar que un placeholder puede ser agregado en formularios también, no sólo en vistas! Ahora, continuemos. Como dije, el placeholder es agregado al WorkItem con algún ID, por lo tanto, se puede agregar en el ShellForm con un ID y accederlo en los modules. Por ejemplo:

SmartPartPlaceholder placeholder = WorkItem.RootWorkItem.Items.Get<SmartPartPlaceholder>(“Placeholder”);
MyView view = WorkItem.SmartParts.AddNew<MyView>();
placeholder.SmartPart = view;

De esta forma, se puede mostrar una vista en el placeholder, y cuando se agregue otra, la primera se removerá automáticamente del placeholder.

Para este propósito, debe tener en mente que:

  • Llamar al método Presenter.OnCloseView() no realiza nada, ya que busca un workspace contenedor y llama a su método IWorkspace.Close(object view) y, en estes caso, la vista se encuentra en un SmartPartPlaceholder.
  • Si se desea remover la vista del placeholder, se debe remover la vista explicitamente de la colección Controls.
  • El placeholder no contiene un evento SmartPartClosing.
  • El placeholder no contiene una colección de SmartParts.

Espero que este artículo les haya dejado un concepto más claro de la clase SmartPartPlaceholder.

Como siempre: cualquier feedback es bienvenido! :D

Saludos!

- Nacho

See this post in English.

Cómo: Generar tipos genéricos dinámicos

Hace un tiempo, empecé a preguntarme: “Cómo podría crear objetos genéricos específicos como List o Dictionary con tipos enviados como parámetros?”. Todos sabemos que entre “” tenemos que especificar el(los) tipo(s), pero ciertamente no podemos hacer algo como “Dictionary”.

Mi estimado colega Angel “Java” Lopez me dijo una vez que dicha creación dinámica de genéricos se podía hacer (no de esa forma, se refería a que el objetivo se podía alcanzar :P). Así que comencé a investigar y preguntar, hasta que encontré el método Type.MakeGenericType(Type[] args). Leí la documentación y finalmente hice funcionar la idea! Así que lo compartiré con ustedes! :)

Código Ejemplo

Este ejemplo muestra una clase llamada FabricaDeGenericos que genera objetos genéricos de acuerdo con los tipos pasados como parámetros. Para este propósito, la clase expone un método llamado CrearInstanciaGenerica(Type tipoBase, Type[] tiposDeArgumento).

Parámetros:

  • tipoBase: el tipo que será el tipo base genérico, y
  • tiposDeArgumento: un arreglo (array) de argumentos que proveerá los tipos para los argumentos genéricos.

Devuelve:

  • object.

public class FabricaDeGenericos
{
    public object CrearInstanciaGenerica(
        Type tipoBase, Type[] tiposDeArgumento
        )
    {
        return Activator.CreateInstance(tipoBase.MakeGenericType(
            tiposDeArgumento));
    }
}

Esta clase recibe el tipo de la clase base del genérico y sus argumentos genéricos, y luego llama al método MakeGenericType(Type[] args) de la clase System.Type de tipoBase que construye el genérico.

Hora, podemos preparar nuestra clase Cliente (aquí es una Windows Console Application) para que use la clase FabricaDeGenericos de la siguiente manera:

class Program
{
    static void Main(string[] args)
    {
        FabricaDeGenericos miFabricaDeGenericos = new FabricaDeGenericos();

        Type[] misTiposDeArgumento = { typeof(string), typeof(object) };

        Dictionary<string, object> miDiccionario =
            (Dictionary<string, object>)miFabricaDeGenericos.CrearInstanciaGenerica(
                typeof(Dictionary), misTiposDeArgumento);

        Console.WriteLine(miDiccionario.GetType().FullName);
        Console.Read();
    }
}

Aquí, estoy creando un nuevo Dictionary. Si le dan una mirada al código del Cliente, le estoy enviando el objeto tipo base que representa el tipo genérico Dictionary omitiendo los argumentos tipo, pero manteniendo la coma que los separa, para que el compilador pueda inferir el número de parámetros tipo (y, por supuesto, para evitar excepciones :P). Luego, también le envío un arreglo (array) de tipos (string para TKey y object para TValue) para reemplazar los parámetros tipo de Dictionary.

NOTA: La conversión (casting) es necesaria ya que el método CrearInstanciaGenerica() devuelve object! :)

Finalmente, llamo al método MakeGenericType(Type[] args) en el tipoBase el cuál devolverá el tipo para Dictionary. Activator.CreateInstance(Type TType) simplemente genera el objeto que será devuelto de acuerdo con el tipo.

La línea Console.WriteLine() muestra en la consola el tipo construido del nuevo objeto.

Más información:

- Salu2, Nacho

See this topic in English.

Actualizando una aplicación CAB a SCSF

Este artículo describe como actualizar una aplicación CAB a Smart Client Software Factory 2007.

Contenidos

  • Requisitos
  • Tarea 1: Creando una solución nueva de Smart Client
  • Tarea 2: Agregando la aplicación CAB antigua
  • Tarea 3: Removiendo el proyecto Shell de Smart Client
  • Tarea 4: Actualizando el proyecto Shell antiguo
  • Tarea 5: Modificando el archivo de la solución
  • Tarea 6: Compile y Ejecute… Diviértase! :)

Requisitos

Para poder completar la actualización, debe tener lo siguiente instalado en su computadora:

Tarea 1: Creando una solución nueva de Smart Client

Para actualizar su solución, creará una nueva solución de Smart Client usando la plantilla Smart Client Solution y luego agregará los módulos de su solución y el proyecto Shell a la nueva solución.

1. En el Visual Studio, dirígase a New (Nuevo) en el menú File (Archivo), y luego haga click en Project (Proyecto).

500x339.aspx

Figura 1. Las plantillas Smart Client Application Solution.

2. En el diálogo de New Project (Nuevo Proyecto), expanda el nodo de Guidance Packages. Haga click en el tipo de proyecto Smart Client Development May 2007.

3. En la parte de Templates (Plantillas), seleccione Smart Client Application (C#) o Smart Client Application (Visual Basic) (dependiendo del lenguaje en el que haya codificado la aplicación CAB).

4. Cambie el Name (Nombre).

5. (Opcional) Cambie la ubicación de la solución.

6. Haga click en OK (Aceptar).

500x290.aspx

Figura 2. El asistente Create Solution.

7. Ingrese la ubicación de los ensamblados del Composite UI Application Block, Enterprise Library, y de los offline application blocks.

8. Ingrese el Root namespace para su aplicación.

9. Desmarque la opción Create a separate module to define the layout for the shell (Crear un módulo separado para definirel diseño de la Shell).

10. (Opcional) Seleccione la opción Allow solution to host WPF SmartParts (Permitir que la solución albergue WPF SmartParts) (si su solución contiene o contendrá WPF SmartParts).

11. (Opcional) Seleccione la opción Show documentation after recipe completes (Mostrar documentación luego de que se complete el asistente).

12. Haga click Finish (Finalizar).

NewSCSFSolutionExplorer

Figura 3. La estructura de una solución de Smart Client.

Tarea 2: Agregando la aplicación CAB antigua

En esta tarea, agregará su aplicación CAB a la solución nueva de Smart Client y la configurará de acuerdo con la estructura de SCSF.

1. Copie su aplicación CAB antigua a la carpeta Source ubicada dentro de la carpeta raíz de su solución de Smart Client nueva.

2. En el Solution Explorer, haga click derecho sobre la carpeta Source, dirígase a Add (Agregar), y luego haga click en Existing Project (Proyecto Existente).

3. Busque el directorio de la aplicación CAB antigua y agregue uno de los proyectos de módulo (ej.: su proyecto del módulo Shell de CAB).

234x375.aspx

Figura 4. Proyecto del módulo Shell de CAB agregado.

4. Repita los pasos 2 y 3 hasta que todos los módulos hayan sido agregados.

5. Haga click derecho sobre su Shell de CAB y seleccione Set as StartUp Project (Establecer como proyecto de inicio).

6. Por cada módulo, haga click derecho sobre el proyecto y seleccione Add Reference (Agregar Referencia). En el tab de Projects (Proyectos), seleccione los proyectos Infrastructure.Interface e Infrastructure.Library y haga click en OK (Aceptar).

7. Por cada módulo, reemplace los ensamblados Microsoft.Practices.* con los que están ubicados en %CarpetaRaízDeLaSolución%\Lib. Si tiene referencias a los proyectos CompositeUI y CompositeUI.WinForms, reemplacelos por las referencias a los ensamblados ubicados en %CarpetaRaízDeLaSolución%\Lib.

Nota: %CarpetaRaízDeLaSolución%\Lib es el directorio de la carpeta raíz en la cual ubicó la nueva solución de Smart Client.

8. Por cada módulo, haga click derecho sobre el proyecto y seleccione Properties (Propiedades). En el tab de Build (Construir), dirígase a la sección Output (Salida) y edite el directorio de Output (Salida) a %CarpetaRaízDeLaSolución%\bin\Debug.

500x353.aspx

Figura 5. Output Path (Directorio de Salida) en la sección de Output (Salida) ubicada en el tab de Build (Construir) en las propiedades del Proyecto. Nota: quizás no vea un directorio relativo hasta que cierre la ventana de propiedades y la vuelva a abrir.

Tarea 3: Removiendo el proyecto Shell de Smart Client

En esta tarea, removerá el proyecto Shell de Smart Client.

1. En el Solution Explorer, haga click derecho sobre el proyecto Infrastructure.Shell y luego haga click en Remove (Remover).

199x375.aspx

Figura 6. Removiendo el proyecto Shell de SCSF.

2. Elimine la carpeta Source\Infrastructure\Shell ubicada dentro de la carpeta raíz de la solución.

500x288.aspx

Figura 7. Eliminando la carpeta Shell.

Tarea 4: Actualizando el proyecto Shell

En esta tarea, actualizará el proyecto Shell de su aplicación CAB para reemplazar al proyecto Shell de Smart Client.

1. En el Solution Explorer, expanda su proyecto Shell, abra el archivo ProfileCatalog.xml y actualícelo para que concuerde con el esquema usado en las soluciones de SCSF. Por ejemplo, si su ProfileCatalog aparece así:

XML

 <?xml version=”1.0″ encoding=”utf-8″ ?>

<SolutionProfile xmlns=”http://schemas.microsoft.com/pag/cab-profile” >

    <Modules>

        <!– algunos modulos –>

    </Modules>

</SolutionProfile>

Deberá actualizarlo para que aparezca así:

XML

 <SolutionProfile xmlns=”http://schemas.microsoft.com/pag/cab-profile/2.0″>

   <Section Name=”Services”>

      <Modules>

         <ModuleInfo AssemblyFile=”Infrastructure.Module.dll” /> <!– ESTO ES REQUERIDO POR LAS SOLUCIONES SC-SF –>

         <!– UBIQUE LOS MÓDULOS DE INFRAESTRUCTURA (FOUNDATIONAL) AQUÍ –>

      </Modules>

   </Section>

   <Section Name=”Apps”>

      <Dependencies>

         <Dependency Name=”Services” />

      </Dependencies>

      <Modules>

         <!– UBIQUE LOS MÓDULOS DE NEGOCIO (BUSINESS) AQUÍ –>

      </Modules>

   </Section>

</SolutionProfile>

 

2. Haga click en Save (Guardar) y cierre el archivo.

3. Ahora, haga click derecho sobre su archivo ShellApplication.cs y seleccione View Code (Ver Código).

4. Agregue la siguiente línea:

Visual Basic

Imports %RootNamespace%.Infrastructure.Library;
C#

using %RootNamespace%.Infrastructure.Library;
Nota: %RootNamespace% debe ser reemplazado con el RootNamespace que especificó al crear la solución de Smart Client.

5. Actualice la firma de la clase para que herede de FormShellApplication en vez de SmartClientApplication.

6. Haga click en Save (Guardar).

Tarea 5: Modificando el archivo de la solución

En esta tarea, modificará el archivo suSolución.sln para terminar de establecer el proyecto Shell de su aplicación CAB como el de Smart Client.

1. En la carpeta raíz de su solución, haga click derecho sobre el archivo .sln, seleccione Open with (Abrir con) y elija el NotePad (Bloc de notas).

2. Localice la sección Project de su proyecto Shell.

500x224.aspx

Figura 8. Sección del proyecto Shell.

3. Copie al portapapeles el GUID que aparece luego de la ubicación del proyecto (su formato es “XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”).

1

Figura 9. El GUID que aparece luego de la ubicación del proyecto.

4. Localice la línea GlobalSection(ExtensibilityGlobals) = postSolution.

5. Reemplace el GUID especificado en la propiedad ShellProjectGuid con la GUID de su proyecto Shell.

500x224.as1px

Figura 10. ShellProjectGuid.

6. Guarde y cierre el archivo.

Tarea 6: Compile y Ejecute… Diviértase! :)

En esta tarea, concluirá compilando y ejecutando su solución actualizada.

1. Vuelva al Visual Studio. Aparecerá un mensaje indicando que la solución ha sido modificada fuera del ambiente de desarrollo. Haga click en Reload (Recargar) para recargar la solución.

2. Compile (Build) y ejecute la aplicación.

Felicidades! Usted ha actualizado su aplicación CAB a SCSF May 2007! :) Debería poder utilizar las recipes (asistentes) ahora.

- Salu2, Nacho

See this topic in English.

Cómo: Desechar una Vista (SmartPart) en CAB y SCSF (Dispose a View)

En el foro de Smart Client en Codeplex, varios han preguntado cómo desechar una vista. Además, varias soluciones fueron propuestas.

La solución más rápida era extender el método CloseView() en el Presenter (de la Vista que queríamos desechar) de la siguiente manera:

public void OnCloseView()
{
    base.CloseView();

    WorkItem.SmartParts.Remove(View); //Removes the view from the SmartPart collection.
    if (View is IDisposable) ((IDisposable)View).Dispose(); //Disposes the view.
}

Sin embargo, quizás querramos desechar cualquier vista cada vez que la cerremos, por lo que la mejor forma de lograr ésto es mandando este código a la clase base Presenter localizada en el proyecto Infrastructure.Interface. JochenZ me mostró una forma ampliada de hacerlo (en el tópico SmartParts do not get disposed on WorkItem.Terminate(), en Codeplex). En su código, JochenZ consideró la posibilidad de que algún subscriptor al evento SmartPartClosing quizás haya establecido la propiedad Cancel en True, evitando que la Vista se cerrara. Es un código bien hecho, así que lo citaré:

protected virtual void CloseView()
{
    Services.IWorkspaceLocatorService locator = WorkItem.Services.Get<Services.IWorkspaceLocatorService>();
    IWorkspace wks = locator.FindContainingWorkspace(WorkItem, View);

    if (wks != null)  wks.Close(View);

   // check whether the view was actually closed (a subscriber to the IWorkspace.SmartPartClosing event might have cancelled it)
    wks = locator.FindContainingWorkspace(WorkItem, View);

    // if the view was removed from the workspace, remove it from the smartparts collection and dispose it if possible
    if (wks == null) {
        WorkItem.SmartParts.Remove(View);
        IDisposable viewAsDisposable = View as IDisposable;
        if (viewAsDisposable != null) viewAsDisposable.Dispose();
    }
}

Debo admitir que encuentro bastante interesante el código citado arriba, pero también debo decir que las soluciones propuestas hasta aquí presentan una falla. Como podemos ver, el método CloseView() llama al método IWorkspace.Close(smartPart) para cerrar la vista (obviamente :P). Pero, ¿qué pasaría si un botón, por ejemplo, en otro lugar (por así decirlo, en el Shell o en otra vista) llamara directamente al método IWorkspace.Close(smartPart)? ¡No habría llamada al método CloseView() por lo que la vista no se desecharía! Por lo tanto, necesitamos encontrar un nuevo hogar para el procedimiento de disposing.

Un nuevo hogar podría ser una clase que proveyera un método estático para hacer el disposing, la cuál podríamos ubicar en el proyecto Infrastructure.Interface (por razones de uso :) ). El código resultante debería verse así:

public class ViewDisposer
{
    ///
    /// Disposes the closing view.
    ///
    /// Closing SmartPart.
    /// Container WorkItem.

    public static void DiposeView(object smartPart, WorkItem workItem)
    {
        IDisposable viewAsDisposable = smartPart as IDisposable;
        if (viewAsDisposable != null) viewAsDisposable.Dispose();
        workItem.SmartParts.Remove(smartPart);
    }
}

Una vez que tenemos ésto, podemos utilizarlo así (por ejemplo):

WorkItem.Workspaces[WorkspaceNames.RightWorkspace].SmartPartClosing += new EventHandler<WorkspaceCancelEventArgs>(
    delegate(object workspace, WorkspaceCancelEventArgs e)
        {               
            ViewDisposer.DisposeView(e.SmartPart, WorkItem);
        });

Por favor, recuerden que deben considerar cuándo y dónde hacer el disposing (si no quieren hacerlo todo el tiempo). Además, si se posiciona este handler en la primera posición (en cuanto a orden de handlers), no se podrá cancelar el diposing, por lo que recomiendo usar ésto cuidadosamente y, por supuesto, a conciencia! ;)

- Salu2, Nacho

See this topic in English.

Patrones de diseño

Luego de un tiempo de no haber posteado, porque no había encontrado el momento ni el tema, he decidido volver a hacerlo, motivado por el post llamado A post a day keeps the doctor away de mi estimado compañero de trabajo Angel “Java” Lopez.

Me faltaba ver el tema en particular, ya que estaba decidido en emprender una vuelta fuerte, con algo en particular, un aporte para todos. Dicho tema lo encontré en mi estudio de patrones de diseño. Busqué… busqué… y busqué, pero la falta de contenido en la red era sorprendente (en particular en ciertos lugares donde reinaban las inconsistencias).

Luego de la lectura de algunos libros, webs y blogs, me di cuenta que lo que tenía uno, le faltaba al otro, por lo que me he decidido emprender una descripción concisa de algunos.

Aún así, quiero aclarar que trataré de lo que es un patrón en sí. Cuándo y cómo seleccionar uno en particular, cómo resuelven los patrones los problemas de diseño y cómo se utiliza un patrón (es decir, los pasos que debe seguir el lector para ponerlo en práctica, y no su implementación) están fuera del alcance de este post.

Dicho ésto, a su vez, quiero aclarar que todo tipo de comentario o feedback será bienvenido. Así se podrá corregir y actualizar para proveer una fuente base para todo lector interesado en el tema.

¿Qué es un patrón de diseño?

Ahora, bien, concentrémonos en el tema: patrones de diseño. Primero, quiero hacer una especie de “herencia” de definiciones, comenzando por lo genérico hasta llegar a lo específico.

Comencemos por la definición más abstracta:

  • Dechado (modelo o muestra que se copia para aprender a hacer lo que hay en ella) que sirve de muestra para sacar otra cosa igual.
  • Referencia aceptada usada para definir una unidad de medida.
  • Modelo que se toma como eje para medir o valorar otros de la misma especie.

Curiosamente, estas tres definiciones presentan un patrón, un modelo común; apuntan a lo mismo. Indican que un patrón es una referencia que sirve como modelo para medir, valorar y hacer otro del mismo tipo. Esa es mi interpretación. Simple y concreta, ¿no? Me parece correcta para asentar una base del concepto de la cual comenzaremos a “heredar”.

Continuemos con el concepto de patrón. En su libro A Timeless Way Of Building, Christopher Alexander indica que “cada patrón es una regla de tres partes que expresa una relación entre un cierto contexto, un problema y una solución.

Como un elemento en el mundo, cada patrón es una relación entre un cierto contexto, un cierto sistema de fuerzas que ocurre repetidamente en ese contexto, y una cierta configuración espacial que permite que esas fuerzas se resuelvan.

Como un elemento de lenguaje, un patrón es una instrucción que muestra cómo esa configuración espacial puede ser usada, una y otra vez, para resolver el sistema de fuerzas dado, en cualquier contexto que lo haga relevante.

Un patrón es, brevemente, al mismo tiempo una cosa que sucede en el mundo y la regla que nos dice cómo crear esa cosa, y cuándo tenemos que crearla. Es ambas, un proceso y una cosa; una descripción de una cosa que vive y una descrición de un proceso que generará esa cosa.”

Aquí me detendré para denotar lo interesante de estos párrafos que me llevó a citarlos. Alexander indica que un patrón es una regla de tres partes (contexto-problema-solución), una cosa que nos indica cómo y cuándo crearla para resolver un sistema dado en un contexto relevante. Ésto muestra que un patrón puede describir un problema determinado, una situación particular para dicho problema y una solución en el mismo contexto dado.

Ahora, pasemos a algo más específico, quizás, a nuestro tema. Alexander nos dice (en su libro A Pattern Language) que “cada patrón describe un problema que ocurre una y otra vez en nuestro entorno, así como la solución a ese problema, de tal modo que se pueda aplicar esta solución un millón de veces, sin hacer lo mismo dos veces”. Es muy interesante esta definición. La primera vez que la vi fue en un post del blog de Mathew Nolton llamado The Bridge Pattern, en el cual Nolton rescata la parte que indica que un patrón describe “[...] la solución a ese problema [...]“, para hacer hincapié en que entendiendo ese problema se puede llegar a comprender el por qué de cúando aplicar qué patrón. Es decir, entendiendo nuestro problema utilizaremos un patrón determinado y lo adaptaremos a dicho contexto.

Vayamos entrando en lo que es un patrón de diseño. Veamos la definición que nos provee Erich Gamma (en su libro Patrones de diseño):

“Un patrón de diseño nomina, abstrae e identifica los aspectos clave de una estructura de diseño común, lo que los hace útiles para crear un diseño orientado a objetos reutilizable. El patrón de diseño identifica las clases e instancias participantes, sus roles y sus colaboraciones, y la distribución de responsabilidades. Cada patrón de diseño se centra en un problema concreto, describiendo cuándo aplicarlo y si tiene sentido hacerlo teniendo en cuenta otras restricciones de diseño, así como las consecuencias y las ventajas e inconvenientes de su uso.”

Esta definición ya no da lugar a dudas. Explica concretamente lo que es un patrón de diseño y todo lo que conlleva, pero Gamma no se queda allí y explica que “un patrón es como una plantilla que puede aplicarse en muchas situaciones diferentes. [...] una descripción abstracta de un problema de diseño y cómo lo resuelve una disposición general de elementos”. Creo que con ésto ya se cerraría el concepto. Estas frases agregan valor a la definición ya que indica que un patrón de diseño nos provee una plantilla, un formato predeterminado de solución, un set de herramientas que podemos utilizar de forma personalizada para adaptarlo a nuestra situación particular. Es decir, un patrón ataca un problema en particular, si, pero somos nosotros quienes debemos adaptarlo a nuestro caso personal, nuestro contexto concreto. Para darse cuenta de ésto, simplemente basta con recordar la definición de C. Alexander.

Teniendo ya el concepto en claro, ¿qué más debemos saber de un patrón de diseño? Gamma nos indica que un patrón generalmente cuenta con cuatro elementos esenciales:

  • Nombre del patrón: describe, en una o dos palabras, un problema de diseño junto con sus soluciones y consecuencias.
  • Problema: describe cuándo aplicar el patrón (explica el problema y su contexto).
  • Solución: describe los elementos que constituyen el diseño, sus relaciones, responsabilidades y colaboraciones.
  • Consecuencias: son los resultados (ventajas e inconvenientes) de aplicar el patrón. Dichos pueden referirse al equilibrio entre espacio y tiempo, junto con su impacto sobre la flexibilidad, extensibilidad y portabilidad de un sistema.

Y los clasifica según su propósito (refleja que hace el patrón) y su ámbito (si se aplican a clases u objetos).

patterns

Ámbito

Los patrones de clases se ocupan de las relaciones entre las clases y sus subclases establecidas a través de la herencia (relaciones estáticas, fijadas en tiempo de compilación). En cambio, los patrones de objetos tratan con las relaciones entre objetos (relaciones dinámicas que cambian en tiempo de ejecución).

Propósito

Los patrones de creación tienen que ver con el proceso de creación de objetos. Los de clases delegan alguna parte del proceso de creación de objetos en las subclases, mientras que los de objetos lo hacen en otro objeto.

Los patrones estructurales tratan con la composición de clases u objetos. En el primer caso, se usa la herencia, mientras que en el segundo, se describen formas de ensamblar objetos.

Los patrones comportamiento caracterizan el modo en que las clases y objetos interactúan y se reparten la responsabilidad. Los de clases usan la herencia para describir algoritmos y flujos de control, mientras que los de objetos describen cómo cooperan un grupo de objetos para realizar una tarea que ninguno podría llevar a cabo por sí solo.

Con esto último ya cerraría el tema. Dejo aquí el listado de patrones que, a medida que vaya tratando cada uno, contendrán los links a sus respectivos posts.

Patrones de creación:

  • Abstract Factory
  • Builder
  • Factory Method
  • Prototype
  • Singleton

Patrones estructurales:

  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Façade
  • Flyweight
  • Proxy

Patrones de comportamiento:

  • Chain of Responsibility
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer

- Salu2, Nacho

PD: Les dejo unos links que contienen referencias interesantes:

See this topic in English.