jueves, 28 de agosto de 2014

La tienda de animales VI

Stock online

Patrón Adapter y Flyweight

Hola de nuevo a todos y todas. Después de una semana de descanso volvemos con la sexta entrada sobre los patrones GOF. En este caso seguiremos con el patrón Adapter para implementar la conexión a los servicios web de un proveedor para actualizar nuestro stock y el patrón Flyweight que nos servirá para crear los artículos que obtendremos después con la conexión al webservice de nuestro proveedor.



Diagrama de Clases

Para el patrón Flyweight queda así:

 

El patrón Flyweight se utiliza para reducir gasto de recursos innecesarios y mejorar el rendimiento global. En nuestro caso al acceder al webservice del proveedor obtendremos una serie de artículos/productos que deberemos instanciar en nuestro sistema. Estos artículos tienen información común que la podemos generalizar en una clase padre.

Para el patrón Adapter el diagrama sería este:
 


El patrón Adapter se compone de tres clases que realizan todo el trabajo.

Imaginemos, por ejemplo, nuestro sistema de descarga de productos desde el proveeedor de la tienda. El proveedor nos ofrece un WebService que tiene una interfaz pública y que llamaremos en nuestro ejemplo WebMethodAdaptee.

Este web service no lo podemos tocar nosotros de ninguna manera y nuestro problema surge porque tenemos en nuestro sistema un módulo que se encarga de gestionar nuestro stock, y hasta ahora para recuperar el stock ha estado haciendo uso de un método con una interfaz distinta a la que ofrece el webservice. Vaya esto es un problema.

Pero aquí es donde aparece nuestro Adapter. Crearemos una clase,WebMethodAdapter, que incluirá un nivel de indirección extra entre el WebMethodTaget y el WebMethodAdaptee y posibilitará que estos dos puedan funcionar juntos.

Implementación

En primer lugar hablaremos de la parte que se encargará de la creación de los artículos con el patrón Flyweight. Para ello debemos crear una clase abstracta llamada Article que representará a los productos obtenidos desde el webservice del proveedor. Como se ve dispone de métodos y propiedades comunes a todos los productos.

public abstract class Article
{
        protected int _reference;
        protected int _quantity;

        public abstract void Remove(int quantity);
        public abstract void Add(int quantity);
        public abstract void Display();

}


A continuación debemos crea dos clases que hereden de Article. UnsharedConcreteArticle nos servirá para aquellos casos en los que no se deba compartir información y ConcreteArticle que será para cuando sí que haya información a compartir.

public class UnsharedConcreteArticle : Article
{
        public override void Remove(int quantity)
        {
            _quantity -= quantity;
        }

        public override void Add(int quantity)
        {
            _quantity += quantity;
        }

        public override void Display()
        {
            Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name + " " + _quantity);
        }
}


public class ConcreteArticle : Article
{
        public override void Remove(int quantity)
        {
            _quantity -= quantity;
        }

        public override void Add(int quantity)
        {
            _quantity += quantity;
        }

        public override void Display()
        {
            Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name + " " + _quantity);
        }
}


Por último, sólo nos queda crear la clase factoría que cree las instancias de los artículos según su tipo. La clase ArticleFactory dispone de una tabla Hash para devolver una instancia del tipo que recibe. 

public class ArticleFactory
{
        private Hashtable _articles = new Hashtable();

        public ArticleFactory()
        {
            _articles.Add(typeof(Aligator), new ConcreteArticle());
            _articles.Add(typeof(Cat), new ConcreteArticle());
            _articles.Add(typeof(Chamaleon), new ConcreteArticle());
            _articles.Add(typeof(Dingo), new ConcreteArticle());
            _articles.Add(typeof(Dog), new ConcreteArticle());
            _articles.Add(typeof(Eagle), new ConcreteArticle());
            _articles.Add(typeof(Lion), new ConcreteArticle());
            _articles.Add(typeof(Parrot), new ConcreteArticle());
        }

        public  Article GetArticle(Type key)
        {
            return (Article)_articles[key];
        }
}


A continuación seguiremos con el patrón Adapter que hace uso de este patrón Flyweight.

Para simular el webservice del proveedor crearemos la clase WebMethodAdaptee que tendrá un método público y nos dará como respuesta una lista de productos. Esto en un caso real podría ser un webservice que devolvería, por ejemplo, un json, un mensaje xml, etc...

public class WebMethodAdaptee
{
        public string[] SpecificRequest()
        {
            return new[] { "Aligator", "Cat", "Chamaleon", "Dingo", "Dog", "Eagle", "Lion", "Parrot" };
        }
}

