Przykład wzorca strategii ze świata rzeczywistego

95

Czytałem o zasadzie OCP i jak wykorzystać wzorzec strategii, aby to osiągnąć.

Miałem zamiar spróbować wyjaśnić to kilku osobom, ale jedynym przykładem, jaki przychodzi mi do głowy, jest użycie różnych klas walidacji w oparciu o status „zamówienia”.

Przeczytałem kilka artykułów online, ale zazwyczaj nie opisują one prawdziwego powodu, dla którego warto zastosować tę strategię, na przykład generowania raportów / rachunków / walidacji itp.

Czy są jakieś przykłady z prawdziwego świata, w których uważasz, że wzorzec strategii jest powszechny?

Ravindra babu
źródło

Odpowiedzi:

100

A co z tym:

Musisz zaszyfrować plik.

W przypadku małych plików można użyć strategii „w pamięci”, w której cały plik jest odczytywany i przechowywany w pamięci (powiedzmy dla plików <1 GB)

W przypadku dużych plików można zastosować inną strategię, w której części pliku są odczytywane w pamięci, a częściowo zaszyfrowane wyniki są przechowywane w plikach tmp.

Mogą to być dwie różne strategie dla tego samego zadania.

Kod klienta wyglądałby tak samo:

 File file = getFile();
 Cipher c = CipherFactory.getCipher( file.size() );
 c.performAction();



// implementations:
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             // load in byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             // swapt partial results to file.
         }

}

Plik

     Cipher c = CipherFactory.getCipher( file.size() );

Zwróci prawidłową instancję strategii dla szyfru.

Mam nadzieję, że to pomoże.

(Nie wiem nawet, czy Cipher to właściwe słowo: P)

OscarRyz
źródło
8
Czy twój przykład nie jest bardziej wzorcem fabrycznym? Myślę też, że na przykład nie zadziała w C #. Twoja metoda „getCipher ()” jest metodą statyczną, ale w C # nie można zdefiniować metody statycznej na interfejsie (myślę, że ani w Javie, ale nie jestem pewien).
FrenchData,
10
Idą razem. Fabryka tworzy strategię, ale strategia sama posiada algorytm do wykonania (w zasadzie) tej samej operacji. Strategię można również zmienić w czasie wykonywania. Co do metody fabrycznej masz rację, zmieniłem to.
OscarRyz
Aby dodać punkt Osacars, bez fabryki można to utworzyć bez fabryki Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder
Zgadzam się z @FrenchData. Będąc doskonałym przykładem, obecność CipherFactorymoże zmylić tych, którzy nie są zaznajomieni z wzorcem strategii.
user487772
1
Wzorzec fabryczny dotyczy kreacji, strategia dotyczy zachowania. Jest trochę inaczej, prawda?
nhoxbypass
62

