Diseño de software con patrones (parte 2)

Con la colaboración de JavaHispano

Puedes encontrar la versión original en esta dirección

En el artículo anterior acabe diciendo que en el siguiente artículo, es decir, este, explicaría otros patrones, como el creador y el experto. Lo siento pero no será así.

La verdad es que voy a seguir otra aproximación. La pandilla de los cuatro (GoF, ver capítulo anterior) definió tres tipos distintos de patrones:

  • patrones de creacción.
  • patrones estructurales.
  • patrones de comportamiento.

Lo que intentaré hacer desde esta artículo, es ir repasando cada uno de los tipos y los distintos patrones pertenecientes a cada tipo. Seguro que me dejo alguno, ya que hay muchos, pero en fin, todo llegará.

Empezaré con los patrones de creacción, ya que si no tenemos objetos no tenemos programa, al menos no si estamos dedicandonos a la programación orientada a objetos ;-).

Patrones de creacción.

Los patrones de creacción son las soluciones aceptadas como "buenas" a los problemas de creacción de instancias de objetos. Los programas orientados a objetos crean decenas, cientos o incluso miles de instancias de objetos, es por ello, que esta no es una tarea que se puede realizar a la ligera.

Nuestros programas no deben depender de la forma en que se crean y organizan los objetos. Por supuesto que podemos utilizar el operador new cada vez que nos plazca y necesitemos, pero en ocasiones eso puede hacer nuestro software realmente difícil de mantener.

Además, en muchos casos, puede ocurrir que el objeto concreto que necesitemos en un momento concreto dependa del estado de nuestra aplicación en tiempo de ejecución. Por ejemplo, puede ocurrir que en un momento tengamos que dibujar un círculo o un cuadrado, pero no por ello tenemos que llenar nuestro software de sentencias if. El crear clases especiales que abstraen el proceso de creacción de instancias hace que nuestro software sea más flexible y general.

En este artículo empezaremos con dos de los patrones de creacción más usados, el patrón creador de Craig Larman y el patron factoría de la GoF.

El patrón creador (Creator, GRASP de Craig Larman)

Este patrón es muy sencillo y sin duda toda persona que ha trabajado un poco con la POO lo ha utilizado. Lo que define este patrón es que una instancia de un objeto la tiene que crear el objeto que tiene la información para ello. ¿Qué significa esto?, pues que si un objeto A utiliza especificamente otro B, o si B forma parte de A, o si A almacena o contiene B, o si simplemente A tiene la información necesaria para crear B, entonces A es el perfecto creador de B.

Por ejemplo, supongamos que queremos registrar todos los datos de la liga de fútbol. Tenemos clases como Equipo, Futbolista, Arbitro, Partido, Gol, etc. Pues bien, si queremos registrar un gol, ¿quién deberia crear la instanciade Gol?. Analicemos algunas de las posibilidades:

  • El equipo: el gol lo mete un equipo, eso esta claro, pero equipo no es un buen creador ya que el equipo no sabe contra quien juega, ni que jugadores estan en el campo en ese partido.
  • El futbolista: de acuerdo, el gol lo ha metido el delantero centro, pero tampoco el es un buen creador, aunque el "gol real" lo ha "creado" él. El caso es que el jugador no sabe cuando lo ha metido, ni contra quien, por no saber, ni siquiera sabe si esta en el campo o no.
  • El partido: a mi entender, ya que estoy diseñando este ejemplo, el partido sería el creador perfecto, ya que el sabe que equipos están jugando, que jugadores estan en el campo, en que minuto estamos (si fuera un sistema en tiempo real), etc.

Ya veis, este patrón es bastante sencillo y con solo utilizar un poco de lógica podemos conseguir un diseño sencillo y claro con una cohesión y acoplamiento adecuados.

El patrón Factoría (Factory Pattern, GoF)

El patrón factoría es uno de los varios patrones creadores definidos por la GoF. La idea que se esconde detrás de este patrón es la de centralizar el sitio donde se crean los objetos, normalmente donde se crean objetos de una misma "familia", sin dar una definición clara de lo que nuestro software puede enternder como familia, como podría ser componentes visuales, componentes de la lógica del negocio, o objetos concurrentes en el tiempo.

La clase factoría devuelve una instancia de un objeto según los datos que se le pasan como parámetros. Para que la creacción centralizada de objetos sea lo más "útil y eficaz" posible, es de esperar que todos los objetos creados desciendan de la misma clase o implementen el mismo interface (es decir, hagan una operación similar pero de distintas formas), asi podemos usarlos todos de la misma manera, con los mismos métodos (gracias al polimorfismo), sin importarnos que clase concreta estamos tratando en cada momento.

Veamos el típico ejemplo de las figuras geométricas.

Suponiendo la clase base figura:

public abstract Figura
{
  int lado = 0

  public figura (int lado)
  {
    this.lado = lado;
  }

  public abstract int getArea ();

  public abstract void dibujar (Graphics g, int x, int y);

}

Ahora las dos clases hijas, el cuadrado y el círculo.

public Cuadrado
{

  public Cuadrado (int lado)
  {
    super (lado);
}

  public int getArea ()
  {
    return (lado x lado);
  }

  public void dibujar (Graphics g, int x, int y)
  {
    g.setColor (Color.black);
    g.setDrawRect (x, y, lado, lado);
  }

}


public Circulo
{

  public Circulo (int lado)
  {
    super (lado);
  }

  public int getArea ()
  {
    return (Math.PI x lado x lado);
  }

  public void dibujar (Graphics g, int x, int y)
  {
    g.setColor (Color.black);
    g.setDrawArc (x, y, lado, lado, 0, 360);
  }

}

Ahora construimos nuestra clase factoría,una clase realmente sencilla:

public void FactoriaDeFiguras 
{

  public static final int CUADRADO = 0
  public static final int CIRCULO = 1

  public Figura getFigura (int tipo, int lado)
  {
    if (tipo == this.CUADRADO)
    {
      return (new Cuadrado (lado))
    }
    else
    {
      return (new Circulo (lado))
    }
  }

}

Esta factoría funciona recibiendo un argumento que determina que clase de figura crear, pero podríamos determinar la figura que se crea en función del tipo o número de argumentos, aprovechandonos del polimorfismo tan usado en la POO. Por ejemplo, una factoria que cree circulos si recibe un solo argumento, rectangulos si recibe dos, o triangulos si recibe tres. Este principio se puede aplicar hasta el punto que necesitemos.

Ahora, en nuestro código, para utilizar la factoria simplemente tenemos que hacer:

. . .
FactoriaDeFiguras factoria = new FactoriaDeFiguras();
. . .
Figura figura = factoria.getFigura (estado, lado);
. . .
figura.dibujar (g, 10, 10);
. . .

También podemos hacer, si es necesario, que nuestra factoría sea un Singleton (ver artículo anterior), para tenerlo disponible a lo largo de la ejecución del programa si es que la vamos a necesitar habitualmente.

Esto es sólo un ejemplo, las posibilidades son inmensas, pero algunos casos en los que se recomienda usar la factoría son los siguientes:

  • No sabemos la clase de los objetos a instanciar hasta el momento de la ejecución.
  • Queremos centralizar (y quizás auditar) el proceso de creacción de instancias.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP