Patrones de Diseño Estructurales

Un patrón de diseño es una estructura general que puede ser usada para solucionar un problema. No se trata ni de una librería ni de un esqueleto. Para que una solución se considere un patrón de diseño debe ser efectiva y reutilizable, es decir, que pueda aplicarse y resolver problemas similares.

Los Patrones Estructurales definen formas de interconectar objetos.

Adapter (Adaptador, Envoltorio, Wrapper)

Este patrón funciona como puente entre dos interfaces incompatibles, permite su colaboración. Imagina que tienes una clase que se encarga de recibir información de un servicio web en XML, y que ese servicio decide cambiar su software y empezar a devolver JSON. Este patrón es ideal para solucionar este problema sin tener que tocar la clase de parseo.
Bridge (Puente)

Todo el mundo coincide en que definir este patrón es complejo. Este patrón se utiliza cuando se quiere desacoplar una abstracción de su implementación para que ambas puedan cambiar independientemente. La abstracción delegará casi todas sus llamadas al objeto de las implementaciones. La mejor forma de explicarlo es a través de los ejemplos:
public interface Device {
    boolean isEnabled();
    void enable();
    void disable();
    int getVolume();
    void setVolume(int percent);
    int getChannel();
    void setChannel(int channel);
    void printStatus();
}
public class Radio implements Device {
    private boolean on = false;
    private int volume = 30;
    private int channel = 1;

    @Override
    public boolean isEnabled() {
        return on;
    }

    @Override
    public void enable() {
        on = true;
    }
    ...
}
public class Tv implements Device {
    private boolean on = false;
    private int volume = 30;
    private int channel = 1;

    @Override
    public boolean isEnabled() {
        return on;
    }

    @Override
    public void enable() {
        on = true;
    }
	...
}

public interface Remote {
    void power();
    void volumeDown();
    void volumeUp();
    void channelDown();
    void channelUp();
}
public class BasicRemote implements Remote {
    protected Device device;

    public BasicRemote() {}

    public BasicRemote(Device device) {
        this.device = device;
    }

    @Override
    public void power() {
        System.out.println("Remote: power toggle");
        if (device.isEnabled()) {
            device.disable();
        } else {
            device.enable();
        }
    }

    @Override
    public void volumeDown() {
        System.out.println("Remote: volume down");
        device.setVolume(device.getVolume() - 10);
    }
    ...
}

public class Demo {
    public static void main(String[] args) {
        testDevice(new Tv());
        testDevice(new Radio());
    }

    public static void testDevice(Device device) {
        System.out.println("Tests with basic remote.");
        BasicRemote basicRemote = new BasicRemote(device);
        basicRemote.power();
        device.printStatus();
    }
}
Composite (Objeto compuesto, Object Tree)

El patrón Composite compone objetos organizados en forma de árbol, como pueden ser cajas y productos o carpetas y ficheros. Incluso objetos anidados, como por ejemplo un Empleado tiene una lista de subordinados también Empleados. No tiene sentido en una esctructura que no sea en árbol. Este patrón crea una clase que contiene grupos de los mismos componentes y permite modificarlos. Este patrón no tiene ninguna mágia y su implementación es bastante trivial.
La interfaz Componente describe operaciones que son comunes a elementos simples y complejos del árbol. La Hoja es un elemento básico de un árbol que no tiene subelementos y realizan la mayoría del trabajo real, ya que no tienen a nadie a quien delegarselo.
public interface Employee {  
    public int getId();  
    public String getName();  
    public double getSalary();  
    public void print();  
    public void add(Employee employee);  
    public void remove(Employee employee);  
    public Employee getChild(int i);  
}
public class BankManager implements Employee {  
    private int id;  
    private String name;  
    private double salary;  
  
    public BankManager(int id,String name,double salary) {  
      this.id=id;      
      this.name = name;  
      this.salary = salary;  
    }
    
    List employees = new ArrayList();
    
    @Override  
    public void add(Employee employee) {  
        employees.add(employee);  
    }
    @Override  
    public Employee getChild(int i) {  
      return employees.get(i);  
    }  
    @Override  
    public void remove(Employee employee) {  
      employees.remove(employee);  
    }    
    @Override  
    public int getId()  {  
      return id;  
    }  
    @Override  
    public String getName() {  
      return name;  
    }  
    @Override  
    public double getSalary() {  
      return salary;  
    }  
    @Override  
    public void print() {  
      System.out.println("==========================");  
      System.out.println("Id ="+getId());  
      System.out.println("Name ="+getName());  
      System.out.println("Salary ="+getSalary());  
      System.out.println("==========================");  
        
      Iterator it = employees.iterator();  
        
      while(it.hasNext())  {  
      	Employee employee = it.next();  
        employee.print();  
      }  
    }  
}
public class Cashier implements Employee{  
    // In this class,there are many methods which are not applicable to cashier because it is a leaf node. 
    private int id;  
    private String name;  
    private double salary;  
    public Cashier(int id,String name,double salary)  {  
    	this.id=id;  
        this.name = name;  
        this.salary = salary;  
    }  
    @Override  
    public void add(Employee employee) {  
    	//this is leaf node so this method is not applicable to this class.  
    }
    ...
}
public class Accountant implements Employee{  
    // In this class,there are many methods which are not applicable to cashier because it is a leaf node.
    private int id;  
    private String name;  
    private double salary;  
    public Cashier(int id,String name,double salary)  {  
    	this.id=id;  
        this.name = name;  
        this.salary = salary;  
    }  
    @Override  
    public void add(Employee employee) {  
    	//this is leaf node so this method is not applicable to this class.  
    }
    ...
}