Ponownie, stary post, ale nadal pojawia się w wyszukiwaniach, więc dodam jeszcze dwa przykłady (kod jest w C #). Absolutnie uwielbiam wzorzec Strategii, ponieważ oszczędził mi to wiele razy, kiedy kierownicy projektów mówią: „Chcemy, aby aplikacja działała„ X ”, ale„ X ”nie jest jeszcze jasne i może się zmienić w najbliższej przyszłości. " W tym filmie objaśniającym wzorzec strategii jako przykład wykorzystano StarCraft.

Rzeczy, które należą do tej kategorii:

  • Sortowanie: chcemy posortować te liczby, ale nie wiemy, czy będziemy używać BrickSort, BubbleSort czy innego sortowania

  • Walidacja: Musimy sprawdzić pozycje zgodnie z „Jakaś regułą”, ale nie jest jeszcze jasne, jaka będzie ta reguła i możemy wymyślić nowe.

  • Gry: Chcemy, aby gracz chodził lub biegał, gdy się porusza, ale być może w przyszłości powinien również umieć pływać, latać, teleportować się, kopać pod ziemią itp.

  • Przechowywanie informacji: chcemy, aby aplikacja przechowywała informacje w bazie danych, ale później może być konieczne zapisanie pliku lub nawiązanie połączenia internetowego

  • Wyprowadzanie: musimy wypisać X jako zwykły ciąg, ale później może to być CSV, XML, JSON itp.


Przykłady

Mam projekt, w którym użytkownicy mogą przypisywać produkty do osób w bazie danych. To przypisanie produktu do osoby ma status „Zatwierdzony” lub „Odrzucony”, który jest zależny od pewnych reguł biznesowych. Na przykład: jeśli użytkownik przypisuje produkt osobie w określonym wieku, jego status powinien zostać odrzucony; Jeśli różnica między dwoma polami w elemencie jest większa niż 50, jego status jest odrzucony itp.

Teraz, w momencie opracowywania, te reguły biznesowe nie są jeszcze całkowicie jasne, a nowe zasady mogą pojawić się w dowolnym momencie. Potęga wzorca stragety polega na tym, że utworzyłem RuleAgent, który otrzymuje listę IRules.

public interface IRule {
    bool IsApproved(Assignment assignment); 
 }

W momencie przypisania produktu do osoby, tworzę RuleAgent, podaję mu listę reguł (z których wszystkie implementują IRule) i proszę o walidację przypisania. Przejdzie przez wszystkie swoje zasady. Który, ponieważ wszystkie implementują ten sam interfejs, wszystkie mają IsApprovedmetodę i zwracają false, jeśli którykolwiek z nich zwróci false.

Teraz, na przykład, nagle pojawia się kierownik i mówi, że musimy również odrzucić wszystkie zadania dla stażystów lub wszystkie zadania dla osób pracujących w nadgodzinach ... Tworzysz nowe zajęcia w ten sposób:

public OvertimeRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Timesheet >= 40)
        {
            return false;
        }
        return true;
    }
}

public InternRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Title == "Intern")
        {
            return false;
        }
        return true;
    }
}

Widzisz, że nie musisz ciągle dodawać ani usuwać instrukcji if lub kodu, po prostu utwórz nową klasę reguł, która implementuje interfejs IRUle i wyłączaj je w razie potrzeby.


Kolejny świetny przykład: seria filmów Scotta Allena pod adresem http://www.asp.net/mvc/pluralsight, gdzie używa wzorca strategii w części aplikacji poświęconej testom jednostkowym

Tworzy witrynę internetową, która ma stronę wyświetlającą pozycje w oparciu o popularność. Jednak „Popularne” może oznaczać wiele rzeczy (większość wyświetleń, większość subskrybentów, data utworzenia, największa aktywność, najmniejsza liczba komentarzy itp.), A kierownictwo nie wie jeszcze dokładnie, jak zamówić, i może chcieć eksperymentować z różnymi zamówienia w późniejszym terminie. Tworzysz interfejs (IOrderAlgorithm lub coś w tym rodzaju) za pomocą metody zamówienia i pozwalasz obiektowi Orderer delegować porządkowanie na konkretną implementację interfejsu IOrderAlgorithm. Możesz utworzyć „CommentOrderer”, „ActivityOrderer”, itd. I po prostu wyłączaj je, gdy pojawią się nowe wymagania.

Céryl Wiltink
źródło
Wiem, że to trochę poza zakresem pytania, ale co dalej? Mamy to InternRuleteraz, ale w jaki sposób uruchamiamy OvertimeRule? W jaki sposób możemy zapewnić, że jakakolwiek logika nazywa się OvertimeRule.IsApprovedteraz, również wywołuje InternRule.IsApproved?
Spencer Ruport
14

Kluczowe uwagi:

  1. Strategia jest wzorcem projektowania behawioralnego. Służy do przełączania się między rodziną algorytmów.

  2. Ten wzorzec zawiera jeden abstrakcyjny interfejs strategii i wiele konkretnych implementacji strategii ( algorytmów ) tego interfejsu.

  3. Aplikacja korzysta tylko z interfejsu strategii . W zależności od niektórych parametrów konfiguracyjnych, konkretna strategia zostanie oznaczona jako interfejs .

