jueves, 20 de diciembre de 2007

Diego Rivera

Este es otro de los íconos de la pintura mundial. Aca les dejo dos pinturas excelentes del pintor.



La clase game

Hoy vamos a ver un poco el tema de la clase Game que dejamos abierta en la publicación pasada.
Esta clase es la más importante de todas porque se ocupa de orquestar los distintos componentes del juego de manera tal que trabajen en sintonia.

Veamos un poco de código...

import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.*;
import java.util.*;
import javax.microedition.lcdui.game.*;

class Game extends Canvas implements Runnable
{

public Game()
{
}

static long FPS=14;

public void run()
{
setState(Estados.Load);
setFullScreenMode(true);
while (true)
{
try
{
long FPS_Timer = System.currentTimeMillis();
Update();
draw();
repaint();
serviceRepaints();
GameTimer++;
while ((System.currentTimeMillis()-FPS_Timer)<1000/FPS)
Thread.sleep(1);
}
catch (InterruptedException e)
{
//System.out.println(e.toString());
}
catch (Exception e)
{
//e.printStackTrace();
//System.out.println(e.toString());
}
}
}

public void hideNotify ()
{ }

public void showNotify()
{ }

}

Como ya hemos dicho la clase game hereda de Canvas una clase muy importante para los midlets. pueden encontrar mucha info de ella en la red. Lo que no habíamos dicho era que implementa la interfaz Runnable
Esta interfaz lo que permite es correr la clase game en un nuevo hilo. Esencial para hacer el game loop.

Cuando se implementa esta interfaz se debe agregar un método run a la clase. Este método se ejecutará cuando se dispare el hilo.

En este método setearemos el estado incial y que se utilice la pantalla completa de celular.
además crearemos un while para crear el game loop.

Aquí llamaremos a varios métodos muy importantes para nuestro juego (update, draw, etc que aún no están implementados y sirven para actualizar estados y dibujar la animación) y calcularemos los cuadros por segundo. Repaint y serviceRepaints son métodos de canvas que sirven para pintar en la pantalla.

Bueno... ya tenemos lo básico. la estructura. Aunque todavia no hace ni pinta nada ya hemos comenzado...

miércoles, 19 de diciembre de 2007

Video juegos para móviles!!

Hace un tiempo que he incursionado en el área de los video juegos para móviles.
Estuve desarrollando un juego de aventura muy simple al mejor estilo Super Mario Bros.

Como saben (o no) los móviles usan j2me. Una versión reducida de java. hay mucha info al repsecto en la red.

Hoy voy a empezar dandoles la estructura básica de un midlet de un juego:
(si no entienden a priori no importa ya lo vamos a ir explicando...)


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class gfx extends MIDlet
{
private Display display;
static Game game;
static gfx _instance;

public gfx()
{
display=Display.getDisplay(this);
_instance=this;
game=new Game();
new Thread(game).start();
}

public void startApp() throws MIDletStateChangeException
{
display.setCurrent(game);
}

public void pauseApp()
{}
public void destroyApp(boolean unconditional)
{}

}

Como verán lo primero que hago es heredar mi clase de midlet... muy importante. Sino no es un midlet.
Creo un display para mostrar el juego en pantalla, un Game que hereda de Canvas y que lo vamos a ver más a delante.
En el contructor creo las instancias de los distintos objetos y lanzó un hilo que ejecuta Game.
En el método startApp() le digo a display que canvas dibujar y como Game hereda de canvas le paso esa instancia.

Es mul sencillo. Ahora el tema de como implementar la clase Game es bastante más complejo. lo iremos viendo en varias entragas más.

Estoy de vuelta

Hola!

Hace un tiempo que no he tenido tiempo de crear nuevas entradas. En adelante trateré de hacerlo más seguido. :)

viernes, 13 de julio de 2007

FTP con C#

La siguiente es una clase que hice para subir, bajar, eliminar y renombrar archivos y carpetas en un servidor FTP con c#.