El segundo punto a implementar sería la clase que debe poder conectarse al webservice y que ahora no puede. Nuestro WebMethodTarget. Realmente usaremos una clase abstracta que tendrá un método público a implementar y que será el que realmente se conecte con el WebService del proveedor.

public abstract class WebMethodTarget
{
        public abstract List<Article> Request();
}


Por último, debemos implementar la clase que realiza la indirección y permite que todo funcione. La clase WebMethodAdapter con la implementación de la clase abstracta.

public class WebMethodAdapter : WebMethodTarget
{
        public override List<Article> Request()
        {
            List<Article> articles = new List<Article>();




            // Flyweight

            ArticleFactory factory = new ArticleFactory();

            WebMethodAdaptee a = new WebMethodAdaptee();
            foreach (string item in a.SpecificRequest())
            {
                articles.Add(factory.GetArticle(getType(item)));
            }
            return articles;
        }

        private Type getType(string item)
        {
            switch (item)
            {
                case "Aligator":
                    return typeof(Aligator);
                case "Cat":
                    return typeof(Cat);
                 case "Lion":
                    return typeof(Lion);
                 case "Chamaleon":
                    return typeof(Chamaleon);
                 case "Dingo":
                    return typeof(Dingo);
                default:
                    return typeof(Eagle);
            }
        }
}




Podemos ver cómo se ha incluido el uso del patrón Flyweight a la hora de crear los Article. Cuando recuperamos el artículo desde el webservice miramos su tipo y en función de este llamamos a la clase factoría que nos devuelve una instancia de este tipo de artículo.

Hasta aaquí esta entrada en la que hemos visto cómo podemos solucionar problemas relacionados con la incompatibilidad entre interfaces y cómo mejorar el rendimiento del sistema reduciendo el consumo de memoria.

Hasta pronto.

miércoles, 13 de agosto de 2014

La tienda de animales V

 Una vuelta de tuerca más

Patrones Estructurales

Hola de nuevo a todos y todas. En esta quinta entrada sobre los patrones GOF empezaremos una nueva tarea de modelado e implementación ya que hemos visto todos los patrones Creacionales. 

En esta nueva etapa veremos los patrones Estructurales que se caracterizan porque solucionan aspectos de composición (agregración) de clases y objetos.

Lo que vamos a hacer es añadir nuevas clases al sistema ya existente o modificaremos las que ya tenemos para añadir nuevas funcionalidades y características.


Definición de las nuevas características del sistema

Nuestro sistema va a tener la facultad de conectarse a un servicio web de un proveedor para actualizar nuestro stock y poder realizar pedidos online. De esta forma podremos crear un módulo de Inventario con el stock de productos de la tienda y que podremos tener almacenado en una BD local y en una copia de respaldo online.

Además, se creará un sistema de contabilidad básico que lleve el balance de la tienda con las ventas del día. Este sistema de contabilidad será particular para cada tipo de tienda.

Otra linea de negocio que nuestra tienda ha iniciado recientemente es  el club de amigos en defensa de los animales. Para ello se ha puesto en contacto con una red de asociaciones del gremio y colaborará buscando a personas entre sus clientes que quieran adoptar o apadrinar a un animal. Pero lo curioso del caso es que algunos de estos animales provienen de circos que se han visto obligados a cerrar por la crisis y presentan un plus un tanto especial. Son animales virtuosos que pueden realizar cosas como por ejemplo cantar, bailar, tocar el piano, etc...

Diagrama de Clases

El diagrama con las nuevas clases que se deben añadir al sistema también presenta algunas de las clases del modelado anterior, pero con algunas modificaciones. Ya que los nuevos requerimientos amplian las funcionalidades de algunas clases ya existentes.




