lunes, 2 de mayo de 2016

Patrones de Diseño de Comportamiento Parte 2


16. Iterator: Provee un mecanismo estándar para acceder secuencialmente a los elementos de una colección; define una interface que declara métodos para acceder secuencialmete a los objetos de una colección. Una clase accede a una colección a través de dicha interface. La motivación de este patrón reside en la gran diversidad de colecciones y algoritmos que existe hoy en día para recorrer una colección. Lo que se busca es acceder a los contenidos de los objetos incluidos sin exponer su estructura. Podemos decir que este patrón nace para poder soportar diversas formas de recorrer objetos y para ofrecer una interfaz uniforme para recorrer distintos tipos de estructuras de agregación.
Se utiliza cuando una clase necesita acceder al contenido de una colección sin llegar a ser dependiente de la clase que es utilizada para implementar a la colección, es decir sin tener que exponer su representación interna. Una clase necesita un modo uniforme de acceder al contenido de varias colecciones. Cuando se necesita soportar múltiples recorridos de una colección. Este patrón debe ser utilizado cuando se requiera una forma estándar de recorrer una colección, es decir, cuando no es necesario que un cliente sepa la estructura interna de una clase. Un cliente no siempre necesita saber si debe recorrer un List o un Set o un Queue y, menos que menos, que clase concreta está recorriendo.

Representación UML


Ejemplo

Comencemos por el principio. Imaginemos que disponemos de una fábrica de Vehículos. Esta  fábrica guarda  un registro pormenorizado de los vehículos que fabrica, que están modelados a través de la siguiente clase:

public class Vehiculo
{
    public string Marca { get; set; }
    public string Modelo { get; set; }
    public DateTime FechaFabricacion { get; set; }
    public double Precio { get; set; }

    public Vehiculo(string marca, string modelo,
        DateTime fechaFabricacion, double precio)
    {
        this.Marca = marca;
        this.Modelo = modelo;
        this.FechaFabricacion = fechaFabricacion;
        this.Precio = precio;
    }

    public string CaracteristicasVehiculo()
    {
        return Marca + " " + Modelo + " fabricado en " +
            FechaFabricacion.ToShortDateString() + " con un precio de " +
            Precio + " euros.\n";
    }
}

Como nuestro ingenieros han sido previsores, han decidido que la estructura de datos en la cual se almacenarán los vehículos implemente una interfaz, a la que llamaremos lRegistroVehículos. Este interfaz definirá las operaciones básicas que se harán sobre el registro, que serán insertan un nuevo vehículo y mostrar información sobre éste (un ejemplo más completo también incluirá métodos para eliminar y modificar, pero nos centraremos de momento en esta funcionalidad básica para no desviarnos del ejemplo):

public interface IRegistroVehiculos
{
    void InsertarVehiculo(string marca, string modelo, double precio);
    Vehiculo MostrarInformacionVehiculo(int indice);
    IIteratorVehiculo ObtenerIterator();
}

 Nuestra fábrica de vehículos posee un sistema de gestión que almacena los vehículos en una estructura de datos agregada, como por ejemplo un ArrayList. Nuestra clase, como podremos imaginar, implementará la interfaz lRegistroVehículos.

public class RegistroVehiculos : IRegistroVehiculos
{
    private ArrayList listaVehiculos;

    public RegistroVehiculos()
    {
        this.listaVehiculos = new ArrayList();
    }

    public void InsertarVehiculo(string marca, string modelo, double precio)
    {
        Vehiculo v = new Vehiculo(marca, modelo, DateTime.Now, precio);
        listaVehiculos.Add(v);
    }

    public Vehiculo MostrarInformacionVehiculo(int indice)
    {
        return (Vehiculo)listaVehiculos[indice];
    }
}
 Hasta ahora hemos creado una clase que contendrá la información sobre los vehículos y otra que contendrá un listado de éstos y será capaz de añadir nuevos vehículos y recuperar un vehículo del cual le pasaremos un índice. Sin embargo, aún no hemos visto ni rastro del patrón Iterator por ningún sitio. Este patrón es, precisamente, la pieza que nos falta para que nuestro modelo esté completo. Si el método MostrarInformacionVehiculo le pasamos un índice mayor que el número de elementos que contenga el ArrayList, lo que obtendremos será una bonita excepción de índice fuera de rango. Una posible solución sería comprobar  dentro del método que el número de elementos menos uno es siempre mayor o igual que el índice pasado como parámetro, pero esto no arreglaría el problema: necesitamos una estructura que sea capaz de iterar sobre la colección e informar al cliente si existen más elementos disponibles. Es hora de implementar un iterador.

