RSS

Archivo de la categoría: Patrones de diseño(GoF)

GoF – Patrones de diseño (XII): Facade

El siguiente patrón estructural que vamos a ver es el patrón Facade, que básicamente nos ayuda a crear o nos provee de una interfaz unificada para manejar un conjunto de objetos en un subsistema. De está forma, estaremos definiendo un interfaz de alto nivel que hará los subsistemas más fáciles de manejar. Por hacer una referencia vinculada a la literatura fantástica, sería algo así como “uno para dominarlos a todos” (ESDLA).

La estructuración de un sistema en pequeños subsistemas permite reducir la complejidad de dicho sistema, el problema es que muchas veces crea muchas dependencias entre los subsistemas implementados. Es en este caso donde el patrón Facade nos puede ayudar creando una única interfaz simple que nos permita acceder a las funcionalidades de nuestros subsistemas. De esta forma, los clientes que utilicen nuestro sistema no necesitarán conocer nuestros diferentes subsistemas y su funcionamiento, sino que solo necesitaran conocer el interfaz para manejar el sistema completo.

El patrón Facade se puede aplicar:

  • Cuando se quiere proveer de una interfaz simple para un conjunto complejo de subsistemas. Por ejemplo, la aplicación de muchos patrones de diseño provoca la creación de muchas clases muy pequeñas y reutilizables, pero este nivel de granuralidad hace difícil el manejo del sistema, aquí podríamos aplicar este patrón.
  • Cuando hay muchas dependencias entre los clientes y el sistema. Aplicando el patrón podemos aumentar el nivel de desacoplamiento aumentando así la independencia y la portabilidad.
  • Cuando quieres introducir capas en tus subsistemas, usando el patrón para definir un punto de entrada. Además, si los subsistemas son muy dependientes, podemos simplificar esta dependencia haciendo que estos se comuniquen a través de sus Facades.

Los elementos que componene este patrón son:

  • Facade: Conoce los subsistemas implicados en el sistema global, accede a ellos y les envía las peticiones de los clientes.
  • Subsystem classes: Implementan la funcionalidad del sistema, realizan las tareas enviadas por el Facade y no tienen referencias a Facade.

Con todo esto, las consecuencias de la aplicación de este patrón son una reducción del número de objetos que tienen que manejar los clientes ya que, en vez de necesitar un objeto por cada subsistema implicado, solo necesitan el objeto Facade. Esto último provoca un acoplamiento más débil entre el sistema y los clientes, con lo cual hace más fácil los cambios o modificaciones en el sistema y sus subsistemas, permitiendonos también menos acoplamiento entre nuestros subsistemas mejorando las dependecias de compilación, cosa muy importante en grandes sistemas. Aunque, la implementación del patrón no impide, en caso de necesitarlo, que algún cliente use o acceda a alguno de nuestros subsistemas.

Pero como siempre, aunque el punto de vista teórico está muy bien y es muy importante, lo mejor es verlo con un pequeño ejemplo para que nos quede del todo claro. En este caso, vamos a coger un sistema simple de dibujo de figuras geométricas.

Subsystem classes:

public class Circle {
    public void draw() {
        ...
    }
}

public class Square {
    public void draw() {
        ...
    }
}

public class Rectangle {
    public void draw() {
        ...
    }
}

Facade:

public class ShapeProducer() {
    private Circle circle;
    private Square square;
    private Rectangle resctangle;

    public void drawCircle() {
        circle = new Circle();
        circle.draw();
    }

    public void drawSquare() {
        square = new Square();
        square.draw();
    }

    public void drawRectangle() {
        rectangle = new Rectangle();
        rectangle.draw();
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        ShapeProducer sp = new ShapeProducer();
        sp.drawCircle();
        sp.drawSquare();
        sp.drawRectangle();
    }
}

Como vemos, si no tuviéramos el objeto Facade, el cliente tendría que conocer y tener definidos los tres objetos de nuestro subsistema, de esta forma, solo necesita nuestro objeto Facade.

Otros patrones realcionados con este son: Abstract Factory, que puede ser utilizado junto con Facade. Mediator, no visto aún, similar a Facade en el aspecto de abstraer funcionalidad, pero realizando otro enfoque. Y está indirectamente relacionado con Singleton, ya que muchas veces el objeto Facade es Singleton también.

Bueno, hasta aquí por hoy. Nos vemos.

Anuncios
 
Deja un comentario

