Los patrones de diseño(design patterns en ingles) en síntesis son técnicas que resuelven problemas conocidos de diseño de software. Por lo que no tenemos que rompernos la cabeza buscándole una solución a dichos problemas, sino que podemos asirnos de los distintos patrones que ya otros genios del área han figurado.
Existen unos cuantos principios de diseño(design principles en ingles) que nos ayudan a programar mucho mejor si los mantenemos en mente, esto es crear códigos extensibles y de fácil mantenimiento, sobre los mismos suelen basarse los patrones de diseño.
A continuación presento tres principios de diseño:
1.)
Identifica los aspectos de tu aplicación que varían y separalo de aquellos que permanecen iguales.
Un ejemplo, en un vídeo juego una clase Character tiene un método fight() los personajes siempre van a pelear en este hipotético juego que es un genero de pelea por así decir, de modo que usaran algún tipo de arma pero el personaje a su vez puede cambiar de armas varias veces, por tanto, si implementamos la lógica del arma dentro de la clase Character no podríamos extender esa característica del juego luego si quisiéramos agregar algo nuevo en cuanto a las armas, sin tener que modificar el código de la clase, violando así otro principio llamado open/closed principle.
2.)
Programa orientado a una interfaz, no a una implementación.
Interfaz aquí no se refiere exactamente a una interfaz como tienen Java o C#, es decir, que interfaz aquí se refiere tanto a un interface como seria en los lenguajes antes mencionado o igualmente podría ser una clase abstracta, la cuestión es que este principio nos exhorta a usar en nuestras aplicaciones un super tipo o supertype en vez de una clase concreto.
Me explico mejor, supongamos que siguiendo con el ejemplo anterior en nuestra aplicación usualmente usaremos
2 armas: Cuchillo(Knife) y hacha(Axe). Entonces podría darse el caso que en algún momento quisiéramos agregar
al juego nuevas armas, si en la propiedad de la arma en el personaje
fuere una arma concreta, digamos que se llame Knife, de modo que tendríamos en la clase Character
algo como
Knife knife = new Knife()
haciendo así seria imposible agregar nuevas armas al juego sin modificar esta clase.
De modo que ahí estaríamos usando una implementación
que aquí se refiere a usar una clase concreta, en cambio,
si creamos una Interfaz o una clase abstracta como plantilla de la cual extenderán o implementaran todas las
demás armas y que el campo en la clase Character vendría siendo precisamente esa clase padre, por lo que en el
cliente donde instanciemos Character podríamos pasarle cualquier clase como arma que sea del tipo de la
anterior clase padre, así pudiendo extender esta para crear nuevas sin tener que modificar el código existente.
3.)
Favorece
composición
sobre herencia.
Hay que entender bien esto aquí, porque herencia aun sigue siendo la mejor opción en algunos casos, así que
hay que entender digamos el contexto. Este principio se refiere a que si quieres agregar una funcionalidad(o
un mínimo de las funcionalidades de esta) de otra clase a otra en vez de usar herencia que te limitaría en
muchas cosas, ya que de por si la relación de tu clase con la que heredes seria del tipo "IS-A"(es decir,
que es del mismo tipo en otras palabras la extiende o implementa), encima que no se soporta la herencia múltiple
en algunos lenguajes, así que vaye imaginándose, además de eso todo el código que no necesitas de la misma.
Lo otro es, que composición
aquí se refiere a que la clase en cuestión contenga la otra clase, por lo que
igual seria valido composición
o agregación
en términos
estrictos de UML.
Patrón de diseño Strategy (Strategy pattern)
Define una familia de protocolos, encapsula cada uno de ellos y los hace intercambiables. Strategy permite al algoritmo variar independientemente de los clientes que lo usen.
A continuación un diagrama UML de un juego de Aventuras(el que intencionalmente use antes para las explicaciones mas arriba), el mismo utiliza Strategy para ofrecer la flexibilidad de poder agregar cuantas armas se quieren y poder cambiarlas en tiempo de ejecución, observar como se reflejan los tres principios de diseño de los que se hablo antes.
El diagrama se explica así mismo, notar que Character es una clase abstracta y que usamos el método setWeapon() para poder cambiar de arma(estrategia) en runtime el cual definimos en la misma clase padre Character ya que posiblemente no haya necesidad de sobreescribirlo jamas.
Ahora veremos el código en Java, puede escribirse fácilmente en otros lenguajes, lo importante es tomar el concepto, claro evidentemente no todos los lenguajes permiten hacer esto, la wikipedia sobre los requisitos para implementar el Strategy pattern dice:
The essential requirement in the programming language is the ability to store a reference to some code in a data structure and retrieve it. This can be achieved by mechanisms such as the native function pointer, the first-class function, classes or class instances in object-oriented programming languages, or accessing the language implementation's internal storage of code via reflection.
Character.java
public abstract class Character{ WeaponBehavior weapon; public Character(WeaponBehavior weapon){ this.setWeapon(weapon); } public abstract void fight(); public void setWeapon(WeaponBehavior weapon){ this.weapon = weapon; } }
King.java
public class King extends Character{ public King(WeaponBehavior weapon){ super(weapon); System.out.println("El Rey ha entrado al juego...inclinate!"); } public void fight(){ System.out.println("El rey da un paso hacia un lado como " + "en el ajedrez y de repente..."); weapon.useWeapon(); } }
Queen.java
public class Queen extends Character{ public Queen(WeaponBehavior weapon){ super(weapon); System.out.println("La Reina ha entrado al juego...inclinate!"); } public void fight(){ System.out.println("Como toda mujer(o casi) aruña, grita y muerde...y de momento..."); weapon.useWeapon(); } }
Knight.java
public class Knight extends Character{ public Knight(WeaponBehavior weapon){ super(weapon); System.out.println("Un noble caballero ha entrado al juego!"); } public void fight(){ System.out.println("El Caballero de repente..."); weapon.useWeapon(); } }
Troll.java
public class Troll extends Character{ public Troll(WeaponBehavior weapon){ super(weapon); System.out.println("Un infame troll ha entrado al juego!"); } public void fight(){ System.out.println("Y el troll cual criatura malvada..."); weapon.useWeapon(); } }
WeaponBehavior.java
public interface WeaponBehavior{ public void useWeapon(); }
KnifeBehavior.java
public class KnifeBehavior implements WeaponBehavior{ public KnifeBehavior(){ System.out.println("Cuchillo activado..."); } public void useWeapon(){ System.out.println("Fuaa!!! Cuchillo arriba, cuchillo abajo...y un brazo menos!"); } }
SwordBehavior.java
public class SwordBehavior implements WeaponBehavior{ public SwordBehavior(){ System.out.println("Espada activada..."); } public void useWeapon(){ System.out.println("Saca la espada como un samurai, directo al corazon" + " y baaaaam! El enemigo es historia"); } }
AxeBehavior.java
public class AxeBehavior implements WeaponBehavior{ public AxeBehavior(){ System.out.println("Hacha activada..."); } public void useWeapon(){ System.out.println("Agarra el hacha por el palo, la hala en forma " + "de media luna y buuum! Enemigo abatido!"); } }
BowAndArrowBehavior.java
public class BowAndArrowBehavior implements WeaponBehavior{ public BowAndArrowBehavior(){ System.out.println(""); } public void useWeapon(){ System.out.println("Se sube a un alto, agarra su arco, flecha en mano," + "todo listo, suelta y baaaaam\n a la cabeza del enemigo!" + " Totalmente enfermo!"); } }
Por último, una clase para probar todo esto MainClass.java
public class MainClass{ public static void main(String[] args){ // Declaramos e instanciamos los personajes Character c1 = new King(new KnifeBehavior()); Character c2 = new Queen(new KnifeBehavior()); Character c3 = new Knight(new KnifeBehavior()); Character c4 = new Troll(new KnifeBehavior()); System.out.println(); // Y a pelear!!! - Instancia de King c1.fight(); // Primero con la arma por defecto que es el cuchillo c1.setWeapon(new AxeBehavior()); // Y en runtime cambiamos el arma a Hacha c1.fight(); // Atacamos con nuestra flamante nueva arma System.out.println(); // Lo mismo que en lo anterior con diferentes armas // Instancia de Queen c2.fight(); c2.setWeapon(new SwordBehavior()); // Cambiamos el arma a espada c2.fight(); System.out.println(); // Instancia de Knight c3.fight(); c3.setWeapon(new BowAndArrowBehavior()); // Cambiamos el arma a arco y flecha c3.fight(); System.out.println(); // Instancia de Troll c4.fight(); c4.setWeapon(new SwordBehavior()); // Cambiamos el arma a espada c4.fight(); } }
Cuando compilamos y corremos la clase MainClass esta tiene la siguienda salida:
Cuchillo activado... El Rey ha entrado al juego...inclinate! Cuchillo activado... La Reina ha entrado al juego...inclinate! Cuchillo activado... Un noble caballero ha entrado al juego! Cuchillo activado... Un infame troll ha entrado al juego! El rey da un paso hacia un lado como en el ajedrez y de repente... Fuaa!!!! Cuchillo arriba, cuchillo abajo...y un brazo menos! Hacha activada... El rey da un paso hacia un lado como en el ajedrez y de repente... Agarra el hacha por el palo, la hala en forma de media luna y buuum! Enemigo abatido! Como toda mujer(o casi) aruña, grita y muerde...y de momento... Fuaa!!!! Cuchillo arriba, cuchillo abajo...y un brazo menos! Espada activada... Como toda mujer(o casi) aruña, grita y muerde...y de momento... Saca la espada como un samurai, directo al corazon y baaaaam! El enemigo es historia El Caballero de repente... Fuaa!!!! Cuchillo arriba, cuchillo abajo...y un brazo menos! El Caballero de repente... Se sube a un alto, agarra su arco, flecha en mano,todo listo, suelta y baaaaam a la cabeza del enemigo! Totalmente enfermo! Y el troll cual criatura malvada... Fuaa!!!! Cuchillo arriba, cuchillo abajo...y un brazo menos! Espada activada... Y el troll cual criatura malvada... Saca la espada como un samurai, directo al corazon y baaaaam! El enemigo es historia
Esa fue una demostración del patrón de diseño Strategy...usando el lenguaje Java en esta ocasión.
exit(0);
[+] Referencias
- Wikipedia: Strategy Pattern
- Eric Freeman, Elisabeth Freeman. Head First Design Patterns. (Chapter 1)