Diagram UML z Wikipedii

wprowadź opis obrazu tutaj

Przykład prawdziwego słowa: linie lotnicze oferujące zniżki w niektórych miesiącach (lipiec-grudzień) . Możesz mieć jeden moduł Opłaty , który decyduje o opcjach cenowych w zależności od numeru miesiąca.

Spójrz na prosty przykład. Ten przykład można rozszerzyć na aplikacje do sprzedaży detalicznej online, które z łatwością zapewniają rabaty na artykuły w koszyku w specjalne dni / happy hours.

import java.util.*;

/* Interface for Strategy */
interface OfferStrategy {
    public String getName();
    public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0;
    }
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0.25;
    }
}
/* Context is optional. But if it is present, it acts as single point of contact
   for client. 

   Multiple uses of Context
   1. It can populate data to execute an operation of strategy
   2. It can take independent decision on Strategy creation. 
   3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
   4. Code re-factoring will become easy
*/
class StrategyContext {
    double price; // price for some item or air ticket etc.
    Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
    StrategyContext(double price){
        this.price= price;
        strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
        strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());        
    }
    public void applyStrategy(OfferStrategy strategy){
        /* 
        Currently applyStrategy has simple implementation. You can use Context for populating some more information,
        which is required to call a particular operation            
        */
        System.out.println("Price before offer :"+price);
        double finalPrice = price - (price*strategy.getDiscountPercentage());
        System.out.println("Price after offer:"+finalPrice);
    }
    public OfferStrategy getStrategy(int monthNo){
        /*
            In absence of this Context method, client has to import relevant concrete Strategies everywhere.
            Context acts as single point of contact for the Client to get relevant Strategy
        */
        if ( monthNo < 6 )  {
            return strategyContext.get(NoDiscountStrategy.class.getName());
        }else{
            return strategyContext.get(QuarterDiscountStrategy.class.getName());
        }

    }
}
public class StrategyDemo{    
    public static void main(String args[]){
        StrategyContext context = new StrategyContext(100);
        System.out.println("Enter month number between 1 and 12");
        int month = Integer.parseInt(args[0]);
        System.out.println("Month ="+month);
        OfferStrategy strategy = context.getStrategy(month);
        context.applyStrategy(strategy);
    }

}

wynik:

Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0

Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0

Przydatne artykuły:

wzór strategii autorstwa dzone

wzorzec strategii poprzez pozyskiwanie źródeł

Ravindra babu
źródło
dzięki… ma sens… uzgodniono applyStrategy () to potwór, który potrafi osiągnąć dużo czarnej magii. Również niestatyczny sposób utrzymywania strategii w strategii
Kontekst
12

Przychodzi mi do głowy kilka dość prostych przykładów:

  • Sortowanie listy. Strategia to porównanie używane do decydowania, który z dwóch elementów na liście jest „Pierwszy”
  • Możesz mieć aplikację, w której sam algorytm sortowania (QuickSort, HeapSort itp.) Może być wybrany w czasie wykonywania
  • Dodatki, układy i filtry w Log4Net i Log4j
  • Layout Managers w zestawach narzędzi UI
  • Kompresja danych. Możesz mieć interfejs ICompressor, którego jedyna metoda wygląda mniej więcej tak:

    byte [] kompresja (bajt [] wejście);

    Twoje konkretne klasy kompresji mogą być takie jak RunLengthCompression, DeflateCompression itp.

Eric Pohl
źródło
9

Jednym z powszechnych zastosowań wzorca strategii jest definiowanie własnych strategii sortowania (w językach bez funkcji wyższego rzędu), np. Sortowanie listy ciągów według długości w Javie, przekazując anonimową klasę wewnętrzną (implementacja interfejsu strategii):

List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
  public int compare(String o1, String o2) {
    return o1.length() - o2.length();
  }
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);