Publicado por en 14 enero, 2014 en Patrones de diseño(GoF)

 

GoF – Patrones de diseño (XI): Decorator

El siguiente patrón de diseño que vamos a ver es el patrón Decorator. Dicho patrón nos permitirá añadir funcionalidades o responsabilidades a un objeto de forma dinámica. Este patrón nos provee de una alternativa muy flexible a la creación de subclases para extender funcionalidades. Esto nos permitirá, en tiempo de ejecución, añadir esta funcionalidad o responsabilidad extra que necesita nuestro objeto. esto se podría hacer a través de la herencia pero no sería lo suficientemente flexible ya que de esta forma serían añadidas de forma estática. En vez de esto, nuestro objeto decorator rodeará al objeto inicial añadiendo estás responsabilidades o funcionalidades. Se hace referencia muchas veces a este patrón como Wrapper. Estoy seguro que este nombre si que lo habréis escuchado muchas veces con anterioridad.

Este patrón lo podemos aplicar para:

  • Añadir responsabilidades a un objeto individual de forma dinámica y transparente sin afectar a otros objetos.
  • Responsabilidades que pueden ser eliminadas de un objeto.
  • Cuando la extensión a través de la herencia no es viable. Por ejemplo, cuando esto provocaría una gran cantidad de subclases o la definición de la clase padre está oculta o no es accesible para realizar esta herencia.

Los elementos implicados en este patrón son:

  • Component: Define la interfaz de los objetos a los que se le podrá añadir responsabilidades dinámicamente.
  • ConcreteComponent: Define el objeto al que se le podrán añadir responsabilidades.
  • Decorator: Mantiene una referencia al objeto Component y define una interfaz que se ajusta a la interfaz de Component.
  • ConcreteDecorator: Añade las responsabilidades al componente.

El uso de este patrón, como ya hemos comentado anteriormente, nos añade mucha más flexibilidad que la herencia estática proveyéndonos de mecanismos para añadir o eliminar responsabilidades en tiempo de ejecución. Quizás el mayor problema que plantea será el de mantenibilidad del código ya que genera muchos objetos pequeños muy similares.

Pero, como siempre, lo mejor de todo es ver esto con un ejemplo. En este caso consistirá en un sistema de envío de emails corporativos.

Component:

public interface IEmail {
    public String getContents();
}

ConcreteComponent:

public class Email implements IEmail {
    private String content;

    public Email(String content) {
        this.content = content;
    }

    @Override
    public String getContents() {
        return content;
    }
}

Decorator;

public abstract class EmailDecorator implements IEmail
    IEmail originalEmail;
}

ConcreteDecorator:

public class ExternalEmailDecorator extends EmailDecorator {
    private String content;

    public ExternalEmailDecorator(IEmail basicEmail) {
        originalEmail = basicEmail;
    }

    @Override
    public String getContents() {
        content = addDisclaimer(originalEmail.getContents());
        return content;
    }

    private String addDisclaimer(String message) {
        return  message + "\n Disclaimer";
    }
}

public class SecureEmailDecorator extends EmailDecorator {
    private String content;

    public SecureEmailDecorator(IEmail basicEmail) {
        originalEmail = basicEmail;
    }

    @Override
    public String getContents() {
        content = encrypt(originalEmail.getContents());
        return content;
    }

    private String encrypt(String message) {
        return encryptedMessage;
    }
}

Main:

public class Sender() {
    public static void main(String[] args) {
        ...
    }

    public String generateEmail(IEmail email, int sentType) {
        String emailText = "";

        switch(sentType) {
            case EXTERNAL:
                EmailDecarator d1 = new ExternalEmailDecorator(email);
                emailText = d1.getContents();
                break;
            case SECURE:
                EmailDecarator d2 = new SecureEmailDecorator(email);
                emailText = d2.getContents();
                break;
            default:
                ...
        }

        return emailText;
    }
}

Como podemos ver en el ejemplo se modifican de forma fácil los emails que queremos enviar.

Otros patrones relacionados con este son: Adapter, aunque similares, se diferencian en que el Decorator modifica solo las responsabilidades del objeto, mientras que el Adapter modifica la interfaz. El Composite, ya que se puede ver al patrón Decorator como una degeneración de este con un solo componente. Strategy, es otra alternativa de cambiar un objeto.

Bueno, hasta aquí hemos llegado por hoy. Nos vemos.

 
Deja un comentario

Publicado por en 8 enero, 2014 en Patrones de diseño(GoF)

 

GoF – Patrones de diseño (X): Composite

Tras un breve (quizás no tan breve) periodo de tiempo, vamos a continuar con la serie de artículos relacionados con los patrones de diseño. El siguiente patrón que vamos a ver es el patrón “Composite”. El objetivo de este patrón es el de montar los objetos en una estructura de árbol para representarlos de una forma jerarquizada. De este modo, se podrán manejar de forma similar y uniforme, tanto los objetos individuales como los objetos compuestos.

Este patrón es aplicable en situaciones es las que se necesita trabajar simultaneamente con elementos simples y colecciones que contienen elementos simples u otras colecciones, obteniendo de esta forma la estructura de árbol comentada. Un ejemplo muy utilizado para hacer ver la necesidad y aplicabilidad de este patrón es la implementación de un editor gráfico en el que podemos encontrar multitud de figuras y, además, escenas compuestas por muchas figuras. Siendo las figuras los objectos simples y las escenas colecciones de objetos simples u otras escenas.

Este patrón lo podemos aplicar en las siguientes situaciones:

  • Representar jerarquias de objetos simples y compuestos que puedan ser manejados de forma uniforme.
  • Permitir a los clientes ignorar las diferencias entre objetos individuales o composiciones de objetos.

Los elementos implicados en este patrón son:

  • Component: Declara la interfaz para objectos de la composición, implementa el comportamiento por defecto que será común a todas las clases, declara la instancia para el acceso y manejo de los componentes hijos y, opcionalmente, define la interfaz para acceder a los componentes padres en una estructura recursiva e implementa esta última si es necesario.
  • Leaf: Representa los objetos simples de la composición. Estos objetos no tiene más hojas. Además, define el comportameinto de estos objetos primitivos.
  • Composite: Define el comportamiento para componentes que tienen hijos, almacena objetos simples de la composición e implementa el comportamiento relacionado con los hijos en el “Component”.
  • Client: Manipula los objetos de la composición a través de los métodos del “Component”.

Como en todos los casos anteriores, la aplicación del patrón tiene sus fortalezas, pero también provoca algunos pequeñas pegas como que puede hacer el diseño sea demasiado general y, en ocasiones, que sea díficil controlas los elementos que se incluyen en las diferentes composiciones y pertenecen a ellas. Para paliar este problema, se pueden incluir controles en tiempo de ejecución para poder controlarlo.

Pero, para ver de verdad como funciona esto, vamos a realizar un pequeña implementación como siempre. El típico ejemplo, como ya he comentado antes, suele ser algún editor gráfico, pero para nuestro ejemeplo vamos a utilizar algo que está en la naturaleza y que posee está estructura de árbol que perseguimos implementar, las moléculas. Como objetos simples, tendremos los átomos, y como objetos compuestos las moléculas, que a su vez pueden estar compuestas por otras moléculas.

Component:

public abstract class Component {
    protected String nombre;
    protected int weight;
    public Component(String nombre, int weight) {
        ...
    }
    abstract public void add(Component c);
    abstract public void remove(Component c);
    abstract public int obtainWeight();
}

Leaf:

public class Atom extends Component {
    public Atom(String nombre, int weight) {
        super(nombre, weight);
        ...
    }
    public void add(Component c) {
        ...
    }
    public void remove(Component c) {
        ...
    }
    public int obtainWeight() {
        return weight;
    }
}

Composite:

public class Molecule extends Component {
    private List<Component> list;
    public Molecule(String nombre) {
        this.nombre = nombre;
        ...
    }
    public void add(Component c) {
        list.add(c);
    }
    public void remove(Component c) {
        list.add(c);
    }
    public int obtainWeight() {
        int total = 0;
        for(Component c : list) {
            total += c.obtainWeight();
        }
        return total;
    }
}

Client:

public class Laboratory {
    public static void main(String[] args) {
        Component a1 = new Atom("a1", 12);
        Component a2 = new Atom("a2", 10);
        Component m1 = new Molecule("m1");
        m1.add(a1);
        m1.add(a2);
        System.out.println(a1.obtainWeight());
        System.out.println(a2.obtainWeight());
        System.out.println(m1.obtainWeight());
    }
}

Con este código en el cliente, estaríamos creando dos nuevos átomos y con ellos formando una molécula y utilizando sus métodos para obtener su peso molecular o atómico segun corresponda.