/// <summary>
/// Clase para hacer uso de un servidor FTP
/// </summary>
/// <date>2007-07-11</date>
/// <author>Rodrigo Barros Pascual</author>
public class ManejadorFTP
{
#region <<Propiedades>>

private String _usuario;

/// <summary>
/// Usuario FTP
/// </summary>
public String Usuario
{
get { return _usuario; }
set { _usuario = value; }
}

private String _contraseña;
/// <summary>
/// Contraseña de Usuario FTP
/// </summary>
public String Contraseña
{
get { return _contraseña; }
set { _contraseña = value; }
}

private String _servidor;

/// <summary>
/// URL servidor FTP
/// </summary>
public String Servidor
{
get { return _servidor; }
set { _servidor = value; }
}



#endregion

/// <summary>
/// Sube Documentos al servidor FTP
/// </summary>
/// <param name=pPathDocumento">Path del Documento</param>"
/// <param name=pNombreDocumento">Nombre del Documento</param>"
/// <param name=pPathServidor">Path en el servidor donde se guardará el documento</param>"

public void SubirDocumento(string pPathDocumento, string pNombreDocumento, string pPathServidor)
{
try
{
WebClient request = new WebClient();
request.Credentials = new NetworkCredential(Usuario, Contraseña);
request.UploadFile(Servidor + pPathServidor + pNombreDocumento, pPathDocumento + "\\" + pNombreDocumento);
}
catch (Exception ex)
{
throw new Exception("Error al intentar subir el documento al servidor.", ex);
}
}

/// <summary>
/// Sube archivos al servidor FTP
/// </summary>
/// <param name=pPathDocumento">Path del Documento</param>"
/// <param name=pNombreDocumento">Nombre del Documento</param>"

public void SubirDocumento(string pPathDocumento, string pNombreDocumento)
{
SubirDocumento(pPathDocumento, pNombreDocumento, String.Empty);
}

/// <summary>
/// Baja Documentos del servidor FTP
/// </summary>
/// <param name=pPathServidor">Ruta del Documento en el servidor</param>"
/// <param name=pNombreDocumento">Nombre y extensión del Documento</param>"
/// <param name=pPathFisico">Ruta del documento en el sistema de archivos</param>"
/// <param name=pNombreFisico">Nombre y extensión que se le asignará al documento en el sistema de archivos</param>"

public void BajarDocumento(string pPathServidor, string pNombreDocumento, string pPathFisico, string pNombreFisico)
{
try
{
WebClient request = new WebClient();
request.Credentials = new NetworkCredential(Usuario, Contraseña);
request.DownloadFile(Servidor + pPathServidor + pNombreDocumento, pPathFisico + "\\" + pNombreFisico);
}
catch (Exception ex)
{
throw new Exception("Error al intentar bajar el documento del servidor.", ex);
}
}

/// <summary>
/// Baja Documentos del servidor FTP
/// </summary>
/// <param name=pNombreDocumento">Nombre y extensión del Documento situado en el directorio raíz</param>"
/// <param name=pPathFisico">Ruta del documento en el sistema de archivos</param>"
/// <param name=pNombreFisico">Nombre y extensión que se le asignará al documento en el sistema de archivos</param>"

public void BajarDocumento(string pNombreDocumento, string pPathFisico, string pNombreFisico)
{
BajarDocumento(String.Empty, pNombreDocumento, pPathFisico, pNombreFisico);
}

/// <summary>
/// Crea directorios en el servidor FTP
/// </summary>
/// <param name=pPathDirectorio">Path del directorio</param>"
/// <param name=pNombreDirectorio">Nombre del directorio</param>"

public String CrearDirectorio(string pPathDirectorio, string pNombreDirectorio)
{
try
{
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(Servidor + pPathDirectorio + "/" + pNombreDirectorio);
ftp.Credentials = new NetworkCredential(Usuario, Contraseña);
ftp.KeepAlive = false;
ftp.Method = WebRequestMethods.Ftp.MakeDirectory;
ftp.Proxy = null;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
return response.StatusDescription;
}
catch (Exception ex)
{
throw new Exception("Error al intentar crear el directorio en el servidor.", ex);
}
}

/// <summary>
/// Crea directorios en el servidor FTP
/// </summary>
/// <param name=pNombreDirectorio">Nombre del directorio que se agregará al directorio raíz</param>"
public String CrearDirectorio(string pNombreDirectorio)
{
return CrearDirectorio(String.Empty, pNombreDirectorio);
}

/// <summary>
/// Elimina directorios en el servidor FTP
/// </summary>
/// <param name=pPathDirectorio">Path del directorio</param>"
/// <param name=pNombreDirectorio">Nombre del directorio</param>"

public String EliminarDirectorio(string pPathDirectorio, string pNombreDirectorio)
{
try
{
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(Servidor + pPathDirectorio + "/" + pNombreDirectorio);
ftp.Credentials = new NetworkCredential(Usuario, Contraseña);
ftp.KeepAlive = false;
ftp.Method = WebRequestMethods.Ftp.RemoveDirectory;
ftp.Proxy = null;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
return response.StatusDescription;
}
catch (Exception ex)
{
throw new Exception("Error al intentar eliminar el directorio del servidor.", ex);
}
}

/// <summary>
/// Elimina directorios en el servidor FTP
/// </summary>
/// <param name=pNombreDirectorio">Nombre del directorio que se eliminará del directorio raíz</param>"
public String EliminarDirectorio(string pNombreDirectorio)
{
return EliminarDirectorio(String.Empty, pNombreDirectorio);
}

/// <summary>
/// Renombra directorios en el servidor FTP
/// </summary>
/// <param name=pPathDirectorio">Path del directorio</param>"
/// <param name=pNombreDirectorio">Nombre del directorio</param>"
/// <param name=pNuevoNombreDirectorio">Nombre con el que se renombrará</param>"

public String RenombrarDirectorio(string pPathDirectorio, string pNombreDirectorio, string pNuevoNombreDirectorio)
{
return Renombrar(pPathDirectorio, pNombreDirectorio, pNuevoNombreDirectorio);
}

/// <summary>
/// Renombra directorios en el servidor FTP
/// </summary>
/// <param name=pNombreDirectorio">Nombre del directorio</param>"
/// <param name=pNuevoNombreDirectorio">Nombre con el que se renombrará</param>"

public String RenombrarDirectorio(string pNombreDirectorio, string pNuevoNombreDirectorio)
{
return Renombrar(String.Empty, pNombreDirectorio, pNuevoNombreDirectorio);
}

/// <summary>
/// Renombra Documentos en el servidor FTP
/// </summary>
/// <param name=pPathDocumento">Path del Documento</param>"
/// <param name=pNombreDocumento">Nombre del Documento</param>"
/// <param name=pNuevoNombreDocumento">Nombre con el que se renombrará</param>"

public String RenombrarDocumento(string pPathDocumento, string pNombreDocumento, string pNuevoNombreDocumento)
{
return Renombrar(pPathDocumento, pNombreDocumento, pNuevoNombreDocumento);
}

/// <summary>
/// Renombra Documentos en el servidor FTP
/// </summary>
/// <param name=pNombreDocumento">Nombre del Documento</param>"
/// <param name=pNuevoNombreDocumento">Nombre con el que se renombrará</param>"

public String RenombrarDocumento(string pNombreDocumento, string pNuevoNombreDocumento)
{
return Renombrar(String.Empty, pNombreDocumento, pNuevoNombreDocumento);
}

/// <summary>
/// Renombra en el servidor FTP
/// </summary>
/// <param name=pPath">Path del directorio o documento</param>"
/// <param name=pNombre">Nombre del directorio o documento</param>"
/// <param name=pNuevoNombre">Nombre con el que se renombrará</param>"

private String Renombrar(string pPath, string pNombre, string pNuevoNombre)
{
try
{
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(Servidor + pPath + "/" + pNombre);
ftp.Credentials = new NetworkCredential(Usuario, Contraseña);
ftp.KeepAlive = false;
ftp.Method = WebRequestMethods.Ftp.Rename;
ftp.RenameTo = pNuevoNombre;
ftp.Proxy = null;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
return response.StatusDescription;
}
catch (Exception ex)
{
throw new Exception("Error al intentar renombrar en el servidor.", ex);
}
}

/// <summary>
/// Elimina documentos en el servidor FTP
/// </summary>
/// <param name=pPathDirectorio">Path del documento</param>"
/// <param name=pNombreDirectorio">Nombre del documento</param>"

public String EliminarDocumento(string pPathDocumento, string pNombreDocumento)
{
try
{
FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(Servidor + pPathDocumento + "/" + pNombreDocumento);
ftp.Credentials = new NetworkCredential(Usuario, Contraseña);
ftp.KeepAlive = false;
ftp.Method = WebRequestMethods.Ftp.DeleteFile;
ftp.Proxy = null;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
return response.StatusDescription;
}
catch (Exception ex)
{
throw new Exception("Error al intentar eliminar el documento del servidor.", ex);
}
}

/// <summary>
/// Elimina documentos en el servidor FTP
/// </summary>
/// <param name=pNombreDirectorio">Nombre del documento</param>"
public String EliminarDocumento(string pNombreDocumento)
{
return EliminarDocumento(String.Empty, pNombreDocumento);
}

/// <summary>
/// Constructor por defecto.
/// </summary>
/// <param name=pUsuario">Usuario FTP</param>"
/// <param name=pContraseña">Contraseña de usuario FTP</param>"
/// <param name=pURLServidor">URL del servidor FTP</param>"

public ManejadorFTP(String pUsuario, String pContraseña, String pURLServidor)
{
Usuario = pUsuario;
Contraseña = pContraseña;
Servidor = pURLServidor;
}

}
 