W podobny sposób można zastosować strategie do zapytań natywnych z obiektowymi bazami danych, np. W db4o:

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});
Fabian Steeg
źródło
8

Mam aplikację, która codziennie synchronizuje swoją bazę użytkowników z naszym katalogiem korporacyjnym. Użytkownicy kwalifikują się lub nie kwalifikują się na podstawie ich statusu na Uniwersytecie. Każdego dnia program obsługi administracyjnej przechodzi i upewnia się, że te, które mają się kwalifikować, są obsługiwane w aplikacji, a te, które nie są, są usuwane (w rzeczywistości zgodnie z wdzięcznym algorytmem degradacji, ale to nie ma znaczenia). W sobotę robię dokładniejszą aktualizację, która synchronizuje niektóre właściwości każdego użytkownika, a także upewniam się, że mają one odpowiednie uprawnienia. Pod koniec miesiąca przetwarzam zwrot rachunków w oparciu o wykorzystanie w tym miesiącu.

Używam komponowalnego wzorca strategii, aby wykonać tę synchronizację. Program główny zasadniczo wybiera strategię główną w zależności od dnia tygodnia (synchronizacja tylko zmian / synchronizacja wszystkich) i czasu semestru w stosunku do kalendarza akademickiego. Jeśli cykl rozliczeniowy dobiega końca, komponuje go również ze strategią rozliczeniową. Następnie uruchamia wybraną strategię za pośrednictwem standardowego interfejsu.

Nie wiem, jak często się to zdarza, ale czułem, że idealnie pasuje do wzorca strategii.

tvanfosson
źródło
To jest bardzo dobry przykład. Ponadto jasno pokazuje różnicę między wzorcem poleceń a strategią w pigułce - intencję. „Główny program zasadniczo wybiera strategię główną w zależności od dnia tygodnia”
Utsav T
7

Wiem, że to stare pytanie, ale myślę, że mam inny ciekawy przykład, który niedawno wdrożyłem.

Jest to bardzo praktyczny przykład wzorca strategii stosowanego w systemie dostarczania dokumentów.

Miałem system dostarczania PDF, który otrzymał archiwum zawierające wiele dokumentów i kilka metadanych. Na podstawie metadanych zdecydował, gdzie umieścić dokument; powiedzmy, w zależności od danych, mogę zapisać dokument A, Blub Cmagazynowych systemów lub mieszankę tych trzech.

Różni klienci korzystali z tego systemu i mieli różne wymagania dotyczące wycofywania / obsługi błędów w przypadku błędów: jeden chciał, aby system dostarczania zatrzymał się przy pierwszym błędzie, zostawił wszystkie dokumenty już dostarczone w swoich magazynach, ale zatrzymał proces i nie dostarczał niczego innego ; inny chciał, żeby Bw przypadku błędów przy składowaniu został wycofany C, ale zostawił to, co zostało już dostarczone A. Łatwo sobie wyobrazić, że trzeci czy czwarty też będzie miał inne potrzeby.

Aby rozwiązać ten problem, utworzyłem podstawową klasę dostarczania, która zawiera logikę dostarczania oraz metody wycofywania danych ze wszystkich magazynów. Metody te nie są w rzeczywistości wywoływane bezpośrednio przez system dostarczania w przypadku błędów. Zamiast tego klasa używa Dependency Injection, aby otrzymać klasę „Rollback / Error Handling Strategy” (opartą na kliencie korzystającym z systemu), która jest wywoływana w przypadku błędów, która z kolei wywołuje metody wycofywania zmian, jeśli jest to odpowiednie dla tej strategii.

Klasa dostawy sama zgłasza, co się dzieje w klasie strategii (jakie dokumenty zostały dostarczone do jakich magazynów i jakie awarie się wydarzyły), a gdy wystąpi błąd, pyta strategię, czy kontynuować, czy nie. Jeśli strategia mówi „stop it”, klasa wywołuje metodę „cleanUp” strategii, która używa wcześniej zgłoszonych informacji, aby zdecydować, które metody wycofywania wywołać z klasy dostarczającej lub po prostu nic nie robić.

rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);

if (rollbackStrategy.mustAbort()) {
    rollbackStrategy.rollback(); // rollback whatever is needed based on reports
    return false;
}

Więc mam teraz dwie różne strategie: jedna to QuitterStrategy(która kończy pracę po pierwszym błędzie i nic nie czyści), a druga to MaximizeDeliveryToAStrategy(która stara się jak najbardziej nie przerywać procesu i nigdy nie przywracać zawartości dostarczonej do pamięci) A, ale wycofuje rzeczy z, Bjeśli dostawa się Cnie powiedzie).

Z mojego zrozumienia jest to jeden z przykładów wzorca strategii. Jeśli (tak, czytasz) myślisz, że się mylę, skomentuj poniżej i daj mi znać. Ciekawi mnie, co oznaczałoby „czyste” wykorzystanie wzorca strategii i jakie aspekty mojej realizacji naruszają definicję. Myślę, że wygląda to trochę zabawnie, ponieważ interfejs strategii jest nieco gruby. Wszystkie przykłady, które widziałem do tej pory, wykorzystują tylko jedną metodę, ale nadal uważam, że zawiera ona algorytm (jeśli część logiki biznesowej można uznać za algorytm, a myślę, że tak jest).

Ponieważ strategia jest również powiadamiana o zdarzeniach podczas realizacji dostawy, można ją również uznać za obserwatora , ale to już inna historia.

Po przeprowadzeniu krótkich badań wydaje się, że jest to „wzorzec złożony” (jak MVC, wzorzec wykorzystujący wiele wzorców projektowych pod spodem w określony sposób) zwany Doradcą . Jest doradcą, czy dostarczanie powinno być kontynuowane, czy nie, ale jest również aktywną obsługą błędów, ponieważ może wycofać dane, gdy zostanie o to poproszony.

W każdym razie, jest to dość złożony przykład, który może sprawić, że poczujesz, że użycie wzorca strategii jest zbyt proste / głupie. W połączeniu z innymi wzorami może być naprawdę złożony i jeszcze bardziej przydatny.

Gui Prá
źródło
6

Wzorzec strategii jest najczęściej używanym wzorcem, szczególnie w przypadku algorytmów walidacji i sortowania.

Pozwólcie, że wyjaśnię na prostym praktycznym przykładzie

enum Speed {
  SLOW, MEDIUM, FAST;
}

class Sorter {
 public void sort(int[] input, Speed speed) {
    SortStrategy strategy = null;
    switch (speed) {
    case SLOW:
        strategy = new SlowBubbleSortStrategy();
        break;
    case MEDIUM:
        strategy = new MediumInsertationSortStrategy();
        break;

    case FAST:
        strategy = new FastQuickSortStrategy();
        break;
    default:
        strategy = new MediumInsertationSortStrategy();
    }
    strategy.sort(input);
 }

}

interface SortStrategy {

    public void sort(int[] input);
}

class SlowBubbleSortStrategy implements SortStrategy {