Otros patrones relacionados con este son: el patrón Decorator que muchas veces se suele utilizar en conjunto con Composite teniendo de esta forma una clase común padre. El patrón Flyweight, que aún no hemos visto, pero permite compartir componentes, aunque se pierde la accesibilidad a los componentes padres. El Iterator, tampoco lo hemos visto aún, puede ser usado para cruzar objetos Composite. El patrón Visitor, tampoco lo hemos visto aún, localiza operaciones y componentes que podrían de otra forma distribuirse por las clases Composite y Leaf.

Nos vemos.

 
Deja un comentario

Publicado por en 7 enero, 2014 en Patrones de diseño(GoF)

 

GoF – Patrones de diseño (IX): Bridge

El siguiente de los patrones que vamos a ver es el Bridge. El objetivo de este patrón es la separación (desacoplamiento) de una abstracción de su implementación, de tal forma que ambas puedan ser modificadas independientemente sin a fectar la una a la otra. Bien, ¿y qué significa esto? Porque seguro que os lo estáis preguntando o al menos, yo si que lo hice la primera (segunda y tercera) vez que leí la frase. Por ponerlo en palabras más entendible o intentar explicar la idea, vamos a comentar primero el caso habitual. Imaginemos una clase abstracta a partir de la cual, posteriormente, vamos a implementar por mecanismos de herencia varia subclases. En este caso, tanto la clase abstracta (que está definiendo el interfaz), como las subclases implementadas, están fuertemente ligadas lo que nos hace perder mucha flexibilidad a la hora de modificar, extender o reusar todo este conjunto. Pues bien, este problema de flexibilidad es lo que viene a solicitar este patrón Bridge colocando en diferentes jerarquias la clase abstracta y sus implementaciones.

Utilizamos este patrón cuando:

  • Se quiere evitar un enlace permanente entre la abstracción y su implementación. Este podría ser el caso, por ejemplo, cuando una implementación debe ser seleccionada o cambiada en tiempo de ejecución.
  • Cuando la abstracción y su implementación deben poderse extender creando nuevas subclases. En este caso, el patrón nos permite combinar diferentes abstracciones e implementaciones y extender todas ellas de forma independiente.
  • Los cambios en la implementación de una abstracción no deberían tener impacto en los clientes (el código no debería tener que ser recompilado).
  • Los cambios en la implementación no deberían ser visibles para los clientes.
  • Cuando se quiere compartir una implementación entre múltiples clientes, pero no se desea que el cliente sea conciente de esto.

Los elementos implicados en este patrón son:

  • Abstraction: Define la clase abstracta que será la interfaz y mantiene la referencia al objeto de tipo Implementor.
  • RefinedAbstraction: Extiende la intefaz definida por Abstraction.
  • Implementator: Define la interfaz para la implementación de las clases. No tiene porque tener correspondencia con el interfaz Abstraction. De hecho, suelen ser diferentes. Por lo general, Implementor facilita las operaciones primitivas,y Abstraction define las operaciones de alto nivel basadas en esas primitivas.
  • ConcreteImplementor: Implementa Implementor creando ya una clase para su utilización.

Las consequencias de la aplicación de este patrón son el desacoplamiento de la interfaz y de su implementación, pudiendo cambiar estos incluso en tiempo de ejecución. Además, se eliminan dependencias de compilación y se consigue una arquitectura estructurada en más niveles (esto, no se si es del todo bueno, pero…) Otra de las cosas que si que nos aporta es la flexibilidad ya que se puede heredar de una u otra pudiendo hacer diferentes jerarquias. Y finalmente, nos aporta el esconder detalles de implementación a los clientes.

Pero como siempre, vamos a ver un poco en código como queda esto, que yo sigo pensando que es una de las mejores formas de entender finalmente la idea. Vamos unos mandos a distancia de televisión.

Implementator:

public interface TV {
    public void on();
    public void off();
    public void tunechannel(int);
}

Clase interfaz para las diferentes marcas de televisores.

ConcreteImplementator:

public class Sony implements TV {
    public void on() { ... }
    public void off() { ... }
    public void tuneChannel(int channel) { ... }
}

ConcreteImplementator:

public class LG implements TV {
    public void on() { ... }
    public void off() { ... }
    public void tuneChannel(int channel) { ... }
}

Estos serán nuestros modelos especificos de televisiones.

Abstraction:

public abstract class RemoteControl {
    private TV tv;
    public void on() { tv.on(); }
    public void off() { tv.off(); }
    public void tuneChannel(int channel) { tv.tuneChannel(channel); }
}

Con esto tendríamos definido nuestro control remoto.

RefinedAbstraction:

public class RemoteControlPlus implements RemoteControl {
    private int channel;
    public void next() { channel++; }
    public void previous() { channel--; }
}

Con esto le hemos añadido a nuestor control remoto más funcionalidades sin tener que modificar nada más en la jerarquia de clases, ni haber modificado nada en el Implementator. Esto, como ya se ha dicho antes, nos aporta más flexibilidad, pero genera más complejidad y niveles en nuestra estructura jerarquica lo cual no siempre es bueno. Pero bueno, como siempre, hay que ponderar todas las decisiones que tomemos y ver si nos aportan más beneficios o más problemas.

Otros patrones relacionados con este son el de Abstract Factory que puede crear y configurar un Bridge particular, y el patrón Adapter que sele ser muy util para hacer que clases no relacionadas trabajen juntas.

Hasta aquí todo por hoy. Nos vemos.

 
Deja un comentario

Publicado por en 7 octubre, 2013 en Patrones de diseño(GoF)

 

GoF – Patrones de diseño (VIII): Adapter

Retomando de nuevo la serie de artículos sobre patrones de diseño, hoy vamos a ver el patrón Adapter. Este va a ser el primero de los patrones estructurales que vamos a ver. La definición dada por el libro es que este patrón nos permite convertir una interfaz de una clase en otra interfaz que el cliente está esperando. El patrón Adapter permite de esta forma trabajar juntas a clases que de otra forma no podrían por la incompatibilidad de sus interfaces. Básicamente, y en palabras mucho más llanas, vosotros lo conoceréis como Wrapper, que no es más que una envoltura sobre algún objeto en concreto para que podamos utilizarlo desde puntos donde no estaba pensado antes.

Con las lineas que se han explicado arriba deberíamos tenerlo ya claro, sobre todo, después de saber que este patrón es un wrapper, termino que casi cualquier desarrollador estoy seguro de que conoce. Pero de todas formas, vamos a entrar un poco en detalle. Este tipo de patrón se creo para intentar que objetos implementados a partir de diferentes interfaces pudieran trabajar juntos salvando esta diferencia de interfaces. De esta forma objetos que ya tenemos implementeados pueden ser adaptados para ser reutilizados sin que se necesiten cambios en ellos, simplemente envolviéndolos con una capa alrededor que nos permitirá adaptarlos al nuevo interfaz deseado. Pero como siempre, lo mas fácil es ver esto con un ejemplo, así que sigamos avanzando.

Este patrón se compone de los siguientes elementos:

  • Target: Define el dominio específico de la interfaz que el cliente va a utilizar.
  • Client: Maneja los diferentes objetos según le permite el interfaz Target.
  • Adaptee: Define un interfaz existente que necesita ser adaptado.
  • Adapter: Adapta el interfaz de Adaptee al interfaz usado por el cliente Target.

Al final, el comportamiento resimido de esto, será que el cliente llamará a métodos del interfaz Adapter que a su vez llamarán a métodos del objeto Adaptee.

Pero bueno, vamos a ver todo esto con un poco de código que seguro que nos lo deja algo más claro que una explicación. Vamos a implementar una mini aplicación que poner vehiculos de carreras a correr en un similador por ejemplo, y añadir un vehiculo que no es de carreras para hacer unas pruebas.

Target:

public interface VehiculoCarreras {
    public void correr();
    ...
}

Clases válidas:

public class CitroenCarreras extends vehiculoCarreras {
    public void correr() {
        ...
    }
}
public class OpelCarreras extends vehiculoCarreras {
    public void correr() {
        ...
    }
}

Adaptee:

public class Renault {
    public void arranca() {
        ...
    }
    public void meterPrimera() {
        ...
    }
    public void acelerar() {
        ...
    }
    ...
}

Adapter:

public class RenaultCarrerasWrapper extends VehiculoCarreras {
    public void correr() {
        ...
        renault.arranca();
        renault.metePrimera();
        renault.acelera();
        ...
    }
}

Client:

public class Main() {
    public static void main(String[] args) {
        VehiculoCarreras vc = new CitroenCarreras();
        vc.correr();

        vc = new OpelCarreras();
        vc.correr();

        // vc = new Renault(); No es posible hacer esto
        vc = new RenaultCarrerasWrapper();
        vc.correr();
    }
}