Espero que les sirva.

lunes, 2 de julio de 2007

Wrapper

Disculpen, pero he estado un poco ocupado y no he podido hacer publicaciones.


Acontinuación expongo un wrapper simple que he utilizado para la llamada a un servicio genérico expuesto en un servicio web. El mismo puede recibir cualquier objeto y devolver cualquier objeto. BSService.BSInterfazService es una referencia web. Como ven utilizo la clase Serializacion de la publicación anterior.

    public static RESPONSE EjecutarServicio<REQUEST, RESPONSE>(String pNombreProcesoDeNegocios, REQUEST pEntidad)
{
try
{
BSService.BSInterfazService ws = new BS.FWK.Wrapper.BSService.BSInterfazService();
String wXML = ws.EjecutarServicio(pNombreProcesoDeNegocios, Serializacion.Serialize(pEntidad));

RESPONSE dev = (RESPONSE)Serializacion.Deserialize(typeof(RESPONSE), wXML);

return dev;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
 
Es un caso sencillo, pero efectivo.

miércoles, 20 de junio de 2007

Serialización en C#

La clase siguiente tiene todas las funciones básicas para serializar.

Pasando un objeto a Serialize te devuelve un string con el mismo serializado.
Si usas Deserialize todo lo que tenes que hacer es pasarle el tipo y el string para que te levante el objeto de nuevo.



 
/// <summary>
/// Clase que contiene todas las funciones de serialización básicas.
/// </summary>
public class Serializacion
{
#region -- Binary Serialization --

/// <summary>
/// Serializa un objeto a un archivo binario.
/// </summary>
/// <param name="fileName">Ruta del archivo en el cual depositar los bytes</param>
/// <param name="objToSerialize">Objeto en memoria a transformar en bytes</param>
public static void SerializeToBin(string fileName, object objToSerialize)
{
if (objToSerialize == null)
{
throw new Exception("El objeto a serializar no debe ser nulo.");
}

// Creo el archivo de destino y lo devuelvo a un fileStream
FileStream fs = File.Create(fileName);

// Creo un BinaryFormatter que me sirve como serializador
BinaryFormatter serializer = new BinaryFormatter();

// Le digo al serializador que guarde los bytes en un archivo
// binario, representado por el FileStream
serializer.Serialize(fs, objToSerialize);

// Cierro el FileStream
fs.Close();
}

/// <summary>
/// Deserializa un objeto a partir del contenido de un archivo binario
/// </summary>
/// <param name="fileName">Archivo desde donde toma los bytes que se
/// encuentran serializados</param>
/// <returns>objeto deserializado</returns>
public static object DeserializeFromBin(string fileName)
{
if (!File.Exists(fileName))
{
throw new Exception("El archivo de origen para deserializar " +
"no existe. No se encuentra la ruta '" + fileName + "'");
}

// Abro el archivo de origen y lo devuelvo a un fileStream
FileStream fs = File.OpenRead(fileName);

// Creo un BinaryFormatter que me sirve como deserializador
BinaryFormatter deserializer = new BinaryFormatter();

// Le digo al deserializador que me devuelva el objeto a partir
// del FileStream
object objDeserialized = deserializer.Deserialize(fs);

// Cierro el FileStream
fs.Close();

// Devuelvo el objeto deserializado
return objDeserialized;
}

#endregion

#region -- Xml Serialization using DataSet --

/// <summary>
/// Serializa a un XML y lo coloca en un DataSet
/// </summary>
/// <param name="pObject">Objeto a serializar</param>
/// <param name="pDataSet">Dataset donde se coloca el XML</param>
public static void SerializeToXml(object pObject, ref DataSet pDataSet)
{
XmlSerializer wSerializer;
MemoryStream wStream = new MemoryStream();

if (pDataSet == null)
pDataSet = new DataSet();

wSerializer = new XmlSerializer(pObject.GetType());

wSerializer.Serialize(wStream, pObject);

wStream.Position = 0;
pDataSet.ReadXml(wStream);
}

/// <summary>
/// Serializa un objeto
/// </summary>
/// <param name="pObj"></param>
/// <param name="pDataSet"></param>
public static void Serialize(object pObj, ref DataSet pDataSet)
{
XmlSerializer serializer;
MemoryStream ms = new MemoryStream();

if (pDataSet == null)
pDataSet = new DataSet();

serializer = new XmlSerializer(pObj.GetType());
serializer.Serialize(ms, pObj);

ms.Position = 0;
pDataSet.ReadXml(ms);
}
/// <summary>
/// Deserializa un objeto a partir de un DataSet
/// </summary>
/// <param name="pObjType"></param>
/// <param name="pDataSet"></param>
/// <param name="pTableName"></param>
/// <returns></returns>
public static object Deserialize(Type pObjType, DataSet pDataSet, string pTableName)
{
XmlDocument wDom = new XmlDocument();
wDom.LoadXml(pDataSet.GetXml());
return Deserialize(pObjType, wDom.GetElementsByTagName(pTableName).Item(0).OuterXml);
}

#endregion

#region -- Xml Serialization using Xml --

/// <summary>
/// Deserializa un objeto.
/// </summary>
/// <param name="pObjType"></param>
/// <param name="pXmlData"></param>
/// <returns></returns>
public static object Deserialize(Type pObjType, string pXmlData)
{
XmlSerializer wSerializer;
UTF8Encoding wEncoder = new UTF8Encoding();
MemoryStream wStream = new MemoryStream(wEncoder.GetBytes(pXmlData));

wSerializer = new XmlSerializer(pObjType);
return wSerializer.Deserialize(wStream);
}

/// <summary>
/// Deserializa un objeto.
/// </summary>
/// <param name="pObjType"></param>
/// <param name="pXmlData"></param>
/// <param name="pXPath"></param>
/// <returns></returns>
public static object Deserialize(Type pObjType, string pXmlData, string pXPath)
{
XmlDocument wDom = new XmlDocument();
wDom.LoadXml(pXmlData);
return Deserialize(pObjType, wDom.DocumentElement.SelectSingleNode(pXPath).OuterXml);
}

/// <summary>
/// Deserializa un objeto
/// </summary>
/// <param name="pTipo"></param>
/// <param name="pXml"></param>
/// <returns></returns>
public static object DeserializeFromXml(Type pTipo, string pXml)
{
XmlSerializer wSerializer;
StringReader wStrSerializado = new StringReader(pXml);
XmlTextReader wXmlReader = new XmlTextReader(wStrSerializado);
XmlSerializerNamespaces wNameSpaces = new XmlSerializerNamespaces();
object wResObj = null;

wNameSpaces.Add("", "");
wSerializer = new XmlSerializer(pTipo);
wResObj = wSerializer.Deserialize(wXmlReader);

return wResObj;
}

/// <summary>
/// Serializa un objeto
/// </summary>
/// <param name="pObj"></param>
/// <returns></returns>
public static string SerializeToXml(object pObj)
{
XmlSerializer wSerializer;
StringWriter wStwSerializado = new StringWriter();
XmlTextWriter wXmlWriter = new XmlTextWriter(wStwSerializado);
XmlSerializerNamespaces wNameSpaces = new XmlSerializerNamespaces();

wXmlWriter.Formatting = Formatting.Indented;
wNameSpaces.Add("", "");
wSerializer = new XmlSerializer(pObj.GetType());
wSerializer.Serialize(wXmlWriter, pObj, wNameSpaces);

return wStwSerializado.ToString().Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "");
}

/// <summary>
/// Serializa un objeto.
/// </summary>
/// <param name="pObj">Objeto a serializar</param>
/// <returns>Representación en XML del objeto</returns>
public static string Serialize(object pObj)
{
return Serialize(pObj, false);
}

/// <summary>
/// Serializa un objeto.
/// </summary>
/// <param name="pObj">Objeto a serializar</param>
/// <param name="pRemoveDeclaration">Indica si se debe remover el nodo de declaración</param>
/// <returns>Representación en XML del objeto</returns>
public static string Serialize(object pObj, bool pRemoveDeclaration)
{
XmlDocument wDoc = new XmlDocument();
wDoc.Load(GetStream(pObj));

if (pRemoveDeclaration && wDoc.ChildNodes.Count > 0 && wDoc.FirstChild.NodeType == XmlNodeType.XmlDeclaration)
{
wDoc.RemoveChild(wDoc.FirstChild);
}

return wDoc.InnerXml;
}


/// <summary>
/// Devuelve un stream formado a partir del objeto enviado por parámetro.
/// </summary>
/// <param name="pObj">Objeto para extraer stream</param>
/// <returns>MemoryStream</returns>
public static MemoryStream GetStream(object pObj)
{
XmlSerializer wSerializer;
MemoryStream wStream = new MemoryStream();

wSerializer = new XmlSerializer(pObj.GetType());
wSerializer.Serialize(wStream, pObj);

wStream.Position = 0;

return wStream;
}

#endregion
}

martes, 19 de junio de 2007

El Patrón Abstract Factory

Este patrón es útil cuando se necesita instanciar clases complejas y devuelven varios objetos en conjunto.

A continuación voy a presentar un ejemplo muy simple de este patrón.




using System;

using System.Collections.Generic;

using System.Text;



namespace carpinteriaFactory

{

public enum tipoTabla

{

Cuadrada,

Redonda,

Ovalada

}

class tabla

{

private tipoTabla _tipo = tipoTabla.Cuadrada;



public tipoTabla Tipo

{

get { return _tipo; }

set { _tipo = value; }

}



public tabla(tipoTabla tipo)

{

Tipo = tipo;

}

}



public enum tipoPata

{

Cuadrada,

Redonda

}

class Pata

{

private tipoPata _tipo = tipoPata.Redonda;



public tipoPata Tipo

{

get { return _tipo; }

set { _tipo = value; }

}



public Pata(tipoPata tipo)

{

this.Tipo = tipo;

}

}



public class Mueble

{



}



class Mesa : Mueble

{

private Pata[] _Patas;



public Pata[] Patas

{

get { return _Patas; }

set { _Patas = value; }

}



private tabla _tabla;



public tabla Tabla

{

get { return _tabla; }

set { _tabla = value; }

}



private String _nombre;



public String Nombre

{

get { return _nombre; }

set { _nombre = value; }

}



public Mesa() { }



public Mesa(String nombre, tipoTabla tabla, tipoPata TipoPata, Int32 CantidadPatas)

{

this.Nombre = nombre;



this.Tabla = new tabla(tabla);



Pata[] wPatas = new Pata[CantidadPatas];



for (int i = 0; i < CantidadPatas; i++)

{

wPatas[i] = new Pata(TipoPata);

}



this.Patas = wPatas;

}



public override string ToString()

{

String r = Nombre + " => " + Tabla.Tipo.ToString();

Int32 i = 1;

foreach (Pata p in Patas)

{

r = r + " Pata " + i.ToString() + ": " + p.Tipo.ToString();

i++;

}

return r;

}



}



public abstract class CarpinteriaFactory

{

abstract public Mueble crearMueble();

}





public class MesaAntiguaFactory : CarpinteriaFactory

{

public override Mueble crearMueble()

{

return new Mesa("Mesa Antigua", tipoTabla.Cuadrada, tipoPata.Cuadrada, 4);

}

}



public class MesaExoticaFactory : CarpinteriaFactory

{

public override Mueble crearMueble()

{

Mesa exotica = new Mesa("Mesa Exótica", tipoTabla.Ovalada, tipoPata.Redonda, 4);

exotica.Patas[1].Tipo = tipoPata.Cuadrada;

exotica.Patas[3].Tipo = tipoPata.Cuadrada;

return exotica;

}

}



public class MesaModernaFactory : CarpinteriaFactory

{

public override Mueble crearMueble()

{

return new Mesa("Mesa Moderna", tipoTabla.Redonda, tipoPata.Redonda, 3);

}

}



}


Como pueden ver existen las clases pata y tabla que tienen propiedades cada una que definen su ámbito.


Luego existe una clase mueble que por el momento no tiene nada pero que se podría anexar funcionalidad.


La clase mesa hereda de mueble y se compone de una tabla y un array de patas, además de un nombre.


Luego existe la clase abstracta carpinteriaFactory que define el método publico crearMueble.


Por último existen tres implementaciones de carpinteríaFactory: MesaExoticaFactory,MesaAntiguaFactory y MesaModernaFactory.


Estas clases instancian la clase mesa, crear todos los objetos necesarios y asignan valores a sus propiedades.


De esta forma se pueden obtener objetos complejos y encapsular su instanciación. Les dejo el esquema del patrón:




lunes, 11 de junio de 2007

MasterPage con ASP.Net 2.0

Este es un video que explica de forma muy básica el uso de MasterPage de ASP.NET 2.0. Tal vez sirva como introducción para los principiantes. ¡Espero que les sirva!

Las señoritas de Avignon



Las señoritas de Avignon es un cuadro de Pablo Picasso. Es un cuadro que a primera vista genera repudio. En realidad puede llegar a parecer feo. Pero lo que hizo este cuedro tan famoso es como el autor plasma los sentimientos de rechazo y astío por aquellas mujeres de forma inconciente.
Este es un cuadro de gran composición que mezcla de forma exiquisita la tradición naturalista y el movimiento abstracto. Demuestra la capacidad pictórica de Picasso y su talento para crear una obra tan revolucionaria para la época.

miércoles, 6 de junio de 2007

Los Patrones Estrategia y Decorador trabajando en conjunto

A continuación daré un ejemplo de como utilizar estos dos patrones de forma conjunta en C#, aunque se podría aplicar en VFP de forma muy sencilla siguiendo la misma lógica que venimos trabajando en los distintos artículos.





Vamos a reutilizar todas las clases que venimos publicando en el artículo de estategia, decorador y puente.




La pantalla anterior es como luce nuestra aplicación. Veamos el código para lograrlo:





using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using estrategia;


namespace WindowsApplication1
{
public partial class CodigoDinamico : Form
{
public CodigoDinamico()
{
InitializeComponent();
}

private Estrategia _Procesador= new Estrategia();

public Estrategia Procesador
{
get { return _Procesador; }
set { _Procesador = value; }
}

private void asignarFuncionalidad()
{
Lis<String> Items = new List<String>();
if (optApeNom.Checked) { Items.Add("Puente"); }
if (optNomApe.Checked) { Items.Add("NombreApellido"); }
if (optEnriquecido.Checked) { Items.Add("Enriquecido"); }
if (chkNumera.Checked) { Items.Add("AgregarNumeracion"); };
if (chkNacion.Checked) { Items.Add("AgregarNacionalidad"); };
Procesador = Dinamico.Proceso(Items);
if (chkNacion.Checked)
{
AgregarNacionalidad Nacionalidad = (AgregarNacionalidad)Procesador.Contexto.MiClase;
Nacionalidad.Nacionalidad = txtNacion.Text;
}

}

private void btn1_Click(object sender, EventArgs e)
{
asignarFuncionalidad();
txtSalida.Text = this.Procesador.ProcesarNombres(txtNombre.Text, txtApellido.Text);
}

private void chkNacion_CheckedChanged(object sender, EventArgs e)
{
if (chkNacion.Checked)
{
txtNacion.ReadOnly = false;
}
else
{
txtNacion.ReadOnly = true;
}
}


}
}
 
Como verán, en el botón btn1 siempre llamamos a asignarFuncionalidad antes de llamar a nuestra función. Esta función crea una lista genérica de String y se la pasa al metódo Estático "Proceso" de la Clase Dinamico. Veamos esta nueva clase:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace estrategia
{
public class Dinamico
{
public static Estrategia Proceso(List<String> Decoradores)
{
Assembly Ass = Assembly.GetExecutingAssembly();
Object Estra = Ass.CreateInstance("estrategia." + Decoradores[0].Trim());
Decoradores.Remove(Decoradores[0]);
Object[] o = new Object[1];
o[0] = Estra;
foreach (String deco in Decoradores)
{
o[0] = Ass.CreateInstance("estrategia." + deco, false, BindingFlags.CreateInstance,null, o, null, null);
}
Estrategia st = new Estrategia(new contexto((IProcesarNombres)o[0]));
return st;
}
}
}
La función genera la estrategia y los decoradores mediante Reflection.
Con el primer elemento de la lista selecciona la estrategia y con el resto agrega los decoradores. De esta forma se puede crear dinámicamente toda la funcionalidad. 
Si se crea una nueva clase solo se debe para el nombre de la misma en la lista genérica y sale funcionando sin tocar casi nada del código.
Este tipo de tecnología tiene muchas aplicaciones y que seguro ustedes ya deben estar pensando.

martes, 5 de junio de 2007

El Patrón Estrategia en VFP

Para implementar el patrón estrategia en Visual Fox Pro vamos a tomar el mismo ejemplo que trabajamos con C#.

Debemos crear el contexto, la estrategia abstracta, etc, etc...

Veamos un poco de código:




*Interfaz

DEFINE CLASS IComponente as Custom

FUNCTION procesarNombres()

LPARAMETERS nombre,apellido

endfunc

ENDDEFINE





*estrategia concreta

DEFINE CLASS puente as Icomponente



Caracter= ", "



PROCEDURE Caracter_ACCESS

RETURN THIS.Caracter

ENDPROC



PROCEDURE Caracter_ASSIGN

LPARAMETERS Valor

THIS.Caracter=Valor

ENDPROC



FUNCTION procesarNombres()

LPARAMETERS nombre,apellido

RETURN ALLTRIM(Apellido) + this.Caracter + ALLTRIM(nombre)

ENDFUNC



ENDDEFINE



*estrategia concreta

DEFINE CLASS NombreApellido as Icomponente



Caracter= " "



PROCEDURE Caracter_ACCESS

RETURN THIS.Caracter

ENDPROC



PROCEDURE Caracter_ASSIGN

LPARAMETERS Valor

THIS.Caracter=Valor

ENDPROC



FUNCTION procesarNombres()

LPARAMETERS nombre,apellido

RETURN ALLTRIM(nombre) + this.Caracter + ALLTRIM(Apellido)

ENDFUNC



ENDDEFINE



*estrategia concreta

DEFINE CLASS Enriquecido as Icomponente



Caracter= " - "



PROCEDURE Caracter_ACCESS

RETURN THIS.Caracter

ENDPROC



PROCEDURE Caracter_ASSIGN

LPARAMETERS Valor

THIS.Caracter=Valor

ENDPROC



FUNCTION procesarNombres()

LPARAMETERS nombre,apellido

RETURN "Apellido: " + ALLTRIM(Apellido) + this.Caracter + "Nombre: " + ALLTRIM(nombre)

ENDFUNC



ENDDEFINE



*clase de contexto

DEFINE CLASS contexto as Custom





MiClase= null



PROCEDURE MiClase_ACCESS

RETURN THIS.MiClase

ENDPROC



PROCEDURE MiClase_ASSIGN

LPARAMETERS Valor

THIS.MiClase=Valor

ENDPROC



PROCEDURE init()

LPARAMETERS Clase

This.MiClase=Clase

ENDPROC



ENDDEFINE



*estrategia abstracta

DEFINE CLASS Estrategia as Icomponente



contexto= " "



PROCEDURE contexto_ACCESS

RETURN THIS.contexto

ENDPROC



PROCEDURE contexto_ASSIGN

LPARAMETERS Valor

THIS.contexto=Valor

ENDPROC



PROCEDURE init()

LPARAMETERS Clase

This.contexto=Clase

ENDPROC



FUNCTION procesarNombres()

LPARAMETERS nombre,apellido

RETURN this.contexto.MiClase.procesarNombres(nombre,apellido)

ENDFUNC



ENDDEFINE






Se agregaron dos estrategias además de Puente: NombreApellido y Enriquecido. Las misma realizan distintas acciones para tener de ejemplo.





Veamos ahora como utilizar esto en un formulario:



Al presionar cada botón estamos utilizando una estrategia distinta.

El código del primer botón:


Estrategia = NEWOBJECT("Estrategia",'','',NEWOBJECT("contexto",'','',NEWOBJECT("NombreApellido")))
ThisForm.txt1.Value=Estrategia.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
RELEASE Estrategia

El código del segundo botón:


Estrategia = NEWOBJECT("Estrategia",'','',NEWOBJECT("contexto",'','',NEWOBJECT("Puente")))
ThisForm.txt2.Value=Estrategia.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
RELEASE Estrategia

El código del tercer botón:


Estrategia = NEWOBJECT("Estrategia",'','',NEWOBJECT("contexto",'','',NEWOBJECT("Enriquecido")))
ThisForm.txt3.Value=Estrategia.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
RELEASE Estrategia

En el init del formulario:


SET PROCEDURE TO "estrategia.prg"

donde "estrategia.prg" es el archivo donde están escritas las clases.

Cambiar de una estrategia a otra es muy simple. En entornos donde la tasa de cambios es muy alta este tipo de metodología es excelente.

En las siguientes publicaciones veremos cómo utilizar varios patrones a la vez.-

El Patrón Estrategia en C#

Vamos a ver ahora como implementar el patrón estrategia en c#.

Vamos a reutilizar las clases que venimos trabajando con el puente y el decorador. Pues esa es la idea de los patrones ¿no?. Vamos a reutilizar, entonces, la interfaz "procesarNombres" y la Clase "Puente" dentro del namespace estrategia.

Vamos a crear la clase de contexto:



using System;

using System.Collections.Generic;

using System.Text;



namespace estrategia

{

public class contexto

{

IProcesarNombres _MiClase;



public IProcesarNombres MiClase

{

get { return _MiClase; }

set { _MiClase = value; }

}



public contexto(IProcesarNombres Clase)

{

MiClase = Clase;

}

}

}
 
Ahora crearemos la estrategia abstracta que implementa IProcesarNombres.
 



using System;

using System.Collections.Generic;

using System.Text;



namespace estrategia

{

public class Estrategia : IProcesarNombres

{

private contexto _contexto;



public contexto Contexto

{

get { return _contexto; }

set { _contexto = value; }

}



public Estrategia(contexto clase)

{

Contexto = clase;

}



public Estrategia()

{

}



public void AsignarContexto(contexto clase)

{

Contexto = clase;

}



public string ProcesarNombres(string Nombre, string Apellido)

{

return Contexto.MiClase.ProcesarNombres(Nombre, Apellido);

}



}

}
 

Ya teniendo el contexto y la estategia abstracta podemos hacer las estrategias concretas que también implementan IProcesarNombres. 
Vamos a utlizar aquí la clase puente que ya implementa esta interfaz y además vamos a crear dos estrategias más: Una que muestre nombre y apellido, y otra que Muestre 'Apellido:' y 'Nombre:' y sus respectivos valores.



using System;

using System.Collections.Generic;

using System.Text;



namespace estrategia

{

public class NombreApellido:IProcesarNombres

{

private String _caracter = " ";



public String Caracter

{

get { return _caracter; }

set { _caracter = value; }

}



public string ProcesarNombres(string Nombre, string Apellido)

{

return Nombre.Trim() + Caracter + Apellido.Trim();

}

}
}





using System;

using System.Collections.Generic;

using System.Text;



namespace estrategia

{

public class Enriquecido:IProcesarNombres

{

private String _caracter = " - ";



public String Caracter

{

get { return _caracter; }

set { _caracter = value; }

}



public string ProcesarNombres(string Nombre, string Apellido)

{

return "Apellido: " + Apellido.Trim() + Caracter + "Nombre: " + Nombre.Trim();

}

}

}
Ahora sólo nos falta ver como deberíamos utilizarlo. En la siguiente figura se muestra el formulario.
 
El código que contiene es el siguiente:



using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using estrategia;



namespace WindowsApplication1

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}