   public void sort(int[] input) {
    for (int i = 0; i < input.length; i++) {
        for (int j = i + 1; j < input.length; j++) {
            if (input[i] > input[j]) {
                int tmp = input[i];
                input[i] = input[j];
                input[j] = tmp;
            }
        }
    }
    System.out.println("Slow sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
  }

 }

class MediumInsertationSortStrategy implements SortStrategy {

public void sort(int[] input) {
    for (int i = 0; i < input.length - 1; i++) {
        int k = i + 1;
        int nxtVal = input[k];
        while (input[k - 1] > nxtVal) {
            input[k] = input[k - 1];
            k--;
            if (k == 0)
                break;
        }
        input[k] = nxtVal;
    }
    System.out.println("Medium sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }

 }

}

class FastQuickSortStrategy implements SortStrategy {

public void sort(int[] input) {
    sort(input, 0, input.length-1);
    System.out.println("Fast sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
}

private void sort(int[] input, int startIndx, int endIndx) {
    int endIndexOrig = endIndx;
    int startIndexOrig = startIndx;
    if( startIndx >= endIndx)
        return;
    int pavitVal = input[endIndx];
    while (startIndx <= endIndx) {
        while (input[startIndx] < pavitVal)
            startIndx++;
        while (input[endIndx] > pavitVal)
            endIndx--;
        if( startIndx <= endIndx){
            int tmp = input[startIndx];
            input[startIndx] = input[endIndx];
            input[endIndx] = tmp;
            startIndx++;
            endIndx--;
        }
    }
    sort(input, startIndexOrig, endIndx);
    sort(input, startIndx, endIndexOrig);
 }

}  

Kod testowy to

public class StrategyPattern {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    int[] input = new int[] {7,1,23,22,22,11,0,21,1,2,334,45,6,11,2};
    System.out.print("Input is : ");
    for (int i : input) {
        System.out.print(i + ",");
    }
    System.out.println();
    sorter.sort(input, Speed.SLOW);
 }

}

Ten sam przykład pochodzi z http://coder2design.com/strategy-pattern/

Jatinder Pal
źródło
Różne zastosowania wzorca strategii: Walidacje: Kiedy w kodzie jest dużo walidacji, należy je wykonać. Różne algorytmy: Szczególnie, gdy można zastosować różne algorytmy sortowania, np. Sortowanie bąbelkowe lub sortowanie szybkie. Przechowywanie informacji: kiedy możemy informacje w różnych miejscach, np. W bazie danych lub systemie plików. Parsowanie: podczas analizowania możemy użyć różnych strategii dla różnych danych wejściowych. Strategie filtrowania. Strategie rozmieszczenia.
Jatinder Pal
5

Dobrym przykładem wzorca strategii może być gra, w której możemy mieć różne postacie, a każda postać może mieć wiele broni do ataku, ale jednocześnie może używać tylko jednej broni. Mamy więc postać jako kontekst, na przykład King, Commander, Knight, Soldier i broń jako strategię, w której attack () może być metodą / algorytmem zależnym od używanej broni. Więc jeśli konkretnymi klasami broni były Sword, Axe, Crossbow, BowAndArrow itp., Wszystkie zaimplementowałyby metodę attack (). Jestem pewien, że dalsze wyjaśnienia nie są potrzebne.

Sandipan Karmakar
źródło
1
Myślałem, że zaakceptowana odpowiedź miała mówić o tym przykładzie :)
Jeancarlo Fontalvo
2

Zastosowałem podejście strategiczne w dość złożonym silniku w aplikacji, która jest dobrym przykładem. Zasadniczo rola silnika polegała na znalezieniu najpierw listy osób, które miały widżet, a drugą rolą było ustalenie, którzy byli 10 najlepszych ludzi z widżetem na podstawie nieznanej liczby parametrów (np. , ilość w magazynie, opcje wysyłki itp. itd. itd.)

Zasadniczo podzieliliśmy problem na dwie strategie, z których pierwsza polegała na pobieraniu danych, ponieważ wiedzieliśmy, że mamy wiele źródeł naszych widżetów i musimy mieć możliwość uzyskania danych i przekształcenia ich we wspólną strukturę.

Potem zdaliśmy sobie również sprawę, że mamy wiele algorytmów, niektóre oparte na ważeniu parametrów, inne były bardzo dziwne i właściwe, i nie mogłem im oddać sprawiedliwości bez wyciągania wizualizacji i wykresów. wybór najlepszych ludzi.

Nasza usługa sama w sobie polegała na tym, że zasadniczo definiowała dane wejściowe, wyjściowe i przeprowadzała pewną normalizację danych, a także wykorzystywała wzorzec dostawcy do podłączenia dostawców danych specyficznych dla aplikacji i dostawców algorytmów, którzy używali strategii. To był dość skuteczny system.

Odbyliśmy kilka debat, czy używamy strategii lub wzorca szablonu, którego nigdy nie rozwiązaliśmy.

JoshBerke
źródło
2

Czy na pewno status „zamówienia” nie jest wzorcem stanu? Mam przeczucie, że zamówienie nie będzie obsługiwane inaczej w zależności od jego statusu.

Weźmy na przykład metodę Wysyłka na zamówienie:

order.Ship();
  • Jeśli metoda wysyłki różni się w zależności od jej statusu, masz wzorzec strategii.
  • Jeśli jednak metoda Ship () powiedzie się tylko wtedy, gdy zamówienie zostało opłacone, a zamówienie nie zostało jeszcze wysłane, masz wzór stanu.

Najlepszy przykład wzorca stanu (i innych wzorów), jaki znalazłem, był w książce „ Head First Design Patterns ”, co jest niesamowite. Na drugim miejscu pojawi się seria szablonów w blogach Davida Cumpsa .

grootjans
źródło
2

Powiedzmy, że chcesz napisać algorytm do obliczania n-tego X dnia danego miesiąca i roku, np. Drugiego poniedziałku października 2014 r. Chcesz użyć klasy Time Androida android.text.format.Timedo reprezentowania daty, ale chcesz również napisać ogólny algorytm to może również dotyczyć java.util.Calendar.

To właśnie zrobiłem.

W DatetimeMath.java:

public interface DatetimeMath { 
    public Object createDatetime(int year, int month, int day);

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

W TimeMath.java:

public class TimeMath implements DatetimeMath {
    @Override
    public Object createDatetime(int year, int month, int day) {
        Time t = new Time();
        t.set(day, month, year);
        t.normalize(false);
        return t;
    }

