Estrategia (patrón de diseño)

Este artículo puede contener trabajos no publicados o declaraciones no verificadas (octubre 2014).

Puede ayudar agregando referencias o eliminando contenido no publicado.

En ingeniería de software , la estrategia del jefe es un patrón de diseño ( patrón de diseño) de algoritmos de comportamiento a través del cual se puede seleccionar sobre la marcha durante el tiempo de ejecución bajo ciertas condiciones.

El patrón de diseño de estrategia es útil para situaciones en las que es necesario intercambiar dinámicamente los algoritmos utilizados en una aplicación. El patrón de estrategia está destinado a proporcionar los medios para definir una familia de algoritmos, encapsular cada uno de ellos como un objeto y hacerlos intercambiables. Este jefe permite que los algoritmos cambien independientemente de los clientes que los empleen.

usar

Dado que un objeto puede realizar varios tratamientos diferentes, dependiendo de una variable o un estado.

Estructura

Ejemplo en C ++

#include <iostream> #include <memory> // IStrategie est l’interface permettant d’exécuter un algorithme class IStrategie { public: void execute() { process(); } // NVI virtual ~IStrategie() = default; // Héritage, donc destructeur public virtuel private: virtual void process() = 0; // IStrategie::process() est une fonction virtuelle pure // et de ce fait IStrategie est une classe abstraite // autrement dit une classe qui ne peut être instanciée }; class AlgorithmeA : public IStrategie { private: // Chaque Algorithme redéfinit la façon de procéder void process() override { std::cout << "Traitement A" << std::endl; } }; class AlgorithmeB : public IStrategie { private: void process() override { std::cout << "Traitement B" << std::endl; } }; class AlgorithmeC : public IStrategie { private: void process() override { std::cout << "Traitement C" << std::endl; } }; // Contexte est la classe visible par le client. // Elle fait le lien entre les demandes du client et l’algorithme (ou les algorithmes) à utiliser. class Contexte final { private: std::unique_ptr<IStrategie> strategie; public: Contexte(std::unique_ptr<IStrategie> new_strategie) : strategie(std::move(new_strategie)) {} void execute() { strategie->execute(); } void setAlgorithme(std::unique_ptr<IStrategie> new_strategie) { strategie = std::move(new_strategie); } }; int main() { Contexte contexte(std::make_unique<AlgorithmeA>()); contexte.execute(); // Le contexte va effectuer le traitement A contexte.setAlgorithme(std::make_unique<AlgorithmeB>()); contexte.execute(); // Le contexte va effectuer le traitement B contexte.setAlgorithme(std::make_unique<AlgorithmeC>()); contexte.execute(); // Le contexte va effectuer le traitement C return 0; }

Ver clase abstracta y función virtual pura .

Ejemplo en C #

Ideas similares conducen a la realización mediante interfaces.

El objeto que debe tener una estrategia adaptable en tiempo de ejecución implementa IStrategie: la misma interfaz que otros objetos. El objeto principal delega la ejecución de la tarea a otro objeto miembro que implementa IStrategie.

El objeto miembro que se declara en la clase como una interfaz, su implementación no importa, por lo tanto, podemos cambiar la estrategia en tiempo de ejecución. Esta forma de hacer las cosas es similar al Principio de inyección de dependencia .

using System; /// <summary> La manière dont le grand général guidera ses troupes</summary> interface IStrategie { void MettreEnOeuvre(); } /// <summary> Ce grand homme qui fera bientôt des choix décisifs </summary> class SeigneurDeLaGuerre { /// <summary> une stratégie générique </summary> IStrategie _strategie; /// <summary> comment changer de stratégie </summary> public IStrategie Strategie { set { _strategie = value; } } /// <summary> délégation de la tâche </summary> public void PrendreLaVille() { _strategie.MettreEnOeuvre(); } } class DéfoncerLePontLevisDeFace : IStrategie { public void MettreEnOeuvre() { Console.WriteLine("Prendre la ville de face en défonçant le pont-levis."); } } class PasserParLaFaceNord : IStrategie { public void MettreEnOeuvre() { Console.WriteLine("Prendre la ville en escaladant la muraille nord."); } } class AttendreQueLaVilleSeRende : IStrategie { public void MettreEnOeuvre() { Console.WriteLine("Attendre qu'il n'y ait plus rien à manger en ville " + "et que tout le monde meure de faim."); } } class SeMarierAvecLaCousineDuDuc : IStrategie { public void MettreEnOeuvre() { Console.WriteLine("Organiser un mariage avec la cousine du Duc " + "alors qu'elle rejoint la ville de retour des Baléares " + "et inviter toute la ville à une grande fête."); } } /// <summary> Différentes situations </summary> enum Météo { IlFaitBeau, IlYADuBrouillard, IlFaitTropChaudPourTravailler, IlPleut } class Program { static void Main() { // notre acteur var kevin = new SeigneurDeLaGuerre(); // les aléas du système var météo = (Météo)(new Random().Next(0, 4)); // une liaison tardive switch (météo) { case Météo.IlFaitBeau: kevin.Strategie = new DéfoncerLePontLevisDeFace(); break; case Météo.IlYADuBrouillard: kevin.Strategie = new PasserParLaFaceNord(); break; case Météo.IlFaitTropChaudPourTravailler: kevin.Strategie = new AttendreQueLaVilleSeRende(); break; case Météo.IlPleut: kevin.Strategie = new SeMarierAvecLaCousineDuDuc(); break; default: throw new Exception("Nan finalement seigneur de la guerre c'est " + "pas cool comme job : vous décidez d'aller cueillir " + "des champignons dans le Périgord."); } // une exécution aux petits oignons kevin.PrendreLaVille(); } }

Ejemplo en Delphi