public class CompositePatternDemo {  
    public static void main(String args[]){  
        Employee emp1=new Cashier(101,"Sohan Kumar", 20000.0);  
        Employee emp2=new Cashier(102,"Mohan Kumar", 25000.0);  
        Employee emp3=new Accountant(103,"Seema Mahiwal", 30000.0);   
        Employee manager1=new BankManager(100,"Ashwani Rajput",100000.0);  
            
         manager1.add(emp1);  
         manager1.add(emp2);  
         manager1.add(emp3);  
         manager1.print();  
    }  
}
Decorator (Decorador, Envoltorio, Wrapper)

Permite añadir funcionalidades a objetos sin tener que modificarlos, sólo colocando estos objetos dentro de otros que tienen las nuevas funcionalidades.
public interface Shape {
   void draw();
}
public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Shape: Rectangle");
   }
}
public class Circle implements Shape {

   @Override
   public void draw() {
      System.out.println("Shape: Circle");
   }
}
public abstract class ShapeDecorator implements Shape {
   protected Shape decoratedShape;

   public ShapeDecorator(Shape decoratedShape){
      this.decoratedShape = decoratedShape;
   }

   public void draw(){
      decoratedShape.draw();
   }	
}
public class RedShapeDecorator extends ShapeDecorator {

   public RedShapeDecorator(Shape decoratedShape) {
      super(decoratedShape);		
   }

   @Override
   public void draw() {
      decoratedShape.draw();	       
      setRedBorder(decoratedShape);
   }

   private void setRedBorder(Shape decoratedShape){
      System.out.println("Border Color: Red");
   }
}
public class DecoratorPatternDemo {
   public static void main(String[] args) {

      Shape circle = new Circle();

      Shape redCircle = new RedShapeDecorator(new Circle());

      Shape redRectangle = new RedShapeDecorator(new Rectangle());
      System.out.println("Circle with normal border");
      circle.draw();

      System.out.println("\nCircle of red border");
      redCircle.draw();

      System.out.println("\nRectangle of red border");
      redRectangle.draw();
   }
}
Facade (Fachada)

Proporciona una interfaz simplificada a un sistema complejo, ocultando así su complejidad. Este patrón requiere de una sola clase, la cual provee los métodos necesarios para el cliente y hace las llamadas al sistema complejo.
class VideoFile
// ...
class OggCompressionCodec
// ...
class MPEG4CompressionCodec
// ...
class CodecFactory
// ...
class BitrateReader
// ...
class AudioMixer
// ...

// Creamos una clase fachada para esconder la complejidad del
// framework tras una interfaz simple. Es una solución de
// equilibrio entre funcionalidad y simplicidad.
class VideoConverter is
    method convert(filename, format):File is
        file = new VideoFile(filename)
        sourceCodec = new CodecFactory.extract(file)
        if (format == "mp4")
            destinationCodec = new MPEG4CompressionCodec()
        else
            destinationCodec = new OggCompressionCodec()
        buffer = BitrateReader.read(filename, sourceCodec)
        result = BitrateReader.convert(buffer, destinationCodec)
        result = (new AudioMixer()).fix(result)
        return new File(result)

// Las clases Application no dependen de un millón de clases
// proporcionadas por el complejo framework. Además, si decides
// cambiar los frameworks, sólo tendrás de volver a escribir la
// clase fachada.
class Application is
    method main() is
        convertor = new VideoConverter()
        mp4 = convertor.convert("funny-cats-video.ogg", "mp4")
        mp4.save()

Flyweight (Peso mosca, Peso ligero, Cache)

Este patrón existe principalmente para reducir el número de objetos creados y reducir la huella en memoría y mejorar el rendimiento. La idea base es compartir las partes comunes del estado entre varios objetos en lugar de mantener toda la información en cada objeto.

Este diagrama respondería a este patrón cuando queremos dibujar circulos en distintas partes de la pantalla pero creando sólo 5 objetos, que son los 5 colores que disponemos. El truco es disponer en el Factory de un Hashmap con los objetos ya creados.
Proxy

El patrón Proxy controla el acceso al objeto original, permitiéndote hacer algo antes o después de que la solicitud llegue al objeto original. Creamos un objeto teniendo el original para interconectar su funcionalidad con el mundo exterior, y asi protegemos al original.

También puede ser utilizado para reducir el uso de memoria o tiempos de carga como en el suguiente ejemplo:
public interface Image {
   void display();
}
public class RealImage implements Image {

   private String fileName;

   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}
public class ProxyImage implements Image{

   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName){
      this.fileName = fileName;
   }
   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}
public class ProxyPatternDemo {
	
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");

      //image will be loaded from disk
      image.display(); 
      System.out.println("");
      
      //image will not be loaded from disk
      image.display(); 	
   }
}



Comentarios

Entradas populares de este blog

Envío de checkboxes o selector multiple por AJAX con jQuery

Patrones de Diseño Creacionales

Conceptos básicos de la POO