private void btn1_Click(object sender, EventArgs e)

{

Estrategia Proceso = new Estrategia(new contexto(new NombreApellido()));

txt1.Text = Proceso.ProcesarNombres(txtNombre.Text, txtApellido.Text);

}



private void button1_Click(object sender, EventArgs e)

{

Estrategia Proceso = new Estrategia(new contexto(new Puente()));

txt2.Text = Proceso.ProcesarNombres(txtNombre.Text, txtApellido.Text);

}



private void btn3_Click(object sender, EventArgs e)

{

Estrategia Proceso = new Estrategia(new contexto(new Enriquecido()));

txt3.Text = Proceso.ProcesarNombres(txtNombre.Text, txtApellido.Text);

}



}

}
 
Como se puede observar es muy sencillo cambiar de estrategia y obtener un resultado distinto cada vez.

lunes, 4 de junio de 2007

El Patrón Estrategia

La estrategia describe una solución al problema principal inherente a escribir el código principal – como accionar con demandas inesperadas para cambios de implementación.
La definición formal de estrategia, fue dada por el "GoF" es:


"Define una familia de algoritmos, encapsula cada uno, y los hace intercambiables. La estrategia permite al algoritmo variar en dependencia del cliente que la utilice."



Esto resulta algo oscuro en una primera lectura, vamos a tratar de aclarar.