Justificación de los patrones elegidos y clases implicadas

  • Adapter: Este patrón se utiliza cuando un cliente espera una interfaz determinada y la clase a utilizar presenta otra diferente, pero no la podemos cambiar porque por ejemplo, es un módulo externo compilado. Para solucionarlo se crea una nueva clase intermedia que añade un nivel de indirección con la misma interfaz que espera el cliente y se hace la petición a la interfaz adaptada. En nuestro ejemplo lo utilizaremos para acceder al webservice de un proveedor que nos envia su listado de artículos.
    • WebMethodTarget: Es la clase abstracta con la interfaz que espera el cliente.
    • WebMethodAdapter: la clase que implementa la interfaz.
    • WebMethodAdaptee: la clase que tiene la interfaz a adaptar, o sea el webservice.
  • Bridge: Permite desacoplar la interfaz de una funcionalidad de su implementación de forma que pueda cambiar sin modificarse la interfaz. Lo utilizaremos para representar el sistema de Balance de la tienda, ya al tener dos tipos de tienda, no tenemos que cambiar nada si modificamos la lógica del balance.
    • Balance: Tiene la interfaz con un enlace a la implementación.
    • SavageBalance, HomeBalance: el balance de cada tipo de tienda.
    • BalanceImpl: La interfaz de la implementación.
    • SavageBalanceImpl, HomeBalanceImpl: la implementación de la interfaz de la implementación.
  • Composite: Este patrón nos permite hacer objetos complejos a partir de objetos simples o similares. Para nuestro caso lo usaremos para modelar el club de amigos de los animales. Las partes simples serían cada uno de los miembros y las complejas serían los grupos o subgrupos que se componen de estos miembros.
    • Club: Tiene la interfaz para manejar las partes.
    • Group: es la parte compleja del patrón que se compone de Member.
    • Member: la parte simple del patrón.
  • Decorator: Permite añadir funcionalidades de forma dinámica a un objeto, es decir, en tiempo de ejecución. De esta forma se reduce la complejidad del modelo ya que se elimina la necesidad de herencia y facilita modificar el modelo sin tener que tocar la implementación.  En el nuevo modelo se utilizará para dar respuesta a los animales que tienen talentos especiales. Evitaremos complicar la herencia del árbol Animal añadiendo una nueva clase TalentAnimal que se encargará de los nuevos requerimientos.
    • Animal: A esta clase que ya estaba presente en el modelo inicial le queremos añadir nuevas funcionalidades. Ahora habrá animales que tienen talentos.
    • Bird, Reptil, Feline, Canine: Se les ampliará su funcionalidad.
    • TalentAnimal: Es la clase abstracta que tendrá la nueva funcionalidad.
    • DanceAnimal, SingAnimal: Clases que implementarán TalentAnimal y que serán las clase que representarán las nuevas funcionalidades.
  • Facade: Cuando tenemos un sistema con una gran complejidad ya que tiene multitud de clases y/0 su interacción es también alta podemos crear una clase Facade que haga de punto de entrada al sistema y de esta forma el cliente se abstrae del sistema y sólo utiliza la interfaz simplificada del mismo. Lo utilizaremos para modelar el inventario de la tienda que tiene una gran cantidad de clases y métodos.
    • Inventory: es la clase facade que abstrae el sistema de inventario.
    • ConnectionData: clase del sistema que crea la conexión a la BD.
    • LoadData: obtiene el contenido de la BD.
    • SaveData: almacena el inventario en la BD.
  • Flyweight: elimina o reduce la redundancia de información cuando vamos a necesitar muchas instancias de objetos que tienen la misma información básica. Los Articles de nuestra tienda disponen de campos como pueden ser la referencia o la cantidad por lo que al tener que crear muchos Articles bajamos el coste en cuanto a memoria utilizada.
    • Article: Es la clase abstracta con los campos comúnes.
    • ConcreteArticle: una implementación de un Article.
    • UnsharedConcreteArticle: otra implementación que no comparte información.
    • ArticleFactory: la clase que se encarga de gestionar la creación y mantenimiento de las instancias de Article creadas.
  • Proxy: Cuando necesitamos tener una representación de otro objeto porque la creación o el acceso al objeto real es muy costoso o presenta características especiales como por ejemplo la comprobación de permisos de acceso. En nuestro caso para poder modelar el acceso a la BD remota crearemos un proxy que representará una BD local y sólo accederá a la real cuando sea absolutamente necesario.
    • DB: Es la interfaz común que comparten el objeto real de acceso a la base de datos y el Proxy.
    • DBProxy: la clase que controla el acceso al objeto real SQLServer.
    • SQLServer: es la clase de acceso a la BD real.
Hasta aquí llegamos con esta entrega y os invito a seguir la siguiente en la que nos adentraremos en la implementación es este nuevo modelo ampliado con los patrones estructurales.
Hasta pronto.

viernes, 8 de agosto de 2014

La tienda de animales IV

¿Los contactos de la tienda son importantes? 

Patrón Factory Method

Hola de nuevo a todos y todas. En esta cuarta entrada sobre los patrones GOF seguiremos con el patrón Factory Method para implementar la agenda de contactos de la tienda.

Recordemos que nuestra tienda podía tener clientes representados por la clase Customer y proveedores representados por la clase Suplier.

Cada uno de ellos tendrá información específica que no es necesaria en el otro. Por ejemplo, el proveedor tendrá información sobre su compañía o el tipo de descuento que le hace a la tienda en sus productos.

El diagrama de clases es el que aparece a continuación:



Implementación

El patrón Factory Method tiene una estructura muy sencilla. La clase abstracta Contact es de la que heredan las clases Customer y Suplier que representan a los clientes y los proveedores. En esta hay un método abstracto que se encarga de formar el contenido de cada clase llamando a las clases de Data adecuadas según sea el tipo de Contact.