    @Override
    public int getDayOfWeek(Object o) {
        Time t = (Time)o;
        return t.weekDay;
    }   

    @Override
    public void increment(Object o) {
        Time t = (Time)o;
        t.set(t.monthDay + 1, t.month, t.year);
        t.normalize(false);
    }
}

W OrdinalDayOfWeekCalculator.java, klasa z algorytmem ogólnym:

public class OrdinalDayOfWeekCalculator {   
    private DatetimeMath datetimeMath;

    public OrdinalDayOfWeekCalculator(DatetimeMath m) {
        datetimeMath = m;
    }

    public Object getDate(int year, int month, int dayOfWeek, int ordinal) {
        Object datetime = datetimeMath.createDatetime(year, month, 1);
        if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
            return datetime;
        } 
        int xDayCount = 0;
        while (xDayCount != ordinal) {
            datetimeMath.increment(datetime);
            if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
                xDayCount++;
            }
        }
        return datetime;
    }
}

W mojej aplikacji na Androida nazwałbym coś takiego

OrdinalDayOfWeekCalculator odowc = 
        new OrdinalDayOfWeekCalculator(new TimeMath());
Time canadianThanksgiving = (Time)odowc.getDate(
        year, Calendar.OCTOBER, Time.MONDAY, 2);

Jeśli chcę ponownie użyć tego samego algorytmu java.util.Calendar, po prostu napisałbym klasę CalendarMath, która implementuje trzy metody w DatetimeMath, a następnie użyłbym

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);
anomalne
źródło
2
public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // pay by paypal
        cart.pay(new PaypalStrategy("[email protected]", "mypwd"));

        // pay by credit card
        cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15"));
    }
}

interface PaymentStrategy {
    public void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {

    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }

}

class PaypalStrategy implements PaymentStrategy {

    private String emailId;
    private String password;

    public PaypalStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }

}

class Item {

    private String upcCode;
    private int price;

    public Item(String upc, int cost) {
        this.upcCode = upc;
        this.price = cost;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }

}

class ShoppingCart {

    // List of items
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<Item>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}
Vivek Goel
źródło
1