Como podéis ver tenemos entre nuestros vehiculos de carreras un coche normal (Renault) porque queremos hacer una prueba con él, para no tener que tocar la implementación de este, hemos desarrollado un wrapper por encima (RenaultCarrerasWrapper) para que pueda ser manejado por nuestro programa.

Así de simple es este patrón. Como final, solo queda añadir que está relacionado con patrones como:

  • Bridge: Tienen una estructura similar, pero el objetivo de Bridge es separar la interfaz de la implementación. Ya lo veremos próximamente.
  • Decorator: Es otro camino para cambiar un objeto sin cambiar su interfaz, quizás este sea un poquito más transparente, lo cual le permite soportar composición recursiva, cosa que no es posible con Adapter.
  • Proxy: Define una representación o sustituto de un objeto sin cambiar su interfaz. Ya lo veremos más adelante.

Bueno, hasta aquí todo. Espero que os sirva de ayuda y haberos dejado un poquito más claro este patrón. Nos vemos.

 
Deja un comentario

Publicado por en 22 julio, 2013 en Patrones de diseño(GoF)

 

GoF – Patrones de diseño (VII): Singleton

El siguiente de los patrones que vamos a ver es el de Singleton. Asumo que muchos de vosotros ya lo conoceréis ya que es muy común y muchas clases Java lo implementan, pero había que explicarlo también.

El objetivo de este patrón es asegurarse de que de una clase solo existe una instancia y que esta es accesible, o mejor dicho, ofrecer un punto de acceso a ella.

Existen muchas clases para las cuales es importante tener únicamente una sola instancia que pueda ser utilizada en muchas partes diferentes del sistema. Por ejemplo, por muchas impresoras que haya en nuestro sistema lo interesante es tener una sola cola de impresión que maneje todas las impresiones. Otro ejemplo podría ser un gestor de ventanas de una aplicación, ya que no vamos a tener varios gestores de ventanas para una misma aplicación. Y sin duda, una que todo programador de Java conocerá, la clase Calendar que es un objeto que implementa el patrón Singleton. Si no lo sabías aún, terminad de leer el artículo y os daréis cuenta fácilmente de ello.

Vamos a empezar. Para ello mantengamos dos ideas en la cabeza: tener una sola instancia y que sea accesible desde cualquier parte de nuestra aplicación.

Alguno estará pensando que teniendo variables globales, ¿para qué hace falta este patrón? Pero pensemos que esto hace un objeto accesible desde cualquier lado, pero no nos asegura la existencia de una sola instancia ya que se pueden crear nuevas instancias. El crearlas o no, quedaría en manos de los desarrolladores, lo cual sabemos que es una buena idea siempre.

Algunos de los beneficios que nos aporta el patrón son:

  • Poder controlar el acceso a la instancia.
  • Reduce el espacio de nombres ya que evita contaminarlo con variables globales.
  • Permite refinar operaciones y la representación a través de la creación de subclases.
  • Permite controlar fácilmente y sin apenas cambios el número de instancias que creamos. Sé que el patrón está enfocado a tener una sola instancia, pero en determinadas circunstancias quizás necesitamos dos o tres por ejemplo.

Todo esto está muy bien, pero vamos a ver un poco de código, porque este patrón es tan simple que con un simple vistazo al código lo entenderemos muy fácilmente.

Clase que implementa patrón Singleton:

public class PrintSpooler {
    private static PrintSpooler instance = null;
    protected PrintSpooler() {
        …
    }
    public static PrintSpooler getInstance() {
        if (instance == null) {
            instance = new PrintSpooler();
        }
        return instance;
    }
}

Main:

public class Main {
    public static void main(String[] args) {
        PrintSpooler spooler = PrintSpooler.getInstance();
        …
    }
}

Como podéis ver el constructor del objeto no es accesible y el el método “getInstance” el que gestiona si se ha de crear o no una instancia del objeto, de forma que si ya existe devuelve la instancia existente.

¿Veis ahora la similitud con la clase Calendar de Java?

Hasta aquí hemos llegado con los patrones creacionales. A partir de aquí, en sucesivos artículos empezaremos a ver los patrones estructurales. Nos vemos.

 
Deja un comentario

Publicado por en 9 mayo, 2013 en Patrones de diseño(GoF)

 