¿Cuáles son los componentes de una estrategia?


Una estrategia tiene tres componentes esenciales: "la estrategia abstracta", que es una clase que define la interfaz, y funcionalidad genérica para las "estrategias concretas", que son subclases que definen varias implementaciones posibles. El tercer componente, el "Contexto", es responsable de controlar la referencia a la implementación actual.






Si está pensando que este diagrama se ve muy similar al puente, está en lo cierto. Puede pensar incluso, que el patrón de estrategia es como un "puente dinámico" en el cual un final (el contexto) es estático; pero el otro (la estrategia) está creado por varias variantes con el propósito de entregar una implementación específica en un momento específico. La esencia del patrón es tal que la decisión como establecer qué subclase debe ser instanciada en cualquier momento dependa del estado del "cliente". El ejemplo clásico del patrón descansa en la responsabilidad de instanciar la estrategia concreta del objeto en el cliente, con la que pasa la referencia al contexto.

viernes, 1 de junio de 2007

Implementando el Patrón Decorador con Visual Fox Pro

Siguiendo la misma línea del artículo anterior, vamos a implementar el patrón decorador en Visual Fox Pro.





Dentro de un prg para mi caso ejemplos.prg vamos a escrbir las clases:






DEFINE CLASS IComponente as Custom
FUNCTION procesarNombres()
LPARAMETERS nombre,apellido
endfunc
ENDDEFINE



