Strategia
Strategia
Wzorzec Strategia (ang. Strategy) jest wzorcem projektowym, który umożliwia definiowanie rodzin algorytmów, umieszczanie ich w oddzielnych klasach, a następnie używanie ich wymiennie. Dzięki temu możemy dynamicznie zmieniać zachowanie obiektu w trakcie działania programu, bez konieczności modyfikowania jego kodu.
Kluczowe aspekty wzorca Strategia:
- Definiowanie rodziny algorytmów: Każdy algorytm jest reprezentowany przez osobną klasę, która implementuje wspólny interfejs.
- Wymienność algorytmów: Algorytmy mogą być zamieniane w trakcie działania programu.
- Zasada Open/Closed: Możemy łatwo dodawać nowe algorytmy, bez modyfikacji istniejących klas.
Kiedy używać wzorca Strategia?
- Gdy mamy różne warianty algorytmów, które mogą być dynamicznie zmieniane.
- Gdy chcemy oddzielić logikę algorytmu od kodu klienta, aby móc dodawać nowe algorytmy bez zmian w istniejącym kodzie.
- Gdy potrzebujemy elastyczności w wyborze algorytmów podczas działania programu.
Przykład bez wzorca Strategia
Załóżmy, że mamy system, który wykonuje operacje matematyczne. Zamiast strategii, mamy jeden duży warunkowy blok, który określa, którą operację wykonać:
class Calculator {
public int calculate(String operation, int a, int b) {
switch (operation) {
case "add":
return a + b;
case "subtract":
return a - b;
case "multiply":
return a * b;
case "divide":
return b != 0 ? a / b : 0; // Zakładamy, że nie dzielimy przez 0
default:
throw new IllegalArgumentException("Nieznana operacja");
}
}
}
// Klient
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int result1 = calculator.calculate("add", 10, 5);
System.out.println("Dodawanie: " + result1);
int result2 = calculator.calculate("subtract", 10, 5);
System.out.println("Odejmowanie: " + result2);
}
}
Problemy z takim podejściem:
- Brak rozszerzalności: Każdorazowo, gdy chcemy dodać nową operację, musimy modyfikować kod w klasie
Calculator
. - Złożoność warunków: Jeśli operacji będzie więcej, kod stanie się bardziej skomplikowany i trudniejszy do utrzymania.
Przykład z użyciem wzorca Strategia
Teraz zmienimy podejście na takie, które stosuje wzorzec Strategia, co pozwoli nam łatwo dodawać nowe algorytmy i dynamicznie je zmieniać.
1 Interfejs strategii
// Interfejs Strategii
interface OperationStrategy {
int doOperation(int a, int b);
}
2. Implementacje strategii (algorytmy)
// Konkretna strategia - dodawanie
class AdditionStrategy implements OperationStrategy {
@Override
public int doOperation(int a, int b) {
return a + b;
}
}
// Konkretna strategia - odejmowanie
class SubtractionStrategy implements OperationStrategy {
@Override
public int doOperation(int a, int b) {
return a - b;
}
}
3. Klasa kontekstowa
class Calculator {
// Wykonanie operacji na podstawie dostarczonej strategii
public int executeStrategy(OperationStrategy strategy, int a, int b) {
return strategy.doOperation(a, b);
}
}
4. Klient (główna część programu)
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
// Dodawanie
int result1 = calculator.executeStrategy(new AdditionStrategy(), 10, 5);
System.out.println("Dodawanie: " + result1);
// Odejmowanie
int result2 = calculator.executeStrategy(new SubtractionStrategy(), 10, 5);
System.out.println("Odejmowanie: " + result2);
}
}
Zalety wzorca Strategia:
- Rozszerzalność: Możemy łatwo dodawać nowe algorytmy (np. nowe operacje matematyczne) poprzez dodanie nowych klas implementujących interfejs
OperationStrategy
, bez modyfikacji istniejących klas. - Elastyczność: Możemy dynamicznie zmieniać strategię w czasie działania programu.
- Rozdzielenie odpowiedzialności: Każda operacja (algorytm) jest wydzielona w osobnej klasie, co upraszcza kod i sprawia, że jest bardziej przejrzysty.