Patró estratègia
<templatestyles src="Metacaixa enllaç de desambiguació/styles.css" />
| 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 a 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 mes 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
Exemples de Codi
C++
<source lang="cpp">
- 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;
} </source>
Java
<source lang="java">
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'estrategia A * Estem utilitzant el comportament de l'estrategia B * Estem utilitzant el comportament de l'estrategia 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'estrategia A"); } }
public class StrategyB implements Strategy{ @Override public void Behaviour() { System.out.println("Estem utilitzant el comportament de l'estrategia B"); } }
</source>
Python
Pythonja el té implementat i no cal programar-lo explícitament. Aquí tenim un exemple amb una GUI:
<source lang="python"> 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 estrategies
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" </source>
C# 3.0
In C# 3.0, with lambda expressions we can do something similar to the Python example above.
<source lang="csharp"> 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);
}
}
} </source>
C#
<source lang="csharp">
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();
}
}
}
</source>
ActionScript 3
<source lang="actionscript">
//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();
}
}
}
</source>
PHP
<source lang="php"> <?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; ?>
</source>
Perl
Perl ja el té implementat i no cal programar-lo explícitament:
<source lang="perl"> sort { lc($a) cmp lc($b) } @items </source>
El patró d'estratègia pot estar implementat formalment amb Moose:
<source lang="perl"> 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; </source>
Enllaços externs
- The Strategy Pattern from the Net Objectives Repository (anglès)
- Article sobre el patró d'estratègia per Java (anglès)
- El patró d'estratègia en UML i en LePUS3 (una notació de modelatge formal) (anglès)
- Data & object factory (anglès)
- Refactoring: Replace Type Code with State/Strategy (anglès)
- Jt Patró de J2EE orientat a Framework (anglès)
- Patró d'Estratègia amb un twist! (anglès)
| |||||||||||