DEFINE CLASS puente as Icomponente

Caracter= ", "

PROCEDURE Caracter_ACCESS
RETURN THIS.Caracter
ENDPROC

PROCEDURE Caracter_ASSIGN
LPARAMETERS Valor
THIS.Caracter=Valor
ENDPROC

FUNCTION procesarNombres()
LPARAMETERS nombre,apellido
RETURN ALLTRIM(Apellido) + this.Caracter + ALLTRIM(nombre)
ENDFUNC

ENDDEFINE



DEFINE CLASS decorador as Icomponente

componente=null

PROCEDURE init ()
LPARAMETERS clase
this.componente=clase
ENDPROC

FUNCTION procesarNombres()
LPARAMETERS nombre,apellido
RETURN this.componente.procesarNombres(nombre,apellido)
ENDFUNC

ENDDEFINE


DEFINE CLASS AgregarNumeracion as decorador

padre=null

Numero= 1

PROCEDURE Numero_ACCESS
RETURN THIS.Numero
ENDPROC

PROCEDURE Numero_ASSIGN
LPARAMETERS Valor
THIS.Numero=Valor
ENDPROC

Separador= " - "

PROCEDURE Separador_ACCESS
RETURN THIS.Separador
ENDPROC

PROCEDURE Separador_ASSIGN
LPARAMETERS Valor
THIS.Separador=Valor
ENDPROC

