Стратегия (шаблон)

UML диаграма на шаблона Стратегия

Стратегия (на английски: Strategy) е поведенчески шаблон за дизайн, който се използва в обектно-ориентираното програмиране.

Шаблонът Стратегия се използва в случаите, когато е необходим динамичен избор на алгоритъм. Дефинира се семейство от различни, но взаемозаменяеми алгоритми, всеки един от тях капсулиран като отделен обект. Стратегията позволява превключването от един към друг алгоритъм да става независимо от клиентския код който ги използва.

Поведение

Съгласно шаблона Стратегия, поведението на конкретен клас не бива да се наследява. Вместо това трябва да се използва интерфейс, който дефинира външния вид на класовете които ще го наследяват и ще бъдат имплементирани.

Нека разгледаме като примерен класа Автомобил (Car). Възможни поведения за този клас са например Спиране (Brake) и Ускоряване (Accelerate). Тъй като тези две действия се изпълняват различно в зависимост от модела, е нужно различните поведения да се имплементират в подкласове. Ако се използва директно наследянаве на класа Автомобил (Car), това ще създаде спънка за организацията на класовете, тъй като за всеки нов модел ще трябва да се наследи и иплементира този клас наново. Така голяма част от кода ще се повтаря. С увеличаване на набора от модели, подкласовете също ще се увеличават. Също така за да се определи разликата в поведението на различните модели е нужно обстойно изследване на кода.

Шаблонът Стратегия използва композиция. Дадено поведение се дефинира като отделен интерфейс, който се имплементира от различни класове. По този начин се прави разделяне на поведението и класа, който използва това поведение. Поведението може да бъде променяно без знанието на класовете, които го използват. Също така даден клас може да превключва между различните поведения без значителни промени в кода. Поведенията могат да се сменят, както по време на дизайн, така и по време на изпълнение на програмата.

Примери

#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;
}
using System;

namespace Wikipedia.Patterns.Strategy
{
 // MainApp test application
 class MainApp
 {
 static void Main()
 {
 Context anObject;

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

 anObject.UpdateContext(new ConcreteStrategyB());
 anObject.Execute();

 anObject.UpdateContext(new ConcreteStrategyC());
 anObject.Execute();

 }
 }

 // The classes that implement a concrete strategy must implement this Execute function.
 // The context class uses this to call the 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()");
 }
 }

 // Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
 class Context
 {
 IStrategy strategy;

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

 public void UpdateContext(IStrategy strategy)
 {
 this.strategy = strategy;
 }

 public void Execute()
 {
 strategy.Execute();
 }
 }
}
//StrategyExample test application

class StrategyExample {

 public static void main(String[] args) {

 Context context;

 // Three contexts following different strategies
 context = new Context(new ConcreteStrategyAdd());
 int resultA = context.execute(3,4);

 context = new Context(new ConcreteStrategySubtract());
 int resultB = context.execute(3,4);

 context = new Context(new ConcreteStrategyMultiply());
 int resultC = context.execute(3,4);

 }

}

// The classes that implement a concrete strategy should implement this

// The context class uses this to call the concrete strategy
interface Strategy {

 int execute(int a, int b);

}

// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {

 public int execute(int a, int b) {
 System.out.println("Called ConcreteStrategyA's execute()");
 return (a + b); // Do an addition with a and b
 }

}

class ConcreteStrategySubtract implements Strategy {

 public int execute(int a, int b) {
 System.out.println("Called ConcreteStrategyB's execute()");
 return (a  b); // Do a subtraction with a and b
 }

}

class ConcreteStrategyMultiply implements Strategy {

 public int execute(int a, int b) {
 System.out.println("Called ConcreteStrategyC's execute()");
 return a * b; // Do a multiply with a and b
 }

}

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {

 Strategy strategy;

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

 public int execute(int a, int b) {
 return this.strategy.execute(a, b);
 }

}

Тъй като езикът C не е обектно ориентиран, в него могат да се използват структури, за дефиниция на класове. Шаблонът Стратегия може да се реализира чрез използването на указатели към функции.

#include <stdio.h>

void print_sum(int n, int *array) {
 int total = 0;
 for (int i=0; i<n; i++) total += array[i];
 printf("%d", total);
}

void print_array(int n, int *array) {
 for (int i=0; i<n; i++) printf("%d ", array[i]);
}

int main(void) {
 typedef struct {
 void (*submit_func)(int n, int *array);
 char *label;
 } Button;

 // Create two instances with different strategies
 Button button1 = {print_sum, "Add 'em"};
 Button button2 = {print_array, "List 'em"};

 int n = 10, numbers[n];
 for (int i=0; i<n; i++) numbers[i] = i;

 button1.submit_func(n, numbers);
 button2.submit_func(n, numbers);

 return 0;
}
<?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 {
 private $strategy;

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

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

new StrategyExample();
?>

Strategi Solo vs Squad di Free Fire: Cara Menang Mudah!