public interface IIteratorVehiculo
{
    void Primero();
    Vehiculo Actual();
    Vehiculo Siguiente();
    bool QuedanElementos();
}

public class IteratorVehiculo : IIteratorVehiculo
{
    // Referencia al listado completo
    private ArrayList vehiculos;

    // Almacenaremos el índice en el que se encuentra el iterador
    private int posicionActual = -1;

    // El constructor inyectará el ArrayList en el objeto
    public override IteratorVehiculo(ArrayList listado)
    {
        this.vehiculos = listado;
    }

    // Operación 1: Reinicio del índice, colocándolo en el elemento anterior al primero
   public void Primero()
   {
    this.posicionActual = -1;
   }

   // Operación 2: Acceso al elemento actual
    public Vehiculo Actual()
    {
    // Si no existen elementos, devolveremos null.
    // Si el indice actual es mayor que el mayor indice aceptable, devolveremos null.
    // Si el indice actual es -1, devolveremos null.
    if ((this.vehiculos == null) ||
        (this.vehiculos.Count == 0) ||
        (posicionActual > this.vehiculos.Count - 1) ||
        (this.posicionActual < 0))
        return null;

    // Devolvemos el elemento correspondiente al elemento actual
    else
        return (Vehiculo)this.vehiculos[posicionActual];
    }

    // Operación 3: Acceso al siguiente elemento
   public Vehiculo Siguiente()
   {
    // Si no existen elementos, devolveremos null.
    // Si el indice siguiente es mayor que el mayor indice aceptable, devolveremos null.
    if ((this.vehiculos == null) ||
        (this.vehiculos.Count == 0) ||
        (posicionActual + 1 > this.vehiculos.Count - 1))
        return null;

    // Aumentamos el índice en una unidad y devolvemos ese elemento
    else
        return (Vehiculo)this.vehiculos[++posicionActual];
  }
 
  // Operación 4: Comprobación de si existen elementos en la colección
  public bool QuedanElementos()
   {
    // Devolvemos un booleano que será true si la posición siguiente es
   // menor o igual que el
    // máximo índice aceptable (número de elementos del array - 1).
    return (posicionActual + 1 <= this.vehiculos.Count - 1);
   }

   public IIteratorVehiculo ObtenerIterator()
   {
    return new IteratorVehiculo(listaVehiculos);
    }
}

Hacemos la clase principal.

public class MainClass{
   public static void main(String[] args) throws Exception{
     // Declaramos el registro
IRegistroVehiculos registro = new RegistroVehiculos();

// Insertamos unos cuantos elementos
registro.InsertarVehiculo("Volkswagen", "Polo", 12300);
registro.InsertarVehiculo("Volkswagen", "Golf GTI", 18900);
registro.InsertarVehiculo("Volkswagen", "Passat", 27000);
registro.InsertarVehiculo("Volkswagen", "Scirocco", 32100);
registro.InsertarVehiculo("Volkswagen", "Touareg", 21800);

// Obtenemos el iterator
IIteratorVehiculo iterador = registro.ObtenerIterator();

// Mientras queden elementos
while (iterador.QuedanElementos())
{
    // Obtenemos el siguiente elemento
    Vehiculo v = iterador.Siguiente();

    // Mostramos su contenido
    Console.WriteLine(v.Marca + " " + v.Modelo + " fabricado el " +
 v.FechaFabricacion.ToShortDateString() + " (" + v.Precio + " euros)");
}
  }
}