PROCEDURE init ()
LPARAMETERS clase
this.padre=NEWOBJECT("decorador",'','',clase)
ENDPROC

FUNCTION procesarNombres()
LPARAMETERS nombre,apellido
RETURN ALLTRIM(STR(this.numero))+ this.separador + this.padre.procesarNombres(nombre,apellido)
ENDFUNC

ENDDEFINE

DEFINE CLASS AgregarNacionalidad as decorador

padre=null

Nacionalidad= "Argentina"

PROCEDURE Nacionalidad_ACCESS
RETURN THIS.Nacionalidad
ENDPROC

PROCEDURE Nacionalidad_ASSIGN
LPARAMETERS Valor
THIS.Nacionalidad=Valor
ENDPROC

Separador= " - "

PROCEDURE Separador_ACCESS
RETURN THIS.Separador
ENDPROC

PROCEDURE Separador_ASSIGN
LPARAMETERS Valor
THIS.Separador=Valor
ENDPROC


PROCEDURE init ()
LPARAMETERS clase
this.padre=NEWOBJECT("decorador",'','',clase)
ENDPROC

FUNCTION procesarNombres()
LPARAMETERS nombre,apellido
RETURN this.padre.procesarNombres(nombre,apellido) + this.separador + this.nacionalidad
ENDFUNC

ENDDEFINE






Ya estamos listos para consumir las clases. Creamos el mismo formulario que para C#:



En el Init del formulario hacemos una referencia al archivo de las clases...


SET PROCEDURE TO "ejemplos.prg"

En el primer botón colocamos...


MiPuente = NEWOBJECT("puente")
ThisForm.txt1.Value=MiPuente.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
RELEASE MiPuente

En el segundo...


MiPuente = NEWOBJECT("puente")
*agrego los decoradores
deco1 = NEWOBJECT("AgregarNumeracion",'','',MiPuente)
*llamo al método
ThisForm.txt2.Value=deco1.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
*elimino los objetos
RELEASE MiPuente
RELEASE deco1

En el tercero...


MiPuente = NEWOBJECT("puente")
*agrego los decoradores
deco2 = NEWOBJECT("AgregarNacionalidad",'','',MiPuente)
deco2.nacionalidad=thisform.txtNacionalidad.value
*llamo al método
ThisForm.txt3.Value=deco2.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
*elimino los objetos
RELEASE MiPuente
RELEASE deco2

En el cuarto...


