Ya hemos revisado el uso de un objeto singleton, pero es posible que todavía no se haya dado cuenta de que esta estrategia es un patrón de diseño, y uno de los más utilizados además.
Hay muchos de estos patrones, y se pueden clasificar según su propósito específico.
Clasificación de patrones
Tipo de patrón | Aplicación |
---|---|
Creador | Un tipo que resuelve el problema de creación de objetos |
Estructural | Patrones que nos permiten construir una jerarquía de clases correcta y extensible en nuestra arquitectura |
Conductual | Este conjunto de patrones facilita una interacción segura y conveniente entre los objetos en un programa. |
Normalmente, un patrón se caracteriza por el problema que resuelve. Veamos unos cuantos patrones que encontramos con más frecuencia cuando trabajamos con Java:
Patrón | Propósito |
---|---|
Singleton | Ya estamos familiarizados con este patrón: lo usamos para crear y acceder a una clase que no puede tener más de una instancia. |
Iterator | También estamos familiarizados con este patrón. Sabemos que nos permite iterar sobre un objeto de colección sin revelar su representación interna. Se usa con colecciones. |
Adapter | Este patrón conecta objetos incompatibles para que puedan funcionar juntos. Creo que el nombre del patrón de adaptador te ayuda a imaginar exactamente lo que hace. Aquí hay un ejemplo sencillo de la vida real: un adaptador USB para un enchufe |
Método de plantilla |
Un patrón de programación de comportamiento que resuelve el problema de integración y permite cambiar los pasos algorítmicos sin cambiar la estructura del algoritmo. Imagina que tenemos un algoritmo de ensamblaje de automóviles en forma de una secuencia de pasos de ensamblaje: Chasis -> Carrocería -> Motor -> Interior de la cabina Si colocamos un marco reforzado, un motor más potente o un interior con iluminación adicional, no tenemos que cambiar el algoritmo, y la secuencia abstracta sigue siendo la misma. |
Decorador | Este patrón crea envoltorios para objetos para darles funcionalidad útil. Lo consideraremos como parte de este artículo. |
En Java.io, las siguientes clases implementan patrones:
Patrón | Donde se utiliza en java.io |
---|---|
Adaptador |
|
Método de plantilla | |
Decorador |
Patrón Decorador
Imaginemos que estamos describiendo un modelo para un diseño de hogar.
En general, el enfoque se ve así:
Inicialmente, tenemos la opción de varios tipos de casas. La configuración mínima es una planta con un techo. Luego usamos todo tipo de decoradores para cambiar parámetros adicionales, lo que naturalmente afecta el precio de la casa.
Crearemos una clase abstracta Casa:
public abstract class House {
String info;
public String getInfo() {
return info;
}
public abstract int getPrice();
}
Aquí tenemos 2 métodos:
- getInfo() devuelve información sobre el nombre y las características de nuestra casa;
- getPrecio() devuelve el precio de la configuración actual de la casa.
También tenemos implementaciones estándar de Casa, como ladrillo y madera:
public class BrickHouse extends House {
public BrickHouse() {
info = "Brick House";
}
@Override
public int getPrice() {
return 20_000;
}
}
public class WoodenHouse extends House {
public WoodenHouse() {
info = "Wooden House";
}
@Override
public int getPrice() {
return 25_000;
}
}
Ambas clases heredan de la clase House y reemplazan su método de precio, estableciendo un precio personalizado para una casa estándar. Establecemos el nombre en el constructor.
A continuación, necesitamos escribir clases de decoradores. Estas clases también heredarán de la clase House. Para hacerlo, creamos una clase de decorador abstracta.
abstract class HouseDecorator extends House {
}
A continuación, creamos implementaciones de decoradores. Crearemos varias clases que nos permitirán agregar características adicionales a la casa:
public class SecondFloor extends HouseDecorator {
House house;
public SecondFloor(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 20_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + second floor";
}
}
Un decorador que agrega un segundo piso a la casa |
El constructor del decorador acepta una casa que "decoraremos", es decir, a la que agregaremos modificaciones. Y anulamos los métodos getPrice() y getInfo(), devolviendo información sobre la nueva casa actualizada basada en la anterior.
public class Garage extends HouseDecorator {
House house;
public Garage(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 5_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + garage";
}
}
Un decorador que agrega un garage a nuestra casa |
Ahora podemos actualizar nuestra casa con decoradores. Para hacerlo, necesitamos crear una casa:
House brickHouse = new BrickHouse();
A continuación, establecemos nuestra variable casa igual a un nuevo decorador, pasando nuestra casa:
brickHouse = new SecondFloor(brickHouse);
Nuestra variable casa ahora es una casa con una segunda planta.
Veamos algunos casos de uso que involucran decoradores:
Código de ejemplo | Salida |
---|---|
|
Brick House 20000 |
|
Brick House + second floor 40000 |
|
Brick House + second floor + garage 45000 |
|
Wooden House + garage + second floor 50000 |
|
Wooden House 25000 Wooden House + garage 30000 |
Este ejemplo ilustra el beneficio de actualizar un objeto con un decorador. Así, no cambiamos el objeto woodenHouse en sí, sino que creamos un nuevo objeto basado en el anterior. Aquí podemos ver que las ventajas conllevan desventajas: cada vez que creamos un nuevo objeto, aumentamos el consumo de memoria.
Echemos un vistazo a este diagrama UML de nuestro programa:

Un decorador tiene una implementación súper sencilla y cambia dinámicamente los objetos, mejorándolos. Los decoradores se pueden reconocer por sus constructores, que aceptan como parámetros objetos del mismo tipo abstracto o interfaz que la clase actual. En Java, este patrón se utiliza ampliamente en las clases de E/S.
Por ejemplo, como ya hemos señalado, todas las subclases de java.io.InputStream, OutputStream, Reader y Writer tienen un constructor que acepta objetos de las mismas clases.
GO TO FULL VERSION