El código queda así:

public abstract class Contact
{
  List<Data> _datas = new List();

  public List<Data> Datas
  {
    get { return _datas; }
  }

  public Contact()
  {
    this.CreateData();
  }

  public abstract void CreateData();

  public void Show()
  {
    Console.WriteLine(">> " + GetType().Name);

    foreach (var item in _datas)
    {
       Console.WriteLine("\t- " +      item.GetType().Name);
    }
  }
}

public class Customer : Contact
{
  public override void CreateData()
  {
    Datas.Add(new PersonalData());
    Datas.Add(new SalesData());
  }
}

public class Supplier : Contact
{
  public override void CreateData()
  {
    Datas.Add(new CompanyData());
    Datas.Add(new DiscountData());
  }
}

Se puede ver una lista de Data en la clase Contact en la que se almacena la información de cada contacto. Con el método CreateData() se añaden objetos del tipo Data adecuados a la clase Contact correspondiente.

Lo bueno de esto es que si más adelante queremos crear otro tipo de Data sólo habría que hacer un Add en el métdo CreateData() de la subclase de Contact que esté relacionada.
Por ejemplo, si necestamos añadir información sobre las redes sociales que utiliza el contacto usualmente sólo habría que crear una clase que herede de Data y con los campos específicos.
Después en la clase Suplier habría que incluir en el método CreateData() un nuevo Add llamando al constructor de la nueva subclase de Data creada.
Por lo que se refiere a la clase abstracta Data por motivos de simplicidad no se han añadido campos y tampoco en sus subclases. Lo importante aquí es la herencia que se produce entre ellas. De esta forma podemos tener la lista de Data en cada subclase de Contact sin especificar el subtipo instanciado realmente.

public abstract class Data { }

public class SalesData : Data { }

public class CompanyData : Data { }

public class PersonalData : Data { }

public class DiscountData : Data { }

En resumen, hemos visto cómo podemos tener colecciones de objetos que amplian las propiedades de un objeto concreto de forma que estemos preparados ante futuros cambios del modelo sin afectar demasiado a nuestra implementación. Sólo hay que tocar un método añadiendo tantas lineas Add como nuevos subtipos de Data hayamos creado.

Un ejemplo de uso de este patrón en el cliente se resumiría a crear objetos de los subtipos de contact. En el codigo de ejemplo siguiente se utiliza un array como almacén de los Contact.

// Factory Method
Contact[] contacts = new Contact[2];

contacts[0] = new Customer();
contacts[1] = new Supplier();

contacts[0].Show();
contacts[1].Show();


Y esto es todo por ahora. En la próxima entrega hablaremos de los patrones Estructurales y mostraremos el siguiente módulo de la tienda de animales que utilizará estos patrones.

Hasta pronto.




viernes, 1 de agosto de 2014

La tienda de animales III

¿Llegan las ofertas en Packs? 

Patrones Prototype y Builder

Hola de nuevo a todos y todas. En esta tercera entrada sobre los patrones GOF seguiremos con el patrón Prototype y el patrón Builder

Si hacemos un poco de memoria en el sistema teníamos distintos tipos de animales organizados por familias que heredaban de una clase abstracta base llamada Animal. Todos los animales compartían los mismos atributos por lo que si utilizásemos el patrón Prototype se encapsularía su copia de tal forma que se ganaría en seguridad y se evitarían posibles olvidos a la hora de copiar los datos de los animales.

Por otro lado, una de las funcionalidades que se habían previsto era la de poder crear los packs de animales según ofertas de temporada. Estos packs tenían en común su estructura: un animal, un accesorio, su comida y el precio del pack. El patrón Builder sería perfecto ya que nos permitiría separar la estructura de su implementación específica según cada Pack. 

Por ejemplo, si quisiéramos crear un ChristmasPack este tendría un animal Carnivore, un bozal como complemento ( por si al bicho le da por morder ), un paquete de la comida preferida del animal y su precio.

El diagrama de clases del patrón Prototype queda dentro del Abstract Factory de la segunda entrada.


El diagrama del patrón Builder es este.



Implementación

Empezaremos con el patrón Prototype que tiene una estructura muy sencilla. En la clase abstracta Animal se coloca un método abstracto llamado Clone que será implementado por cada una de las clases que heredan de ella. De esta forma cada clase hija podrá decidir cómo clonarse incluyendo lo que quieran en el nuevo objeto. También podría darse el caso que para clonarse se tuvieran que realizar cálculos previos específicos en cada clase, aunque esto se saldría un poco de la filosofía del patrón sería perfectamente posible.

Lo bueno de estar utilizando C# en .Net, entre otras ventajas, es que podemos hacer uso de funciones específicas del entorno para realizar la copia de los valores. Usaremos el método MemberwiseClone que simplemente lo que hace es realizar una copia de los miembros o propiedades del objeto que realiza la llamada. ¡ Vaya un Prototype genérico dentro de .Net !.

El código que resulta de todo esto es el siguiente:

public abstract class Animal
{
   ...
  // prototype
  public abstract Animal Clone();
  ...
}

public abstract class Canine : Animal
{
  ...
  public override Animal Clone()
  {
     return (Animal)this.MemberwiseClone();
  }
  ...
}

public class Dog : Canine
{
  public override Animal Clone()
  {
    return (Animal)this.MemberwiseClone();
  }
}


En el mundo Python existe una norma que dice que "lo simple es bello", pues en este caso el patrón Prototype lo cumple con creces. Existen pocos patrones tan simples como este, quizás Singleton sea el otro.  Pues bien por lo que se refiere al patrón Prototype ya queda poco más que decir, usadlo siempre que lo necesitéis porque es simple y os puede servir más adelante para introducir nuevas funcionalidades a la hora de copiar objetos.

Ahora seguimos con el grueso de esta entrada. El patrón Builder.  Según la definición que se da en dofactory.com este patrón se utiliza cuando se quiere separar la implementación de un objeto completo de su representación, de esta forma se pueden crear diferentes representaciones con el mismo proceso.

Vamos a empezar explicando las clases de abajo a arriba. En primer lugar la clase Pack es la que contrendrá lo que nos interesa realmente, las partes del pack: el animal, su complemento, la comida y el precio. Estos datos se introducirán en una lista con el método público Add.

public class Pack
{
  List _parts = new List();

  public void Add(string part)
  {
    _parts.Add(part);
  }
}


La siguiente es la clase abstracta PackBuilder que tendrá los métodos abstractos para formar las distintas partes del pack. Estos métodos serán llamados más tarde por PackDirector para ir conformando los packs en su totalidad. Su código es muy simple y claro, un método por cada parte y el cómo se realice esto queda delegado a las clases hijas.

public abstract class PackBuilder
{
  protected float _price;

  public float Price
  {
    get { return _price; }
  }

  public PackBuilder(List _stockAnimals){}
  public abstract void AddAnimal();
  public abstract void AddComplement();
  public abstract void AddFood();
  public abstract void AddPrice();
  public abstract Pack GetPack();
}


Ahora vamos con las clases ChristmasPack y EasterPack que implementarán la clase abstracta con sus métodos y cada una podrá realizar dentro de estos lo que les sea propio. 
Por ejemplo, la primera de ellas sólo admite animales carnívoros y la segunda animales herbívoros. Esta es una diferencia muy simple, pero en un caso real podría complicarse hasta límites insospechados. Otra parte importante es que tienen una instancia de la clase Pack como propiedad. 

public class ChristmasPack : PackBuilder
{
  Pack _pack = new Pack();
  Animal _animal = null;

  public ChristmasPack(List _stockAnimals) :  base(_stockAnimals)
  {
  Console.WriteLine("> " +  System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.FullName);

  if (_stockAnimals != null)
  {
    foreach (Animal item in _stockAnimals)
    {
     // only this kind of animal
     if (item.Food.Equals(Food.Carnivore))
     {
       _animal = item.Clone();
      _pack.Add(item.GetType().BaseType.Name + ": " + item.GetType().Name);
       break;
     }
    }

    if (_animal == null)
     Console.WriteLine("\tNo " + Food.Carnivore + " in stock");
   }
  }

  public override void AddAnimal()
  {
   if (_animal != null)
   {
    _pack.Add(_animal.GetType().BaseType.Name + ": " + _animal.GetType().Name);
     _price += _animal.Price;
   }
  }

  public override void AddComplement()
  {
    if (_animal != null)
    {
      _pack.Add("Complement: buzzle");
      _price += 1f;
    }
  }

  public override void AddFood()
  {
    if (_animal != null)
    {
      _pack.Add("Food: " + _animal.Food);
      _price += 2f;
    }
  }

  public override void AddPrice()
  {
    if (_animal != null)
    {
      _pack.Add("Price: " + _price);
    }
  }

  public override Pack GetPack()
  {
    return _pack;
  }
}


Por último, hablaremos de la clase PackDirector que nos permitirá generalizar la creación de los packs y no tener que llamar a todos los métodos de las partes desde el cliente. De esta forma sólo tendremos que hacer uso de una llamada y el proceso de creación y sus partes quedarán ocultos. En un futuro podríamos necesitar quitar o añadir partes y esto podría provocar que en el código del cliente tuviéramos que realizar cambios, de esta forma esto se evita y sólo habría que modificar el código de las otras clases dejando el sistema cliente aislado de los cambios.

public class PackDirector
{
  public void Construct(PackBuilder packBuilder)
   {

     packBuilder.AddAnimal();
     packBuilder.AddComplement();
     packBuilder.AddFood();
     packBuilder.AddPrice();
   }
}


Para finalizar os dejaré un ejemplo de cómo se podría hacer uso de este patrón ya que algunos de vosotros me habéis pedido ejemplos de de uso en el cliente de los anteriores patrones. Procuraré actualizar lo antes posible las entradas I y II con su utilización. 

// Builder
PackBuilder packChristmasBuilder = new ChristmasPack(petShop.StockAnimals);
PackDirector packDire = new PackDirector();
packDire.Construct(packChristmasBuilder);
Pack packChristmas = packChristmasBuilder.GetPack();
Console.WriteLine("\t" + packChristmas.ShowPack());


Básicamente para utilizar este patrón se crea una variable de la clase abstracta PackBuilder, pero llamando al constructor del tipo del pack que queramos, en este caso ChristmasPack. Después a PackDirector se le pasa el PackBuilder creado. Para terminar se llama a su método GetPack  que devuelve el Pack ya creado. Simple y efectivo. 

Se puede ver ahora de forma clara como los cambios en el proceso nunca afectarán al cliente ya que todo el proceso de creación queda oculto al cliente.

Y esto es todo por lo que respecta a estos dos patrones. En la próxima entrada hablaremos del patrón Factory Method para gestionar los datos de los clientes y los proveedores.


Hasta pronto.

jueves, 24 de julio de 2014

La tienda de animales II

¿Qué tipo de animales quieres vender? 

El patrón Abstract Factory

Hola de nuevo a todos y todas. En esta segunda entrada sobre los patrones GOF seguiremos con la parte del sistema que se encargará de crear tiendas específicas que se diferencien por el tipo de animales que pueden vender. En nuestro caso podremos crear tiendas de animales domésticos y salvajes.

El diagrama siguiente muestra cómo queda el patrón Abstract Factory adaptado a nuestro caso.

Recordemos que tenemos por un lado las clases PetShopFactory como clase abstracta que es implementada por SavagePetShopFactory y HomePetShopFactory que se encargarán de crear los distintos tipos de animales según el tipo de tienda.

Por otro lado están las familias de animales agrupados por Reptil, Bird, Canine y Feline. Dependiendo del tipo de tienda se creará uno de los posibles animales que pertenezcan a cada familia de animales.

Por ejemplo, si se crea una tienda SavagePetShopFactory el animal de la familia Feline será Lion.

Implementación

La primera clase a mostrar será PetShopFactory que sólo contendrá los métodos abstractos para crear las familias de animales.

     public abstract class PetShopFactory
   {
        public abstract Reptil CreateReptil();
        public abstract Bird CreateBird();
        public abstract Canine CreateCanine();
        public abstract Feline CreateFeline();
    }


A continuación las clases que heredan de esta e implementan los métodos y que serán los que realmente serán llamados.

      public class SavagePetShopFactory : PetShopFactory
    {
        public override Reptil CreateReptil()
        {
            return new Aligator();
        }

        public override Bird CreateBird()
        {
            return new Eagle();
        }

        public override Canine CreateCanine()
        {
            return new Dingo();
        }

        public override Feline CreateFeline()
        {
            return new Lion();
        }
    }


    public class HomePetShopFactory : PetShopFactory
    {
        public override Reptil CreateReptil()
        {
            return new Chamaleon();
        }

        public override Bird CreateBird()
        {
            return new Parrot();
        }

        public override Canine CreateCanine()
        {
            return new Dog();
        }

        public override Feline CreateFeline()
        {
            return new Cat();
        }
    }


Se puede ver cómo los métodos devuelven objetos de las clases abstractas de las distintas familias, pero realmente lo que se ha creado son objetos que heredan de estas clases. Por ejemplo, al llamar al método CreateReptil en la clase HomePetShopFactory se está creando un Chamaleon y en la clase SavagePetShopFactory se está creando un Aligator, pero en ambas se devuelve un Reptil.

El principio de la programación orientada a objetos sobre el que se basa esto es aquel que dice que cuando una clase hereda de otra, allí donde haya un padre siempre se podrá sustituir por un hijo. Es decir, aunque devolvamos una clase padre abstracta como es Reptil, podremos utilizar una clase hija como es Chamaleon.

