Mi granito de java: Strategy

domingo, 12 de junio de 2011

Strategy

Encapsula algoritmos en clases, permitiendo que éstos sean re-utilizados e intercambiables. En base a un parámetro, que puede ser cualquier objeto, permite a una aplicación decidir en tiempo de ejecución el algoritmo que debe ejecutar.
La esencia de este patrón es encapsular algoritmos relacionados que son subclases de una superclase común, lo que permite la selección de un algoritmo que varia según el objeto y también le permite la variación en el tiempo. Esto se define en tiempo de ejecución. Este patrón busca desacoplar bifurcaciones inmensas con algoritmos dificultosos según el camino elegido.

Este patrón debe utilizarse cuando:
  • Un programa tiene que proporcionar múltiples variantes de un algoritmo o comportamiento.
  • Es posible encapsular las variantes de comportamiento en clases separadas que proporcionan un modo consistente de acceder a los comportamientos.
  • Permite cambiar o agregar algoritmos, independientemente de la clase que lo utiliza.

Diagrama UML


Strategy: declara una interfaz común a todos los algoritmos soportados.
StrategyConcreto: implementa un algoritmo utilizando la interfaz Strategy. Es la representación de un algoritmo.
Context: mantiene una referencia a Strategy y según las características del contexto, optará por una estrategia determinada..
Context / Cliente: solicita un servicio a Strategy y este debe devolver el resultado de un StrategyConcreto. 

Ejemplo

Imaginemos una biblioteca de un instituto educativo que presta libros a los alumnos y profesores. Imaginemos que los alumnos pueden asociarse a la biblioteca pagando una mensualidad. Con lo cual un libro puede ser prestado a Alumnos, Socios y Profesores.
Por decisión de la administración, a los socios se les prestará el libro más nuevo, luego aquel que se encuentre en buen estado y, por último, aquel que estuviese en estado regular.
En cambio, si un Alumno pide un libro, ocurre todo lo contrario. Por último, a los profesores intentarán otorgargarles libros buenos, luego los recién comprados y, por último, los regulares. Este caso es ideal para el patrón Strategy, ya que dependiendo de un parámetro (el tipo de persona a la que se le presta el libro) puede realizar una búsqueda con distintos algoritmos.




 





Seguramente las distintas estrategias concretas sean Singleton.



Consecuencias

  • Permite que los comportamientos de los Clientes sean determinados dinámicamente sobre un objeto base.
  • Simplifica los Clientes: les reduce responsabilidad para seleccionar comportamientos o implementaciones de comportamientos alternativos. Esto simplifica el código de los objetos Cliente eliminando las expresiones if y switch.
  • En algunos casos, esto puede incrementar también la velocidad de los objetos Cliente porque ellos no necesitan perder tiempo seleccionado un comportamiento.

11 comentarios:

Anónimo dijo...

Me gustaria contribuir con estos posibles algoritmos de busqueda:

//buenonuevoregular

