(ang. Strategy)
To wzorzec behawioralny stosowany, gdy chcemy wykonać jakieś zadanie wybierając spośród różnych metod tą najbardziej odpowiednią w danej chwili.
Wzorzec ten daje również możliwość oddzielania logiki biznesowej od szczegółów implementacji poszczególnych algorytmów.
</> Lepszy przykład niż wykład
✘ Błędny kod
Nasza klasa Main to gala mma.
W paczce zawodnik_mma utworzymy klasę ZawodnikMma z polem prywatnym typu String, konstruktorem, setterem i getterem oraz metodą walcz().
public class MmaFighter {
private String name;
public MmaFighter(String name) {
this.name = name;
}
public void fight() {
System.out.println("Atakuję kombinacją ciosów prostych.");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Chcemy, aby nasz zawodnik walczył. Zadanie to zawodnik może wykonać na wiele sposobów, więc trener będzie wybierał najbardziej odpowiednią metodę walki w danym momencie.
Pierwsza akcja jaką trener nakazuje wykonać zawodnikowi to kombinacja cisów prostych. Więc tworzymy naszego zawodnika (nadajemy mu imię) i wydajemy polecenie, aby atakował.
Ale problem pojawia się, gdy trener zdecyduje, aby zawodnik wykonał inną akcję np. obalenie przeciwnika.
public class Main {
public static void main(String[] args) {
MmaFighter mmaFighter = new MmaFighter("Mamed");
// akcja - atak ciosami prostymi
mmaFighter.fight();
// akacja - obalenie
mmaFighter.fight();
}
}
Na wyjściu otrzymujemy zgodne z naszymi przewidywaniami, ale niezgodne z oczekiwaniami trenera akcje.
Atakuję kombinacją ciosów prostych.
Atakuję kombinacją ciosów prostych.
W klasie MmaFighter moglibyśmy tworzyć osobne metody do wykonania każdej akcji, ale my nie chcemy modyfikować naszej klasy MmaFighter za każdym razem, gdy trzeba będzie wykonać nową akcję.
Oprócz tego nasz klient Main (czyi nasza gala mma) nie musi koniecznie wiedzieć jakie metody będą się znajdowały w klasie MmaFighter, ona wymaga tylko, by zawodnik walczył. Powinniśmy też móc w sposób dynamiczny zmieniać naszą strategię.
✔ Prawidłowy kod

Interfejs Action
W paczce mma_fighter tworzymy paczkę action a w niej interfejs Action z deklaracją jednej metody doAction().

public interface Action {
void doAction();
}
Strategie – Klasy implementujące interfejs
Implementujemy metodę doAciton() odpowiednio do akcji, które zawodnik ma wykonać.
public class JabAndCrossAction implements Action {
@Override
public void doAction() {
System.out.println("Atakuję kombinacją ciosów prostych.");
}
}
public class TakedownAction implements Action {
@Override
public void doAction() {
System.out.println("Obalam przeciwnika.");
}
}
Kontekst – Klasa MmaFighter
- mamy pole prywatne name
- dodajemy pole prywatne naszego interfejsu – nie będzie tu przekazywany interfejs, tylko konkretne klasy, które go reprezentują dzięki polimorfizmowi
- mamy konstruktor klasy
- mamy getter i setter dla pola name
- tworzymy getter i setter dla pola interfejsu
- modyfikujemy metodę fight()
public class MmaFighter {
private String name;
private Action action;
public MmaFighter(String name) {
this.name = name;
}
public void fight() {
this.action.doAction();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Action getAction() {
return action;
}
public void setAction(Action action) {
this.action = action;
}
}
Klasa Main
Tworzymy instancję zawodnika.
Ustawiamy strategię.
Każemy zawodnikowi walczyć.
public class Main {
public static void main(String[] args) {
MmaFighter mmaFighter = new MmaFighter("Mamed");
// akcja - atak ciosami prostymi
mmaFighter.setAction(new JabAndCrossAction());
mmaFighter.fight();
// akcja - obalenie
mmaFighter.setAction(new TakedownAction());
mmaFighter.fight();
}
}
Na wyjściu otrzymujemy prawidłowe akcje zawodnika.
Atakuję kombinacją ciosów prostych.
Obalam przeciwnika.
Strategia to popularny wzorzec projektowy, pozwalający na wydzielenie pewnego zachowania z klasy (u nas wydzielenie zachowania z klasy MmaFighter).
To z kolei pozwala na rozdzielenie wywoływania konkretnego zachowania na klasie kontekstowej – czyli nasza klasa główna Main nie musi znać wszystkich zachowań, wszystkich konkretnych metod w klasie MmaFighter, wystarczy że będzie ustawiała konkretne strategie i wywoływała metodę fight(), aby uzyskać żądany efekt.
Bardzo istotne zalety wzorca strategia:
- strategie można ustawiać podczas działania aplikacji, to niezwykle ważne, ponieważ strategie można wymieniać dosłownie w każdej chwili, np.:
– w sklepie internetowym zależnie od dnia tygodnia czy godziny aplikacja może ustawić odpowiedni rabat dla klienta
– zależnie od tego jakie dane użytkownik wpisze w formularzu na stronie internetowej, po wpłynięciu tych danych do naszej aplikacji zostanie wywoływany odpowiedni wariant działania aplikacji - jest to elastyczny wzorzec o wielu zastosowaniach.