Pero tenemos algo que no está muy bien. Cada una de las clases abstractas de las familias representan realmente a Animales. Por lo que tendrán características que se repetirán como el precio, el continente, la comida, el nombre o el entorno en el que viven. Sería mucho mucho mejor crear otra clase llamada Animal de la que heredaran las clases abstractas de las familias y que almacenara todas estas características comunes.

De esta forma el nuevo diagrama quedaría así.


Además, se han añadido tres enumerados para representar los tipos de Continent, Enviroment y Food para asegurarnos que el conjunto de los valores posibles esté restringido y evitar errores en su uso que podría afectar a la lógica del sistema que dependa de los valores.

La implementación de esta parte del sistema sería la siguiente.

      public enum Environment
    {
        Aquatic,
        Terrestrial,
        Amphibian,
        Aerial,
    }

    public enum Food
    {
        Carnivore,
        Herbivore,
    }

    public enum Continent
    {
        Africa,
        America,
        Europe,
        Asia,
        Oceania,
    }

    public abstract class Animal
    {
        protected string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        protected Environment _environment;

        public Environment Environment
        {
            get { return _environment; }
            set { _environment = value; }
        }
        protected Food _food;

        public Food Food
        {
            get { return _food; }
            set { _food = value; }
        }
        protected Continent _continent;

        public Continent Continent
        {
            get { return _continent; }
            set { _continent = value; }
        }

        private float _price;

        public float Price
        {
            get { return _price; }
            set { _price = value; }
        }

        public Animal() { }

        public Animal(string name, Environment environment, 
                     Food food, Continent continent, float price)
        {
            this._name = name;
            this._environment = environment;
            this._food = food;
            this._continent = continent;
            this._price = price;
        }

        public string Show()
        {
            string values = GetType().Name + " >> ";

      PropertyInfo[] fields = GetType().BaseType.GetProperties();

            foreach (PropertyInfo item in fields)
            {
                values += item.Name + ": " + 
                          item.GetValue(this, null) + " | ";
            }
            return values;
        }
    }

Esta clase tiene el método Show que nos vendrá muy bien luego para ver los valores que almacenan los objetos a la hora de realizar las pruebas.

Las clases de las familias que heredan de Animal sólo tienen el constructor porque todo lo común se ha sacado hacia la clase Animal. De esta forma todo queda mucho más limpio.

      public abstract class Feline : Animal
    {
        public Feline()
        {
            this.Price = 40.0f;
        }
    }

    public abstract class Canine : Animal
    {       
        public Canine()
        {
            this.Price = 30.0f;
        }
    }

    public abstract class Reptil: Animal
    {       
        public Reptil()
        {
            this.Price = 10.0f;
        }
    }

    public abstract class Bird: Animal
    {       
        public Bird()
        {
            this.Price = 20.0f;
        }
    }

Las clases finales de los animales sólo tienen un constructor en el que asignar los valores de sus propiedades de clase.

    public class Aligator : Reptil
    {
        public Aligator()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Amphibian;
            this.Food = Food.Carnivore;
            this.Continent = Continent.America;
        }
    }

    public class Chamaleon : Reptil
    {
        public Chamaleon()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Terrestrial;
            this.Food = Food.Carnivore;
            this.Continent = Continent.America;
        }
    }

    public class Eagle : Bird
    {
        public Eagle()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Aerial;
            this.Food = Food.Carnivore;
            this.Continent = Continent.Europe;
        }
    }

    public class Parrot : Bird
    {
        public Parrot()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Aerial;
            this.Food = Food.Herbivore;
            this.Continent = Continent.Asia;
        }
    }

    public class Dingo : Canine
    {
        public Dingo()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Terrestrial;
            this.Food = Food.Carnivore;
            this.Continent = Continent.Oceania;
        }
    }

    public class Dog : Canine
    {
        public Dog()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Terrestrial;
            this.Food = Food.Carnivore;
            this.Continent = Continent.Europe;
        }
    }

    public class Lion : Feline
    {
        public Lion()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Terrestrial;
            this.Food = Food.Carnivore;
            this.Continent = Continent.Africa;
        }
    }

    public class Cat : Feline
    {
        public Cat()
        {
            this.Name = GetType().Name;
            this.Environment = Environment.Terrestrial;
            this.Food = Food.Carnivore;
            this.Continent = Continent.Europe;
        }
    }

Y hasta aquí la entrada de hoy. En la próxima entrega veremos cómo podemos mejorar un poco más lo que hemos visto hoy añadiendo el patrón Prototype y mostraremos la parte del sistema correspondiente al patrón Builder relacionada con los Packs.

Hasta pronto.




jueves, 17 de julio de 2014

La tienda de animales I

Implementación de una tienda de animales utilizando los patrones de diseño GOF.


