Vés al contingut

Patró estratègia

De la Viquipèdia, l'enciclopèdia lliure
Aquest article tracta sobre informàtica. Vegeu-ne altres significats a «Estratègia».

Estratègia (strategy en anglès) és un patró de disseny per desenvolupar programari.

El patró Strategy permet mantenir un conjunt d'algorismes amb l'objectiu que el client pugui escollir el que més li convingui i intercanviar-lo segons les seves necessitats. Els diferents algorismes s'encapçalen i el client treballa contra un objecte Context. Com hem dit, el client pot escollir l'algorisme que prefereix entre tots els disponibles, o pot ser el mateix objecte Context el que esculli el més adequat per a cada situació. Qualsevol programa que ofereixi un servei o funció determinada, que pugui ser realitzada de diferents formes, és candidat a usar el patró Strategy. Pot haver-hi diferents estratègies i qualsevol d'elles pot ser intercanviada per una altra en qualsevol moment, inclús en temps d'execució.

Diagrama

[modifica]

Diagrama de classes del patró Strategy

Exemples de Codi

[modifica]
#include <iostream>

using namespace std;

class StrategyInterface
{
 public:
 virtual void execute() = 0;
};

class ConcreteStrategyA: public StrategyInterface
{
 public:
 virtual void execute()
 {
 cout << "Called ConcreteStrategyA execute method" << endl;
 }
};

class ConcreteStrategyB: public StrategyInterface
{
 public:
 virtual void execute()
 {
 cout << "Called ConcreteStrategyB execute method" << endl;
 }
};

class ConcreteStrategyC: public StrategyInterface
{
 public:
 virtual void execute()
 {
 cout << "Called ConcreteStrategyC execute method" << endl;
 }
};

class Context
{
 private:
 StrategyInterface *_strategy;

 public:
 Context(StrategyInterface *strategy):_strategy(strategy)
 {
 }

 void set_strategy(StrategyInterface *strategy)
 {
 _strategy = strategy;
 }

 void execute()
 {
 _strategy->execute();
 }
};

int main(int argc, char *argv[])
{
 ConcreteStrategyA concreteStrategyA;
 ConcreteStrategyB concreteStrategyB;
 ConcreteStrategyC concreteStrategyC;

 Context contextA(&concreteStrategyA);
 Context contextB(&concreteStrategyB);
 Context contextC(&concreteStrategyC);

 contextA.execute();
 contextB.execute();
 contextC.execute();

 contextA.set_strategy(&concreteStrategyB);
 contextA.execute();
 contextA.set_strategy(&concreteStrategyC);
 contextA.execute();

 return 0;
}
public class Main {	
	public static void main(String args[])
	{
		//Inicialment utilitzem l'estratègia A
		Strategy estrategia_inicial = new StrategyA();
		Context context = new Context(estrategia_inicial);
		context.some_method();
		
		//En un moment determinat decidim utilitzar l'estratègia B
		Strategy estrategia2 = new StrategyB();
		context.setStrategy(estrategia2);
		context.some_method();
		
		//Finalment tornem a utilitzar l'estratègia A
		context.setStrategy(estrategia_inicial);
		context.some_method();
		
		/** Output:
		 * Estem utilitzant el comportament de l'estratègia A
		 * Estem utilitzant el comportament de l'estratègia B
		 * Estem utilitzant el comportament de l'estratègia A
		 **/
	}
}


public class Context {
	Strategy c;

	public Context(Strategy c)
	{
		this.c = c;
	}

	public void setStrategy(Strategy c) {
		this.c = c;
	}
	
	//Mètode que utilitzarà una estratègia 'c'
	public void some_method()
	{
		c.Behaviour();
	}
}

public class StrategyA implements Strategy{
	@Override
	public void Behaviour() {
		System.out.println("Estem utilitzant el comportament de l'estratègia A");
	}
}

public class StrategyB implements Strategy{
	@Override
	public void Behaviour() {
		System.out.println("Estem utilitzant el comportament de l'estratègia B");
	}
}

Pythonja el té implementat i no cal programar-lo explícitament. Aquí tenim un exemple amb una GUI:

class Button:
 """A very basic button widget."""
 def __init__(self, submit_func, label):
 self.on_submit = submit_func # Set the strategy function directly
 self.label = label

# Creem dos objecte amb diferents estratègies
button1 = Button(sum, "Add 'em")
button2 = Button(lambda nums: " ".join(map(str, nums)), "Join 'em")

# Provem cada button
numbers = range(1, 10) # A list of numbers 1 through 9
print button1.on_submit(numbers) # displays "45"
print button2.on_submit(numbers) # displays "1 2 3 4 5 6 7 8 9"

In C# 3.0, with lambda expressions we can do something similar to the Python example above.

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
 static void Main(string[] args)
 {
 var button1 = new MyButton((x) => x.Sum().ToString(), "Add 'em");
 var button2 = new MyButton((x) => string.Join(" ", x.Select(y => y.ToString()).ToArray()), "Join 'em");

 var numbers = Enumerable.Range(1, 10);
 Console.WriteLine(button1.Submit(numbers));
 Console.WriteLine(button2.Submit(numbers));

 Console.ReadLine();
 }

 public class MyButton
 {
 private readonly Func<IEnumerable<int>, string> submitFunction;
 public string Label { get; private set; }

 public MyButton(Func<IEnumerable<int>, string> submitFunction, string label)
 {
 this.submitFunction = submitFunction;
 Label = label;
 }

 public string Submit(IEnumerable<int> data)
 {
 return submitFunction(data);
 }
 }
}
using System;