17. Mediator:  Coordina las relaciones entre sus asociados. Define un objeto que encapsula cómo interactúan un conjunto de objetos. Promueve un bajo acoplamiento al evitar que los objetos se refieran unos a otros explícitmente, y permite variar la interección entre ellos de forma independiente.
El patrón Mediator coordina las relaciones entre sus asociados. Permite la interacción de varios objetos, sin generar acoples fuertes en esas relaciones. Cuando muchos interactúan con otros objetos, se puede formar una estructura muy compleja, con objetos con muchas conexiones con otros objetos. En un caso extremo cada objeto puede conocer a todos los demás objetos. Para evitar esto el patrón Mediator encapsula el comportamiento de todo un conjunto de objetos en un solo objeto. Un conjunto grande de objetos se comunica de una forma bien definida, pero compleja. Reutilizar un objeto se hace difícil por que se relaciona con muchos objetos. El comportamiento de muchos objetos que está distribuido entre varias clases, puede resumirse en una o varias por subclasificación.

Representación  UML


 Ejemplo

Crear clase Mediator
import java.util.Date;

public class ChatRoom {
   public static void showMessage(User user, String message){
      System.out.println(new Date().toString() + " [" + user.getName() + "] : " + 
message);
   }
}
Crear clase usuario

public class User {
   private String name;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public User(String name){
      this.name  = name;
   }

   public void sendMessage(String message){
      ChatRoom.showMessage(this,message);
   }
}
 
Usa el objeto User a mostrar comunicación entre ellos.

public class MediatorPatternDemo {
   public static void main(String[] args) {
      User robert = new User("Robert");
      User john = new User("John");

      robert.sendMessage("Hi! John!");
      john.sendMessage("Hello! Robert!");
   }
}

18. Memento: Este patrón de diseño permite capturar y exportar el estado interno de un objeto para que luego se pueda restaurar; sin romper la encapsulación. Su finalidad es almacenar el estado de un objeto (o del sistema completo) en un momento dado, de manera que se pueda restaurar posteriormente si fuese necesario. Para ello se mantiene almacenado el estado del objeto para un instante de tiempo en una clase independiente de aquella a la que pertenece el objeto(pero sin romper la encapsulación), de forma que ese recuerdo permita que el objeto(pero sin romper la encapsulación), de forma que ese recuerdo permite que el objeto sea modificado y pueda volver  a su estado anterior. Hoy en dia, muchos aplicativos permiten el "deshacer" y "rehacer" de manera muy sencilla. Para ciertos aplicativos es casi una obligación tener estas funciones y sería impensado el hecho que no las posean. Sin embargo, cuando queremos llevar esto a código puede resultar complejo de implementar. Este patrón intenta mostrar una solución a este problema. Se usa cuando se necesite restaurar el sistema desde estados pasados, además se quiera facilitar el hacer y deshacer de determinadas operaciones, para que lo que habrá que guardar los estados anteriores de los objetos sobre los que opere (o bien recordar los cambios de forma incremental). Este patrón debe ser utilizado cuando se necesite salvar el estado de un objeto y tener disponible los distintos estados históricos que se necesiten. Por ello mismo, este patrón es muy intuitivo para darse cuando debe ser utilizado.

Representación  UML


 Ejemplo

Crear clase Memento

public class Memento {
   private String state;

   public Memento(String state){
      this.state = state;
   }

   public String getState(){
      return state;
   }  
}
Crear clase Originator

public class Originator {
   private String state;

   public void setState(String state){
      this.state = state;
   }

   public String getState(){
      return state;
   }

   public Memento saveStateToMemento(){
      return new Memento(state);
   }

   public void getStateFromMemento(Memento Memento){
      state = Memento.getState();
   }
}

Crear  la clase CareTaker

import java.util.ArrayList;
import java.util.List;

public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();

   public void add(Memento state){
      mementoList.add(state);
   }

   public Memento get(int index){
      return mementoList.get(index);
   }
}
 Usar objetos CareTaker y  Originator.

public class MementoPatternDemo {
   public static void main(String[] args) {
 
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
    
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
    
      originator.setState("State #3");
      careTaker.add(originator.saveStateToMemento());
    
      originator.setState("State #4");
      System.out.println("Current State: " + originator.getState());      
    
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
      originator.getStateFromMemento(careTaker.get(1));
      System.out.println("Second saved State: " + originator.getState());
   }
}


No hay comentarios:

Publicar un comentario