GoF – Patrones de diseño (VI): Prototype

El siguiente de los patrones que vamos a ver es el patrón Ptototype que pertenece, al igual que los anteriores, al grupo de patrones creacionales.

Este patrón tiene como objetivo la especificación de un tipo de objeto que será un prototipo para la creación de nuevos objetos copiando este, o mejor dicho, clonando este prototipo.

Una explicación más informal de estoy sería algo así como, construir una jerarquía de clases en las cuales, el primer objeto que se crea sea el prototipo, y a partir de este momento, no se van a crear más nuevos objetos, sino que se van a clonar estos a partir del primero. Por aproximarlo un poco a la programación, aunque luego veremos un ejemplo, nuestra aplicación hará un “new” del objeto prototipo y a partir de ahí, el esto de objetos se obtendrán por clonación.

Y esto, ¿para que sirve? Pues por un lado, viene a solventar problemas derivados de clases dependientes de factores externos que solo se conocerán en tiempo de ejecución y, por otro, nos permite evitar, en el caso de objetos muy complejos, los problemas en la creación de estos.

Se debería usar este patrón cuando el sistema debería ser independiente de cómo sus productos son creados, rellenados y presentados. Además, como ya he comentado, cuando la clase a instanciar se especifica en tiempo de ejecución o para evitar la creación de factorías con herencias de clases complejas o cuando las instancias de una clases difieren únicamente en un estado. En este último caso suele ser mejor, crear el prototipo y clonar dicho prototipo para obtener instancias de nuevos objetos.

Este patrón se compone de los siguientes elementos:

  • Prototype: Declara una interfaz para clonarse.
  • ConcretePrototype: Implementa la operación de clonación para si mismo.
  • Client: Crea un nuevo objeto, pidiendo al prototype que se clone.

Aunque ya hemos adelantado una cuantas, las consecuencias de la utilización de este patrón son las siguiente:

  1. Nos permitirá crear o borrar productos en tiempo de ejecución.
  2. La especificación de objetos nuevos variando sus valores.
  3. La especificación de objetos nuevos variando su estructura.
  4. Reduce el número de subclases respecto al resultado si no utilizáramos este patrón o si utilizáramos otros como Factory Method para los casos que cubre Prototype.
  5. Nos permite la configuración de una aplicación con clases dinámicas.

La única consideración importante que tenemos que tener, ya sabéis un “Must to Have” es que tenemos que implementar la operación de clonación en cada una de las clases que se van a generar a partir del prototipo. Solo con esto ya podemos realizar la implementación de nuestro patrón.

Peor bueno, ya para no aburriros más, vamos a pasar a un ejemplo de implementación, que al fin y al cabo es lo más divertido. Como siempre, el ejemplo va a ser en Java que, en este caso, nos aporta la interfaz Clonable en su API. Para el que no la conozca, le invito a echarle una ojeada, pero básicamente es un interfaz que nos ofrece Java para realizar una implementación rápida y fácil de este patrón. Esto nos da un ejemplo de lo extendido que está.

El caso más fácil a exponer, y que deliberadamente lo voy a exagerar un poco más para que veáis la utilidad del patrón, es la creación de objetos personas. En este caso concreto vamos a hacer una mini aplicación donde crearemos dos personas que serán hermanos para trabajar con ellos.

Prototype:

Será la clase ofrecida por el API de Java Clonable, con lo cual no la tendremos que implementar

ConcretePrototype:

public class Persona implements Comparable {
    /* Datos personales: Nombre, primer apellido, segundo apellido,
       nombrePadre, nombreMadre, teléfonoDomicilio, nacionalidad,
       calle, numero, ciudad, códigoPostal,… */
    /* Getters y Setters */
}

Client:

public class Main {
    public static void main(String[] args) {
        Persona juan = new Persona();
        /* llamada a setters (mínimo 11 para nuestro caso*/
        Persona maria = (Persona)juan.clone();
        /* Cambiar nombre */
        maria.setNombre(“Maria”);
        /* Trabajar con los objetos */
    }
}

Como se puede ver, la operación clone nos ha ahorrado un montón de setters, y esto hablado de un objeto muy simple. Ahora imaginad uno compuesto por objetos complejos o multitud de campos.

Bueno, hasta aquí hemos llegado por hoy. Nos vemos.

 
Deja un comentario

Publicado por en 30 marzo, 2013 en Patrones de diseño(GoF)