public Libro findLibro(String titulo) {

Libro libro = null;
ArrayList libros = Biblioteca.getInstance().getLibros();
int i=0;
while(i libros = Biblioteca.getInstance().getLibros();
int i=0;
while(i libros = Biblioteca.getInstance().getLibros();
int i=0;
while(i<libros.size() && (!libros.get(i).getTitulo().equals(titulo) || !libros.get(i).getEstado().equals("Nuevo"))){
if(libros.get(i).getTitulo().equals(titulo)){
if(libro==null){
libro = libros.get(i);
}else if(libros.get(i).getEstado().equals("Bueno")){
libro = libros.get(i);
}
}
i++;
}
if(i<libros.size() && (libros.get(i).getTitulo().equals(titulo) && libros.get(i).getEstado().equals("Nuevo"))){
libro = libros.get(i);
}

return libro;
}

Anónimo dijo...

//nuevobuenoregular

public Libro findLibro(String titulo) {
Libro libro = null;
ArrayList libros = Biblioteca.getInstance().getLibros();
int i=0;
while(i<libros.size() && (!libros.get(i).getTitulo().equals(titulo) || !libros.get(i).getEstado().equals("Nuevo"))){
if(libros.get(i).getTitulo().equals(titulo)){
if(libro==null){
libro = libros.get(i);
}else if(libros.get(i).getEstado().equals("Bueno")){
libro = libros.get(i);
}
}
i++;
}
if(i<libros.size() && (libros.get(i).getTitulo().equals(titulo) && libros.get(i).getEstado().equals("Nuevo"))){
libro = libros.get(i);
}

return libro;
}

Anónimo dijo...

//regular bueno nuevo

public Libro findLibro(String titulo) {
Libro libro = null;
ArrayList libros = Biblioteca.getInstance().getLibros();
int i=0;
while(i<libros.size() && (!libros.get(i).getTitulo().equals(titulo) || !libros.get(i).getEstado().equals("Regular"))){
if(libros.get(i).getTitulo().equals(titulo)){
if(libro==null){
libro = libros.get(i);
}else if(libros.get(i).getEstado().equals("Bueno")){
libro = libros.get(i);
}
}
i++;
}
if(i<libros.size() && (libros.get(i).getTitulo().equals(titulo) && libros.get(i).getEstado().equals("Regular"))){
libro = libros.get(i);
}

return libro;
}

Maximiliano Juarez dijo...

Muchas gracias por los algoritmos!

Anónimo dijo...

Hola, como te he puesto en algún comentario anterior gracias de nuevo por el magnífico trabajo, ejemplos claros , sencillos y concisos.
Como se nota que te dedicas o has dedicado a la formación.

Mi comentario es por lo siguiente, no me queda muy claro la diferencia entre el patrón strategy y el bridge y la conveniencia de usar uno u otro, ya que los dos me parece muy parecidos y el bridge tambien puede cambiar el comportamiento de una interfaz cambiando dinamicamente su implementacion.

Un saludo

Maximiliano Juarez dijo...

Hola, gracias por tus comentarios! Respondiendo a tu pregunta, es verdad que hay muchos casos que pueden utilizarse ambos, pero tambien hay muchos casos donde uno de ellos no tendria sentido. Si miras los ejemplos de ambos, carecen de sentido aplicarlos con el otro patron. Toma al strategy como un decisor sencillo que busca evitar una maraña de if else. La experiancia en la programacion te va a dar la pauta de cual utilizar.

Anónimo dijo...

buenas tengo una duda con este ejercicio, muy poca idea de codificación y se me complica:
Se tiene un sistema de mensajes, similar a los mensajeros actuales como
gtalk, msn etc. Un mensajero colabora con un objeto conexión para enviar
los mensajes a través de la red. El objeto conexión es el que posee la lógica
necesaria para enviar los mensajes. Existe una subclase de conexión que es
la “conexión segura”, que además de heredar el comportamiento de
conexión permite enviar los mensajes a través de la red, utiliza un
encriptador que es el responsable de encriptar los mensajes antes que la
conexión lo envíe por la red.

1. Modifique el diseño original para que ahora la conexión segura pueda
utilizar diferentes algoritmos para encriptar los mensajes como por
ejemplo Blowfish, RC4 y RSA;
2. Implemente la solución planteada en Java. No tiene que implementar
los algoritmos, simplemente imprima por consola algo del Estilo “Encriptando” + mensaje + “utilizando el algoritmo Blowfish”; ni
tampoco el método enviar, en este caso imprima algo del estilo
“Enviando mensaje: ” + mensaje;
3. Defina la clase TestMensajero con su método main, donde se
instancie un objeto Mensajero con una conexión segura que utilice el
algoritmo RC4 para encriptar los mensajes. Luego envíe un mensaje a
través del mensajero.

Marcelo Hervas Granados dijo...

Gracias de corazón
Quisiera contribuir con mi trabajo
Un Saludo.
Marcelo Hervás Granados


------------------------------------------------------------------------------------------
clase libro
------------------------------------------------------------------------------------------

package aplicacionpatronstrategy;

import java.util.Objects;


public class Libro {
private String titulo;
private String estado;
private String situacion;

public Libro(String titulo, String estado,String situacion) {
this.titulo = titulo;
this.estado = estado;
this.situacion = situacion;
}

public String getEstado() {
return estado;
}

public void setEstado(String estado) {
this.estado = estado;
}

public String getTitulo() {
return titulo;
}

public void setTitulo(String titulo) {
this.titulo = titulo;
}

public String getSituacion() {
return situacion;
}

public void setSituacion(String situacion) {
this.situacion = situacion;
}

public void visualizar(){
System.out.println(this.getTitulo()+":"+this.getEstado()+":"+this.getSituacion());
}
@Override
public boolean equals(Object o){
if(!(o instanceof Libro)){
return false;
}else{
Libro otro=(Libro)o;
if(!this.getTitulo().equals(otro.getTitulo())){
return false;
}else{
if(!this.getEstado().equals(otro.getEstado())){
return false;
}else{
if(!this.getSituacion().equals(otro.getSituacion())){
return false;
}
}
}

}
return true;
}

@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.titulo);
hash = 29 * hash + Objects.hashCode(this.estado);
hash = 29 * hash + Objects.hashCode(this.situacion);
return hash;
}
}

