Zmodyfikowany wzorzec projektowania strategii

11

Niedawno zacząłem szukać Wzorów projektowych i jedna rzecz, którą koduję, idealnie pasuje do wzorca Strategii, z wyjątkiem jednej małej różnicy.

Zasadniczo niektóre (ale nie wszystkie) z moich algorytmów wymagają przekazania dodatkowego parametru lub dwóch.

Więc albo będę musiał

  • przekazuję im dodatkowy parametr, gdy wywołuję ich metodę obliczania

lub

  • przechowuj je jako zmienne w klasie ConcreteAl Algorytm i być w stanie je zaktualizować, zanim wywołam algorytm.

Czy istnieje wzorzec projektowy dla tej potrzeby / Jak mogę to zrealizować, pozostając przy schemacie strategicznym?

Rozważałem przekazanie obiektu klienta do wszystkich algorytmów i zapisanie tam zmiennych, a następnie użycie go tylko wtedy, gdy dany algorytm tego potrzebuje. Myślę jednak, że jest to zarówno niewygodne, jak i pokonuje sens wzorca strategii.

Żeby było jasne, wdrażam w Javie, więc nie mam luksusu opcjonalnych parametrów (które by to ładnie rozwiązały).

Megan Walker
źródło
Parametry opcjonalne, takie jak w C ++, nic nie rozwiążą, ponieważ są tylko skrótem do definiowania wielu przeciążonych metod.
maaartinus
Starałem się unikać przechowywania dodatkowych parametrów w miejscu, gdzie musiałem je zmienić przed użyciem. W ten sposób możesz ustawić ConcreteAlgorytm jako stanowy, aby nie można go było łatwo przekazać do innej metody lub wątków. Co więcej, zbyt łatwo jest zapomnieć o ustawieniu parametrów.
maaartinus

Odpowiedzi:

5

Samuelu, czy można zawrzeć parametr, który każda ze strategii uwzględnia w jednej wspólnej klasie, a następnie rozszerzyć tę wspólną klasę Parameter, aby dodać więcej zachowań, których niektóre twoje strategie potrzebują?

Na przykład

StrategyParameter //Base strategy parameter that most of the strategies need
        ^
        |
        |
SpecialStrategyParameter // will be used for strategies that need more parameter

Następnie zdefiniuj hierarchię strategii:

Interface MyStrategy {
   void myStrategyMethod(StrategyParameter parameter);
}

class MyNormalStrategy extends MyStrategy {
   void myStrategyMethod(StrategyParameter parameter) {
       //implement the logic here
   }
}

nazwij powyższą strategię jako: myNormalStrategyInstance.myStrategyMethod(strategyParameter);

class MySpecializedStrategy extends MyStrategy {
   void myStrategyMethod(StrategyParameter parameter) {
       //implement the logic here
   }
}

wywołaj powyższą strategię, przekazując SpecialStrategyParameterinstancję zamiast:mySpecializedStrategy.myStrategyMethod(specialStrategyParameter);

Zaktualizuj, jeśli coś nie jest jasne. Z przyjemnością wyjaśnimy / wyjaśnimy.

szczyt
źródło
2
-1 wymaga obniżenia, przerywa enkapsulację projektu. Chociaż jest to poprawa projektu w pytaniu, istnieją lepsze sposoby na skórowanie tego kota.
wysokość
@tallseth Też widzę przygnębiony. Ale nie widzę lepszych sposobów. Czy możesz wskazać lepsze rozwiązanie? Artykuł czy coś?
Narek,
Aktualnie tak. @ Jordão ma odpowiedź, którą wolałbym, w oparciu o szczegóły podane w pytaniu. Ta odpowiedź ma wpływ na mocne strony strategii. Gdybyśmy zastosowali podejście przedstawione w tej odpowiedzi, chciałbym mieć StrategyParameterwszystkie możliwe parametry, tak jak DTO. Niektóre wdrożenia strategii mogą je zignorować. W niektórych przypadkach jest to najlepsze podejście. Kontekst jest królem dla tego rodzaju problemów.
wysokość
4

Musisz wyjaśnić swoją strategię .

Wszystko zależy od tego, jak korzystasz z algorytmów. Aby klasa klienta mogła używać zamiennie różnych implementacji strategii, wszystkie muszą mieć wspólną abstrakcję . Jeśli nie są zgodne z tym samym interfejsem, być może potrzebujesz różnych abstrakcji .

Wcześniej stosowałem konfigurowalne strategie, w których parametryzujesz konkretne klasy konstrukcyjne:

interface Strategy {
  int calculate();
}

class ConcreteStrategyThatNeedsAParameter implements Strategy {
  private final int param;
  public ConcreteStrategyThatNeedsAParameter(int param) {
    this.param = param;
  }
  public int calculate() { 
    // uses param...
  }
}

Teraz ktoś nadal musi utworzyć instancję tej klasy i przekazać ją klientowi. Ale twój klient musi tylko wiedzieć o Strategyinterfejsie.

Działa również, jeśli twoja metoda strategii przyjmuje parametry, ale wtedy twój klient wie o tych parametrach i przekazuje je do wszystkich implementacji, z którymi współpracuje.

Jordão
źródło
Klient ma kontekst, w którym podaje parametr.
andyczerwonka 10.10.16
1

Tak długo, jak podpis jest wyraźnie zdefiniowany w interfejsie, nadal jest zgodny ze wzorem strategii.

Wzory jak napisane są absolutnie najprostszą formą, która nadal wykazuje oczekiwane zachowanie, więc możesz je upiększyć, o ile zachowasz pierwotne zamiary. To oczywiście zakłada, że ​​chcesz podążać za tym wzorem. Nie ma sensu używać wzoru, jeśli nie pasuje, lub po prostu dlatego, że tam jest, ale w twoim przypadku myślę, że nic ci nie jest.

Ian
źródło
0

rozszerzając powyższą odpowiedź dostarczoną przez peakit - możesz użyć abstrakcji. Używam tutaj kodu peakit -

Interfejs MyStrategy { abstract void myStrategyMethod (parametr StrategyParameter); }

klasa MyNormalStrategy rozszerza MyStrategy {publiczne zastąpienie void myStrategyMethod (parametr StrategyParameter) {// implementuj logikę tutaj}}

klasa MySpecializedStrategy rozszerza MyStrategy {publiczne zastąpienie void myStrategyMethod (parametr StrategyParameter, ExtraStrategyParameter extraParameter) {// implementuj logikę tutaj} }

Jeśli dobrze rozumiem twoje pytanie, chciałeś przekazać dodatkowy parametr do niektórych algorytmów, prawda? Daj mi znać, jeśli tego właśnie szukasz?


źródło
0

Jeśli spojrzysz na książkę wzorców projektowych, nie jest błędem samo w sobie, że istnieje SimpleStrategy, która używa mniej lub wcale nie przekazanych parametrów, lub że parametry są jednym uniwersalnym / najmniejszym wspólnym mnożnikiem. Wybór projektu polega na tym, czy to cię boli pod względem dodatkowego przetwarzania, które ostatecznie nie jest używane.

pjv
źródło