fuente: Delphi GOF Design Patterns (CodePlex)

unit strategy; interface type TContext = class; IStrategy = interface ['{7F63C143-98D0-4B8C-A02B-894D145BB745}'] function Move(c: TContext): integer; end; TStrategy1 = class(TInterfacedObject, IStrategy) public function Move(c: TContext): integer; end; TStrategy2 = class(TInterfacedObject, IStrategy) public function Move(c: TContext): integer; end; TContext = class private FStrategy: IStrategy; FCounter: integer; public constructor Create(counter: integer); function Algorithm: integer; procedure SetStrategy(s: IStrategy); property counter: integer read FCounter write FCounter; end; implementation { TStrategy1 } function TStrategy1.Move(c: TContext): integer; begin c.Counter := c.Counter + 1; Result := c.Counter; end; { TStrategy2 } function TStrategy2.Move(c: TContext): integer; begin c.Counter := c.Counter - 1; Result := c.Counter; end; { TContext } function TContext.Algorithm: integer; begin Result := FStrategy.Move(Self) end; constructor TContext.Create(counter: integer); begin inherited; FCounter := counter; FStrategy := TStrategy1.Create; end; procedure TContext.SetStrategy(s: IStrategy); begin FStrategy := s; end; end. { fichier projet } program Behavioral.strategy.Pattern; {$APPTYPE CONSOLE} uses SysUtils, strategy in 'strategy.pas'; var context: TContext; i: integer; begin try context := TContext.Create(12); context.SetStrategy(TStrategy1.Create); try for i := 0 to 30 do begin if i = 15 then begin WriteLn(#10 + '|| '); context.SetStrategy(TStrategy2.Create); end; Write(IntToStr(context.Algorithm) + ' '); end; ReadLn; finally context.Free; end; except on E:Exception do Writeln(E.Classname, ': ', E.Message); end; end.

Ejemplo de Smalltalk

Aquí, el comportamiento de un automóvil se puede cambiar según la estrategia de conducción:

ConduiteSport>>avance Transcript show: 'à fond à fond'; cr ConduiteTranquille>>avance Transcript show: 'on roule doucement'; cr Voiture>>modeSport strategieConduite := ConduiteSport new Voiture>>modeTranquille strategieConduite := ConduiteTranquille new Voiture>>avance strategieConduite avance

Por tanto, podemos escribir:

maVoiture := Voiture new. maVoiture modeSport. maVoiture avance. "Affiche 'à fond à fond'" maVoiture modeTranquille. maVoiture avance. "Affiche 'on roule doucement'"

Ejemplo de Java

Sabemos que volar () y graznar () son las partes de la clase Pato que varían de un pato a otro.

Para separar estos comportamientos de la clase Duck, extraemos estos dos métodos de la clase y creamos un nuevo conjunto de clases para representar cada comportamiento.


Duck utiliza atributos del tipo de interfaz BehaviorVol y BehaviorCancan (el "ruido" del pato).

Son estas interfaces las que encapsulan el código de los métodos performVol () y performCancan () .

Son estos métodos los que queremos encapsular, de acuerdo con el patrón de diseño de la estrategia, para hacerlos intercambiables.

A pesar de nuestro deseo de hacer que estos métodos sean intercambiables:

  • todavía hay otros métodos que queremos mantener en común: aquí el método swim () .
  • y otros métodos que queremos específicos para la implementación elegida: aquí el método display ().
public abstract class Canard { ComportementVol comportementVol; ComportementCancan comportementCancan; public Canard() { } public abstract void afficher(); public void effectuerVol() { comportementVol.voler(); } public void effectuerCancan() { comportementCancan.cancaner(); } public void nager() { System.out.println("Tous les canards flottent, même les leurres!"); } public void setComportementVol(ComportementVol comportementVol) { this.comportementVol = comportementVol; } public void setComportementCancan(ComportementCancan comportementCancan) { this.comportementCancan = comportementCancan; } }

Ahora, vamos a especializar esta clase de Duck implementando dos nuevas clases heredadas de Duck . La clase Colvert:

public class Colvert extends Canard { public Colvert() { comportementVol = new VolerAvecDesAiles(); comportementCancan = new Cancan(); } public void afficher() { System.out.println("Je suis un vrai colvert"); } }

Y la clase PrototypeCanard  :

public class PrototypeCanard extends Canard { public PrototypeCanard() { comportementVol = new NePasVoler(); comportementCancan = new Cancan(); } public void afficher() { System.out.println("Je suis un prototype de canard"); } }

El atributo Behavior Flight es un objeto cuya clase implementa la interfaz Behavior Flight .

public interface ComportementVol { public void voler(); }

El ' atributo QuackBehavior es un objeto cuya clase implementa la interfaz QuackBehavior.

public interface ComportementCancan { public void cancaner() ; }

Es posible especificar tantos FlyBehavior y QuackBehavior de los necesarios, simplemente creando una nueva clase que implemente ambas interfaces. Entonces depende de estas clases implementar el fly () ...

public class VolerAvecDesAiles implements ComportementVol { public void voler() { System.out.println("Je vole !!"); } } public class NePasVoler implements ComportementVol { public void voler() { System.out.println("Je ne sais pas voler"); } }

... y quack () para especificar el comportamiento deseado.

public class Cancan implements ComportementCancan { public void cancaner() { System.out.println("Cancan"); } }


Notas y referencias

  1. Freeman, Eric, 1965- , Freeman, Elisabeth. , Sierra, Kathy. y Bates, Bert. ( traducido  del inglés), Patrones de diseño, cabeza primero , Beijing / Cambridge / Paris, etc., O'Reilly,2005, 637  p. ( ISBN  2-84177-350-7 y 978-2-84177-350-3 , OCLC  181354957 , lea en línea )