Marcelo Hervas Granados dijo...

Gracias de corazón
Quisiera contribuir con mi trabajo
Un Saludo.
Marcelo Hervás Granados

-----------------------------------------------------------------------------------------------------
Interfaz Constantes
-----------------------------------------------------------------------------------------------------
package aplicacionpatronstrategy;

public interface Constantes {
String estados[]={"BUENO","NUEVO","REGULAR"};
String titulos[]={"JAVA","UML","PATRONES","POO"};
}

-----------------------------------------------------------------------------------------------------
Clase Biblioteca
-----------------------------------------------------------------------------------------------------
package aplicacionpatronstrategy;

import java.util.ArrayList;
import java.util.Iterator;


public class Biblioteca {
/**
*Biblioteca es un Singleton
*/
private static Biblioteca biblioteca;
private static ArrayList libros;

@SuppressWarnings("empty-statement")
private Biblioteca() {
libros=new ArrayList<>();
for(int i=0;i getLibros() {
return libros;
}
public void visualizarBiblioteca(){
for (Iterator iter=libros.iterator();iter.hasNext();) {
Libro libro=(Libro)iter.next();
libro.visualizar();
}
}

}
-----------------------------------------------------------------------------------------------------
Clase LibroFinder
------------------------------------------------------------------------------------------------------
package aplicacionpatronstrategy;

import java.util.ArrayList;


public class LibroFinder {
public Libro EncontrarLibro(ArrayList libros, Persona persona,String titulo){

LibroStrategy strategy=null;
if(persona instanceof Socio){
strategy=new NuevoBuenoRegularStrategy();
}else if (persona instanceof Profesor){
strategy=new BuenoNuevoRegularStrategy();
}else{
strategy=new RegularBuenoNuevoStrategy();
}
return strategy.findlibro(libros, titulo);
}

}
------------------------------------------------------------------------------------------------------
Interfaz LibroStrategy
------------------------------------------------------------------------------------------------------
package aplicacionpatronstrategy;

import java.util.ArrayList;

public interface LibroStrategy {

public Libro findlibro(ArrayList libros,String titulo);

}

Maximiliano Juarez dijo...

Muchas gracias Marcelo!!

Maximiliano Juarez dijo...

Para el anonimo que publico antes que Marcelo: lei tu msg muy tarde, para este tipo de cosas no tengo problema en dar una mano, pero mejor enviame un mail.