namespace Wikipedia.Patterns.Strategy
{
 // MainApp aplicació de prova
 class MainApp
 {
 static void Main()
 {
 Context context;

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

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

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

 }
 }

 // La classe que implementa un "concrete strategy" hauria d'implementar això
 // La classe "context" usa això per cridar a un "concrete strategy"
 interface IStrategy
 {
 void Execute();
 }

 // Implements the algorithm using the strategy interface
 class ConcreteStrategyA : IStrategy
 {
 public void Execute()
 {
 Console.WriteLine("Called ConcreteStrategyA.Execute()");
 }
 }

 class ConcreteStrategyB : IStrategy
 {
 public void Execute()
 {
 Console.WriteLine("Called ConcreteStrategyB.Execute()");
 }
 }

 class ConcreteStrategyC : IStrategy
 {
 public void Execute()
 {
 Console.WriteLine("Called ConcreteStrategyC.Execute()");
 }
 }

 // Configurat amb un objecte ConcreteStrategy object i manté una referencia a un Strategy object
 class Context
 {
 IStrategy strategy;

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

 public void Execute()
 {
 strategy.Execute();
 }
 }
}

ActionScript 3

[modifica]
//invoked from application.initialize
private function init() : void
{
 var context:Context;

 context = new Context(new ConcreteStrategyA());
 context.execute();

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

 context = new Context(new ConcreteStrategyC());
 context.execute();
}

package org.wikipedia.patterns.strategy
{
 public interface IStrategy
 {
	function execute() : void ;
 }
}

package org.wikipedia.patterns.strategy
{
 public final class ConcreteStrategyA implements IStrategy
 {
	public function execute():void
	{
	 trace("ConcreteStrategyA.execute(); invoked");
	}
 }
}

package org.wikipedia.patterns.strategy
{
 public final class ConcreteStrategyB implements IStrategy
 {
	public function execute():void
	{
	 trace("ConcreteStrategyB.execute(); invoked");
	}
 }
}

package org.wikipedia.patterns.strategy
{
 public final class ConcreteStrategyC implements IStrategy
 {
	public function execute():void
	{
	 trace("ConcreteStrategyC.execute(); invoked");
	}
 }
}

package org.wikipedia.patterns.strategy
{
 public class Context
 {
	private var strategy:IStrategy;
		
	public function Context(strategy:IStrategy)
	{
	 this.strategy = strategy;
	}
		
	public function execute() : void
	{ 
 strategy.execute();
	}
 }
}
<?php
class StrategyExample {
 public function __construct() {
 $context = new Context(new ConcreteStrategyA());
 $context->execute();

 $context = new Context(new ConcreteStrategyB());
 $context->execute();

 $context = new Context(new ConcreteStrategyC());
 $context->execute();
 }
}

interface IStrategy {
 public function execute();
}

class ConcreteStrategyA implements IStrategy {
 public function execute() {
 echo "Called ConcreteStrategyA execute method\n";
 }
}

class ConcreteStrategyB implements IStrategy {
 public function execute() {
 echo "Called ConcreteStrategyB execute method\n";
 }
}

class ConcreteStrategyC implements IStrategy {
 public function execute() {
 echo "Called ConcreteStrategyC execute method\n";
 }
}

class Context {
 var $strategy;

 public function __construct(IStrategy $strategy) {
 $this->strategy = $strategy;
 }

 public function execute() {
 $this->strategy->execute();
 }
}

new StrategyExample;
?>

Perl ja el té implementat i no cal programar-lo explícitament:

sort { lc($a) cmp lc($b) } @items

El patró d'estratègia pot estar implementat formalment amb Moose:

package Strategy;
use Moose::Role;
requires 'execute';


package FirstStrategy;
use Moose;
with 'Strategy';

sub execute {
 print "Called FirstStrategy->execute()\n";
}


package SecondStrategy;
use Moose;
with 'Strategy';

sub execute {
 print "Called SecondStrategy->execute()\n";
}


package ThirdStrategy;
use Moose;
with 'Strategy';

sub execute {
 print "Called ThirdStrategy->execute()\n";
}


package Context;
use Moose;

has 'strategy' => (
 is => 'rw',
 does => 'Strategy',
 handles => [ 'execute' ], # automatic delegation
);


package StrategyExample;
use Moose;

# Moose's constructor
sub BUILD {
 my $context;

 $context = Context->new(strategy => 'FirstStrategy');
 $context->execute;

 $context = Context->new(strategy => 'SecondStrategy');
 $context->execute;

 $context = Context->new(strategy => 'ThirdStrategy');
 $context->execute;
}


package main;

StrategyExample->new;

Enllaços externs

[modifica]
  • Refactoring: Replace Type Code with State/Strategy (anglès)