Patrones de diseño de comportamiento: Strategy

Intención del patrón


  • Definir una familia de algoritmos, encapsular cada uno y hacerlos intercambiables. Strategy le permite al algoritmo variar independientemente del cliente que lo usa.
  • Captura la abstracción en una interfaz y encapsula la los detalles de la implementación en clases derivadas.


Ejemplo de problema

Una de las estrategias dominantes del diseño orientado a objeto es el principio Open-close.
La figura muestra cómo se logra habitualmente encapsular los detalles de la interfaz en una clase base y enterrar los detalles de la implementación en las clases derivadas. Los clientes pueden entonces acoplarse a una interfaz y no tener que experimentar los trastornos asociados con el cambio. No hay impacto cuando el número de clases derivadas cambia y tampoco lo hay cuando la implementación de una clase derivada cambia.



Una de las máximas de la comunidad del software ha sido: "maximizar la cohesión y minimizar el acoplamiento". El punto de vista del diseño orientado a objetos visto en la figura, trata de minimizar el acoplamiento. Ya que el cliente se encuentra atado sólo a una abstracción y no a una clase concreta de dicha abstracción, se podría decir que el cliente tiene un "acoplamiento abstracto". Una variante orientada a objeto de la exhortación: "minimizar el acoplamiento".
Una caracterización más popular de este principio de "acoplamiento abstracto" es "programar para una interfaz, no para una implementación".
Los clientes debería preferir el "nivel adicional de indirección" que una interfaz (o una clase base abstracta) da. La interfaz captura la abstracción que el cliente desea ejecutar y la implementación de dicha interfaz está efectivamente oculta.

Estructura

La entidad Interface podría representar tanto una clase base abstracta como la expectativa del cliente de la firma del método. En el primer caso, la jerarquía de herencia representa polimorfismo dinámico. En el último caso, la entidad Interface representa una plantilla de código en el cliente y la jerarquía de herencia representa polimorfismo estático.


Ejemplo

El patrón Strategy define un conjunto de algoritmos que ser usados de manera intercambiable. Los medios de transporte hacia un aeropuerto es un ejemplo de este patrón. Existen varias opciones como manejar un auto, tomar un taxi, un transporte del aeropuerto, un colectivo (autobus) o un servicio de limusina. Para algunos aeropuertos, el subterráneo o un helicóptero, también son opciones posibles para llegar al aeropuerto. Cualquiera de estos medios de transporte llevará a un pasajero hasta el aeropuerto y cualquiera medio puede suplantar a otro. El pasajero debe elegir al Strategy basado en las ventajas y desventajas entre costo, conveniencia y tiempo.




Check list

  1. Identificar un algortimo (o comportamiento) que el cliente podría preferir para acceder a un punto de inflexión.
  2. Especificar la firma para dicho algoritmo en una interfaz.
  3. Introducir los detalles de la implementación alternativa en una clase derivada.
  4. Los clientes del algoritmo se "acoplan" a la interfaz.

Reglas prácticas

  • Strategy es como Template method a excepción de la granularidad.
  • State es como Strategy excepto por su intención.
  • Strategy permite cambiar las "entrañas" de un objeto. Decorator permite cambiar la "piel".
  • State, Strategy, Bridge ( y en cierto grado Adapter) poseen estructuras similares de solución. Todos ellos comparten elementos del estilo handle/body. Se diferencian en su intención. Esto es porque resuleven distintos problemas.
  • Strategy tiene dos posibles implementaciones, la primera es similar a State. La diferencia está en el tiempo de compilación: Strategy es un patrón que se enlaza una vez, miestras que State es más dinámico.
  • Los objetos Strategy suelen ser buenos Flyweight.

Ejemplo de código en C#

using System;

class MainApp
{
  static void Main()
  {
    Context context;

    // Three contexts following different strategies 
    context = new Context(new ConcreteStrategyA());
    context.ContextInterface();

    context = new Context(new ConcreteStrategyB());
    context.ContextInterface();

    context = new Context(new ConcreteStrategyC());
    context.ContextInterface();

    // Wait for user 
    Console.Read();
  }
}

// "Strategy" 
abstract class Strategy
{
  public abstract void AlgorithmInterface();
}

// "ConcreteStrategyA" 
class ConcreteStrategyA : Strategy
{
  public override void AlgorithmInterface()
  {
    Console.WriteLine(
      "Called ConcreteStrategyA.AlgorithmInterface()");
  }
}

// "ConcreteStrategyB" 
class ConcreteStrategyB : Strategy
{
  public override void AlgorithmInterface()
  {
    Console.WriteLine(
      "Called ConcreteStrategyB.AlgorithmInterface()");
  }
}

// "ConcreteStrategyC" 
class ConcreteStrategyC : Strategy
{
  public override void AlgorithmInterface()
  {
    Console.WriteLine(
      "Called ConcreteStrategyC.AlgorithmInterface()");
  }
}

// "Context" 
class Context
{
  Strategy strategy;

  // Constructor 
  public Context(Strategy strategy)
  {
    this.strategy = strategy;
  }

  public void ContextInterface()
  {
    strategy.AlgorithmInterface();
  }
}
Called ConcreteStrategyA.AlgorithmInterface()
Called ConcreteStrategyB.AlgorithmInterface()
Called ConcreteStrategyC.AlgorithmInterface()

2 comentarios:

Alejandro Martinez dijo...

Buenisimo, estoy aprendiendo este tema, y los ejemplos que das son perfectos, al menos para mi, para asimilar los conceptos.. Gracias! saludos

Juan Barrionuevo dijo...

Alejandro, muchas gracias por tu comentario.
Me alegra muchísimo que te sirva.
Es para lo que nació este blog.
Espero que encuentres más cosas útiles aquí.
Saludos!!

Publicar un comentario

Muchas gracias por leer el post y comentarlo.

 
Copyright 2009 Programación SOLIDa
BloggerTheme by BloggerThemes | Design by 9thsphere