Kilka tygodni temu dodałem wspólny interfejs Java, który został zaimplementowany przez jeden z naszych obiektów domeny. Ten obiekt domeny został załadowany z bazy danych, a reprezentacja bazy danych była schematem gwiazdy z ponad 10 gałęziami. Jedną z konsekwencji posiadania tak ciężkiego obiektu domeny jest to, że musieliśmy stworzyć inne obiekty domeny, które reprezentowały ten sam schemat, chociaż były mniej ciężkie. Dlatego sprawiłem, że inne lekkie obiekty implementują ten sam interfejs. Inaczej mówiąc mieliśmy:

public interface CollectibleElephant { 
    long getId();
    String getName();
    long getTagId();
}

public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }

Pierwotnie chciałem użyć CollectibleElephantdo sortowania Elephants. Dość szybko moi koledzy z drużyny wpadli na to, CollectibleElephantaby przeprowadzić kontrole bezpieczeństwa, filtrować je, gdy są wysyłane do GUI itp.

Alan
źródło
1

Musieliśmy stworzyć interfejs obsługi administracyjnej innej firmy dla platformy korporacyjnej z bardzo skomplikowaną bazą danych. Dostarczenie danych do dostarczenia było listą naszych typów danych, które zostały umieszczone w kolejce priorytetowej w naszej aplikacji, aby mogły zostać zapisane w bazie danych we właściwej kolejności ze względu na zależności.

Proces zapisywania tych danych był wtedy dość prosty, wyskakuj z góry kolejki priorytetów, a następnie wybierz strategię opartą na typie wyodrębnionego obiektu.

Coxy
źródło
0

Z Wikipedii

W programowaniu komputerowym wzorzec strategii (znany również jako wzorzec polityki) to behawioralny wzorzec projektowania oprogramowania, który umożliwia wybór algorytmu w czasie wykonywania. Zamiast bezpośrednio implementować pojedynczy algorytm, kod otrzymuje instrukcje w czasie wykonywania, które z rodziny algorytmów mają być używane

W aplikacji Windows Paint można zobaczyć wzorzec strategii, w którym można niezależnie wybrać kształt i kolor w różnych sekcjach. Tutaj kształt i kolor to algorytmy, które można zmieniać w czasie wykonywania.

Jeśli chcesz narysować okrąg w kolorze czerwonym, zamiast oferować opcję „RedCircle”, pozwalają one wybrać okrąg i wybrany kolor.

Shape redCircle = new RedCircle(); // Without stretegy Pattern
Shaped redCircle = new Shape("red","circle"); // With Strategy pattern

Bez wzorca strategii zwiększy się liczba klas z iloczynem kartezjańskim kształtu i koloru. Również interfejs zmienia się dla każdej implementacji.

bharanitharan
źródło
0

Wyobraź sobie na przykład strzelankę z wrogami AI. Chcesz, aby nieustannie walczyli na różne sposoby w zależności od tego, co się dzieje. Dzięki wzorowi strategii możesz w sposób ciągły zapętlać i dynamicznie zmieniać sposób wykonania określonej czynności lub działania.

interface FightingStategy{
    public void fight();
}
public Defense implements FightingStrategy{
    public void figth(){
        ... hide behind wall to shoot
    }
}
public Berserker implements FightingStrategy{
    public void fight(){
        ... run towards you, headrolls and shoots
    }
}
public Dead implements FightingStrategy{
    public void fight(){
        ... is dead, doesn't move
    }
}

public AiShooter{

    FightingStrategy fightingStrategy;

    public AiShooter(){
        fightStrategy = new Berserker();
    }

    public void fight(){
        this.fightingStrategy.fight();
    }

    public void changeStrategy(FightingStrategy f){
        this.fightingStrategy = f;
    }
}

public static void main(){

    ... create list of AiShooters...
    while (condition){
        list.forEach(shooter -> shooter.fight());
    }
    ... you shoot back
    list.ForEach(shooter -> shooter.changeStrategy(new 
Defense()));

    ... you kill one
    list.get(n).changeStrategy(new Dead());
}
Cédric S
źródło