Si buscamos por Internet información sobre los patrones de diseño GOF existen multitud de ejemplos de su implementación. No voy a descubrir nada nuevo. Pero lo que voy a presentar es una implementación de varios patrones y mostrar cómo pueden funcionar juntos.

En este caso sólo haré uso de los patrones Creacionales para no complicar demasiado las cosas. Estos son los que se dedican a aspectos de creación de objetos, por decirlo llanamente. Su nombres son:  Abstract Factory, Builder,  Factory Method, Prototype, Singleton.

En dofactory hay una referencia muy buena de todos ellos por si queréis ampliar más.

Bueno vamos al lio.

Definición del sistema

El proyecto será implementar una tienda de animales. El sistema puede crear y gestionar dos tipos de tienda. Una se dedica a vender animales salvajes y otra animales domésticos. Aunque ahora ya no esté muy claro qué es un animal doméstico.

Los dos tipos de tienda pueden vender distintas categorias de animales: Reptil, Pájaro, Canes y Felinos.
Además, en determinadas épocas del año ofrece unos packs descuento a sus clientes con animales distintos.
Por último, para gestionar los datos de sus clientes dispone de la posibilidad de almacenar sus datos.

Diagrama de clases

A continuación aparece un diagrama de clases con las clases del sistema y su relación. Aparecen organizadas según el patrón de diseño que representan.


Justificación de los patrones elegidos y clases implicadas

  • Singleton: Este patrón se utiliza cuando sólo queremos tener una instancia de una clase. Es decir, queremos que se cree un sólo objeto.
    • PetShop: Es la clase principal que representa la tienda.

  • Abstract factory: Ya que queremos poder crear tiendas de animales domésticos o salvajes, se ha utilizado este patrón porque permite crear famillias de objetos y manejarlos de una forma uniforme. 
    • PetShopFactory: clase abstracta que contiene los métodos de creación de las familias de animales.
    • SavagePetShopFactory: implementa a PetShopFactory y creará las familias de animales salvajes.
    • HomePetShopFactory: implementa a PetShopFactory y creará las familias de animales domésticos.
    • Reptil, Bird, Canine, Feline: Son las clases que representan las familias de animales.
    • Aligator, Chamaleon, Eagle, Parrot, Dingo, Dog, Lion, Cat: Los animales que se pueden crear en cada familia.


  • Builder: Este patrón es ideal para crear objetos que tienen las mismas partes, pero su contenido o proceso de creación son diferentes. En el sistema formará el subsistema de los packs de temporada.
    • PackDirector: Es la clase que gestiona la creación de los packs.

    • PackBuilder: Es una clase abstracta que contiene los métodos necessarios para crear las partes de los packs.

    • ChristmasPack, EasterPack: Son las clases que implementan a PackPuilder y realizan la creación de todas las partes de cada pack siguiendo las reglas de creación apropiadas. Por ejemplo, en el primer pack sólo se admiten animales carnívoros.

    • Pack: Es la clase que representa a un pack genérico con la lista de sus partes.


  • Factory Method: Este patrón permite crear objetos compuestos por distintas clases que sólo se puede saber su tipo en tiempo de ejecución. En nuestro caso nos servirá para encargarse de los contactos de los clientes y proveedores de la tienda.
    • Contact: Es la clase abstracta que representa a un contacto de la tienda. Cada contacto puede tener información especifica.

    • Customer, Suplier: Son las clases que implementan a Contact y representan los dos tipos de contactos que se pueden tener.

    • Data: cada contacto puede almacenar información específica dependiendo de su tipo.

    • CompanyData, DiscountData: Esta información sólo la podrán tener los Suplier.

    • PersonalData, SalesData: Esta información sólo la podrán tener los Customer.  
  

Implementación

Lo primero que vamos a hacer es la clase principal PetShop y que debemos diseñar usando el patrón singleton.

Hay que colocar una propiedad estática que sea privada del mismo tipo de la clase y que será la única instancia de la clase.

private  static PetShop _petShop = null;


El constructor debe ser privado para que no se puedan crear instancias de esta clase desde fuera.

private PetShop() {}

Y tendremos un método que se encargará de crear una única instancia de la clase
        
public static PetShop Create()
{
     if (_petShop == null)
      _petShop = new PetShop();
 

  return _petShop;
}


Todo junto queda así:


public class PetShop
{
     private  static PetShop _petShop = null;
       
     public static PetShop Create()
     {
         if (_petShop == null)
             _petShop = new PetShop();

         return _petShop;
     }

     private PetShop()
     {
     } 


        
Hasta aquí la primera parte. Sigueme y podrás ver cómo se implementa el resto del sistema.

Hasta pronto.