MiPuente = NEWOBJECT("puente")
*agrego los decoradores
deco1 = NEWOBJECT("AgregarNumeracion",'','',MiPuente)
deco2 = NEWOBJECT("AgregarNacionalidad",'','',deco1)
deco2.nacionalidad=thisform.txtNacionalidad.value

*llamo al método
ThisForm.txt4.Value=deco2.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
*elimino los objetos
RELEASE MiPuente
RELEASE deco1
RELEASE deco2

y por último en el quinto...


*agrego los decoradores
deco2 = NEWOBJECT("AgregarNacionalidad",'','',NEWOBJECT("AgregarNumeracion",'','',NEWOBJECT("puente")))
deco2.nacionalidad=thisform.txtNacionalidad.value
*llamo al método
ThisForm.txt5.Value=deco2.procesarnombres(thisform.txtNombre.Value,thisform.txtApellido.Value)
*elimino los objetos
RELEASE deco2

Como se puede ver, al igual que en artículo anterior, con una sola línea se puede agregar funcionalidad de forma dinámica al objeto.


deco2 = NEWOBJECT("AgregarNacionalidad",'','',NEWOBJECT("AgregarNumeracion",'','',NEWOBJECT("puente")))

El patrón decorador es muy útil en ambientes multiempresa, por ejemplo, donde las reglas de negocio son muy cambiantes entre sí, e incluso entre sucursales. Con este patrón se podría manejar de forma más eficiente la diversidad de funcionalidades que el trabajo multiempresa significa.


Implementando el Patrón Decorador con C#

Para empezar vamos a Implementar el patrón decorador en C#. Lo primero será crear la interfaz IProcesarNombres.





using System;
using System.Collections.Generic;
using System.Text;
namespace patronDecorador
{
public interface IProcesarNombres
{
String ProcesarNombres(String Nombre, String Apellido);
}
}






Ahora bien, nuestra clase (en este caso puente) debe implementar esta interfaz.





using System;
using System.Collections.Generic;
using System.Text;
namespace patronDecorador
{
public class Puente : IProcesarNombres
{
private String _caracter = ", ";
public String Caracter
{
get { return _caracter; }
set { _caracter = value.Trim(); }
}
public String ProcesarNombres(String Nombre, String Apellido)
{
return Apellido.Trim() + Caracter + Nombre.Trim();
}
}
}







El decorador también debe implementarla como se muestra a continuación.






using System;
using System.Collections.Generic;
using System.Text;
namespace patronDecorador
{
public abstract class Decorador : IProcesarNombres
{
protected IProcesarNombres _Componente;
public Decorador(IProcesarNombres Clase)
{
_Componente = Clase;
}
public virtual String ProcesarNombres(String Nombre, String Apellido)
{
return _Componente.ProcesarNombres(Nombre, Apellido);
}
}
}



Luego creamos dos clases decoradoras:





1) AgregarNumeracion:



using System;
using System.Collections.Generic;
using System.Text;
namespace patronDecorador
{
public class AgregarNumeracion : Decorador
{
private Int32 _numero = 1;
public Int32 Numero
{
get { return _numero; }
set { _numero = value; }
}

private String _separador = " - ";
public String Separador
{
get { return _separador; }
set { _separador = value; }
}

public AgregarNumeracion(IProcesarNombres Clase): base(Clase){}

public override String ProcesarNombres(String Nombre, String Apellido)
{
return Numero.ToString()+ Separador + base.ProcesarNombres(Nombre, Apellido);
}
}
}





2) AgregarNacionalidad :



using System;
using System.Collections.Generic;
using System.Text;
namespace patronDecorador
{
public class AgregarNacionalidad : Decorador
{
private String _Nacionalidad= "Argentina";
public String Nacionalidad
{
get { return _Nacionalidad; }
set { _Nacionalidad = value.Trim(); }
}

private String _separador = " - ";
public String Separador
{
get { return _separador; }
set { _separador = value; }
}

public AgregarNacionalidad(IProcesarNombres Clase): base(Clase){}

public override String ProcesarNombres(String Nombre, String Apellido)
{
return base.ProcesarNombres(Nombre, Apellido) + Separador +Nacionalidad;
}
}
}








Ahora ya se puede comenzar a usarlas. Creamos el siguiente formulario:



A continuación el código que le da la funcionalidad:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using patronDecorador;
namespace WindowsApplication1
{
public partial class Decorador : Form
{
public Decorador()
{
InitializeComponent();
}

private void btn1_Click(object sender, EventArgs e)
{
Puente MiClase = new Puente();
txt1.Text= MiClase.ProcesarNombres(txtNombre.Text,txtApellido.Text);
}
private void btn2_Click(object sender, EventArgs e)
{
Puente MiClase = new Puente();
AgregarNumeracion Deco1 = new AgregarNumeracion(MiClase);
txt2.Text = Deco1.ProcesarNombres(txtNombre.Text,txtApellido.Text);
}
private void btn3_Click(object sender, EventArgs e)
{
Puente MiClase = new Puente();
AgregarNacionalidad Deco2 = new AgregarNacionalidad(MiClase);
Deco2.Nacionalidad = txtNacionalidad.Text;
txt3.Text = Deco2.ProcesarNombres(txtNombre.Text, txtApellido.Text);
}
private void btn4_Click(object sender, EventArgs e)
{
Puente MiClase = new Puente();
AgregarNumeracion Deco1 = new AgregarNumeracion(MiClase);
AgregarNacionalidad Deco2 = new AgregarNacionalidad(Deco1);
Deco2.Nacionalidad = txtNacionalidad.Text;
txt4.Text = Deco2.ProcesarNombres(txtNombre.Text, txtApellido.Text);
}
private void btn5_Click_1(object sender, EventArgs e)
{
AgregarNacionalidad FormaReducida = new AgregarNacionalidad(new AgregarNumeracion(new Puente()));
FormaReducida.Nacionalidad = txtNacionalidad.Text;
txt5.Text = FormaReducida.ProcesarNombres(txtNombre.Text, txtApellido.Text);
}
}
}

Como verán con una sola y sencilla linea se agrega funcionalidad al objeto:

AgregarNacionalidad FormaReducida = new AgregarNacionalidad(new AgregarNumeracion(new Puente()));

Esta es la principal ventaja del patrón Decorador.

A continuación su aplicación en VFP.