Pytanie Przykład realistycznego wzoru strategii


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

Zamierzałem spróbować wyjaśnić to kilku osobom, ale jedynym przykładem, jaki mogę wymyślić, jest użycie różnych klas walidacji w oparciu o status "zamówienia".

Czytałem kilka artykułów w Internecie, ale zazwyczaj nie opisują one prawdziwego powodu do wykorzystania strategii, np. Generowania raportów / rachunków / sprawdzania poprawności itp.

Czy istnieją przykłady z rzeczywistego świata, w których uważasz, że wzór strategii jest powszechny?


76
2017-12-16 01:29


pochodzenie




Odpowiedzi:


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 (np. W przypadku plików <1 gb)

W przypadku dużych plików można użyć innej strategii, 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.
         }

}

The

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

Zwrócił poprawną instancję strategii dla szyfru.

Mam nadzieję, że to pomoże.

(Nie wiem nawet, czy Cipher jest właściwym słowem: P)


86
2017-12-16 01:40



Czy twój przykład nie jest już wzorcem fabrycznym? Również myślę, że nie będzie działać na przykład w C #. Twoja metoda "getCipher ()" jest metodą statyczną, ale w języku C # nie można zdefiniować metody statycznej w interfejsie (ani w języku Java, jak sądzę, ale nie jestem tego pewien). - FrenchData
Idą razem. Fabryka tworzy strategię, ale strategia sama w sobie utrzymuje algorytm wykonywania (w zasadzie) tej samej operacji. Strategię można również zmienić w czasie wykonywania. O metodzie fabrycznej, którą masz rację, zmieniłem ją. - OscarRyz
Aby dodać punkt Osacara, bez fabryki można go 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 świetną przykładową obecnością CipherFactory może mylić osoby nieznajome w schemacie strategii. - Tim Bezhashvyly
Fabryczny wzór jest o kreacji, strategia o behawioralnym. Jest trochę inne prawo? - nhoxbypass


Znowu stary post, ale wciąż pojawia się w wyszukiwaniach, więc dodam dwa kolejne przykłady (kod jest w C #). Bardzo podoba mi się wzorzec strategii, ponieważ wielokrotnie zapisywał mój tyłek, gdy menedżerowie projektu 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. " To wideo wyjaśniające wzór strategii, używa StarCraft jako przykładu.

Rzeczy należące do tej kategorii:

  • Sortowanie: chcemy posortować te liczby, ale nie wiemy, czy użyjemy BrickSort, BubbleSort lub innego sortowania

  • Walidacja: Musimy sprawdzać przedmioty zgodnie z "Pewną regułą", ale nie jest jeszcze jasne, jaka będzie ta zasada i możemy myśleć o nowych.

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

  • Przechowywanie informacji: Chcemy, aby aplikacja zapisywała informacje w bazie danych, ale później może być konieczne zapisanie pliku lub utworzenie strony internetowej

  • Wyjście: musimy wypisać X jako zwykły ciąg znaków, 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 osobie ma status "zatwierdzony" lub "odrzucony", który zależy 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, status jest odrzucany, itp.

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

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

W momencie przypisywania produktu do osoby, tworzę RuleAgent, podaję mu listę reguł (które wszystkie implementują IRule) i poproszę o sprawdzenie przypisania. Przejdzie przez wszystkie jego zasady. Które, ponieważ wszystkie implementują ten sam interfejs, wszystkie mają IsApproved metoda i zwracaj wartość false, jeśli któraś z nich zwraca wartość false.

Teraz, gdy na przykład kierownik nagle pojawia się i mówi, musimy także odrzucić wszystkie zadania stażystów lub wszystkie zadania dla osób pracujących w nadgodzinach ... Tworzysz nowe klasy 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 dodawać ani usuwać instrukcji if lub kodu, po prostu stwórz nową klasę reguł, która implementuje interfejs IRUle i przełącz je, gdy jest to konieczne.


Kolejny świetny przykład: seria filmów Scotta Allena w http://www.asp.net/mvc/pluralsight gdzie używa wzorca strategicznego w części aplikacji testującej Jednostki

Buduje stronę internetową, która ma stronę wyświetlającą przedmioty oparte na popularności. Jednak "Popularny" może mieć wiele rzeczy (większość wyświetleń, większość subskrybentów, data utworzenia, większość aktywności, najmniej komentarzy itp.), A zarządzanie sprawami jeszcze nie wie dokładnie, jak je zamówić, i może chcieć eksperymentować z różnymi zamówienia w późniejszym terminie. Tworzysz interfejs (IOrderAlgorithm lub coś podobnego) za pomocą metody zamawiania, a pozwany obiekt-Zleceniodawca deleguje zlecenie do konkretnej implementacji interfejsu IOrderAlgorithm. Możesz utworzyć "CommentOrderer", "ActivityOrderer", etc ... I po prostu je wyłączyć, gdy pojawią się nowe wymagania.


43
2018-01-16 14:57





Mogę wymyślić kilka dość prostych przykładów:

  • Sortowanie listy. Strategia jest porównaniem używanym do określenia, która z dwóch pozycji na liście to "Pierwsza"
  • Możesz mieć aplikację, w której algorytm sortowania (QuickSort, HeapSort itp.) Może być wybrany w czasie wykonywania
  • Dodatki, układy i filtry w Log4Net i Log4j
  • Kierownicy układów w zestawach narzędzi UI
  • Kompresja danych. Możesz mieć interfejs ICompressor, którego jedyna metoda wygląda mniej więcej tak:

    byte [] compress (wejście bajtowe []);

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


11
2018-01-08 21:07





Jednym z typowych zastosowań wzorca strategii jest definiowanie niestandardowych strategii sortowania (w językach bez funkcji wyższego rzędu), np. aby posortować listę ciągów według długości w Javie, przekazując anonimową klasę wewnętrzną (implementację 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 strategie mogą być stosowane do natywnych zapytań z bazami danych obiektów, np. w db4o:

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});

9
2018-01-11 16:12





Mam aplikację, która każdego dnia synchronizuje bazę użytkowników z katalogiem korporacyjnym. Użytkownik kwalifikuje się lub nie kwalifikuje się na podstawie jego statusu na Uniwersytecie. Każdego dnia realizowany jest program obsługi i zapewnia, że ​​ci, którzy mają być uprawnieni, są włączani do aplikacji, a ci, którzy tego nie robią, są usuwani (w rzeczywistości według zgrabnego algorytmu degradacji, ale nie ma to znaczenia). W sobotę wykonuję bardziej szczegółową aktualizację, która synchronizuje niektóre właściwości każdego użytkownika, a także upewnia się, że ma odpowiednie uprawnienia. Pod koniec miesiąca wykonuję przetwarzanie z powrotem na rachunek w oparciu o wykorzystanie w danym miesiącu.

Używam kompozycyjnego wzoru strategii do wykonania tej synchronizacji. Główny program zasadniczo wybiera główną strategię w zależności od dnia tygodnia (tylko synchronizacja zmienia / synchronizuje wszystkie) i czas semestru względem kalendarza akademickiego. Jeśli cykl rozliczeniowy się kończy, oznacza to, że jest on zgodny ze strategią rozliczeniową. Następnie uruchamia wybraną strategię za pomocą standardowego interfejsu.

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


8
2017-12-16 02:36



To bardzo dobry przykład. Co więcej, wyraźnie pokazuje różnicę pomiędzy wzorcem poleceń i strategii w pigułce - intencją. "Zasadniczo główny program wybiera główna strategia w zależności od dnia tygodnia " - Utsav T


Kluczowe uwagi:

  1. Strategia to behawioralny wzór projektu. Służy do przełączania rodziny algorytmów.

  2. Ten wzorzec zawiera jedną abstrakcyjną strategię berło i wiele beton implementacje strategii (algorytmy) tego interfejsu.

  3. Aplikacja wykorzystuje strategię berło tylko. W zależności od niektórych parametrów konfiguracyjnych konkretna strategia zostanie oznaczony tagiem berło.

Diagram UML z wikipedia

enter image description here

Jeden prawdziwy przykład: Linie lotnicze oferujące zniżki w ciągu kilku miesięcy (od lipca do grudnia). Możesz go mieć Opłata moduł, który decyduje o opcjach cenowych w zależności od liczby miesięcy.

Rzuć okiem na prosty przykład. Przykład ten można rozszerzyć na aplikacje sprzedaży detalicznej online, dzięki czemu łatwo można uzyskać zniżki na zakupy w specjalnych dniach / godzinach szczytu.

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);
    }

}

wydajność:

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:

strategia wzór dzone

strategia wzór przez sourceraking


8
2018-02-03 14:52





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

Jest to bardzo praktyczny przykład wzoru strategii używanego w systemie dostarczania dokumentów.

Miałem system dostarczania plików PDF, który otrzymał archiwum zawierające wiele dokumentów i niektóre metadane. Na podstawie metadanych zdecydował, gdzie umieścić dokument; powiedzmy, w zależności od danych, mógłbym zapisać dokument w A, B, lub C systemy pamięci masowej lub połączenie tych trzech.

Różni klienci korzystali z tego systemu i w przypadku błędów mieli różne wymagania w zakresie przywracania / usuwania błędów: jeden chciał, aby system dostaw zatrzymał się przy pierwszym błędzie, zostaw wszystkie dokumenty już dostarczone w swoich magazynach, ale zatrzymaj proces i nie dostarczaj niczego innego ; inny chciał go wycofać B w przypadku błędów podczas przechowywania w C, ale pozostaw wszystko, co już zostało dostarczone A. Łatwo sobie wyobrazić, że trzecia lub czwarta także będzie miała inne potrzeby.

Aby rozwiązać problem, utworzyłem podstawową klasę dostarczania, która zawiera logikę dostarczania, a także metody wycofywania elementów ze wszystkich magazynów. Te metody nie są faktycznie wywoływane przez system dostarczania bezpośrednio w przypadku błędów. Zamiast tego, klasa używa Dependency Injection, aby otrzymać klasę "Rollback / Error Handling Strategy" (w oparciu o klienta korzystającego z systemu), która jest wywoływana w przypadku błędów, która z kolei wywołuje metody wycofywania, jeśli jest odpowiednia dla tej strategii.

Klasa dostarczania sama informuje o tym, co dzieje się w klasie strategii (jakie dokumenty zostały dostarczone do przechowywania i jakie awarie wystąpiły), a gdy tylko wystąpi błąd, prosi o strategię, czy kontynuować, czy nie. Jeśli strategia mówi "stop it", klasa wywołuje metodę "cleanUp" strategii, która wykorzystuje informacje wcześniej zgłoszone, aby zdecydować, które metody wycofywania wywołać z klasy dostarczania, lub po prostu nic nie robić.

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

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

Tak więc mam teraz dwie różne strategie: jedną jest QuitterStrategy (która kończy się na pierwszym błędzie i nic nie usuwa), a druga to MaximizeDeliveryToAStrategy (który stara się jak najbardziej nie przerywać procesu i nigdy nie przywracać rzeczy dostarczonych do magazynu A, ale wycofuje rzeczy z B jeśli dostawa do C zawiedzie).

Z mojego rozumienia jest to jeden z przykładów wzorca strategii. Jeśli (tak, czytasz) myślisz, że się mylę, proszę komentarz poniżej i daj mi znać. Ciekawi mnie, co stanowiłoby "czyste" użycie wzorca strategii i jakie aspekty mojej implementacji naruszają definicję. Myślę, że to trochę zabawne, ponieważ interfejs strategii jest trochę gruby. Wszystkie przykłady, które do tej pory widziałem, wykorzystują tylko jedną metodę, ale nadal uważam, że zawiera ona algorytm (jeśli logika biznesowa może być uważana za algorytm, co moim zdaniem ma miejsce).

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

Po przeprowadzeniu niewielkich badań wydaje się, że jest to "złożony wzór" (np. MVC, wzorzec wykorzystujący wiele wzorów pod spodem w określony sposób). Doradca. Doradca określa, czy dostawa powinna być kontynuowana, czy nie, ale jest również aktywną procedurą obsługi błędów, ponieważ może ją przywrócić, gdy zostanie o to poproszony.

Tak czy inaczej, jest to dość złożony przykład, który może sprawić, że twoje odczucie, że użycie wzorca strategii jest zbyt proste / głupie. Może być bardzo skomplikowany, a nawet bardziej odpowiedni, gdy jest używany razem z innymi wzorami.


7
2017-10-05 05:57





Wzorzec strategii to najczęściej używany wzorzec specjalnie dla walidacji i algorytmów sortowania.

Pozwól mi wyjaśnić za pomocą prostego praktycznego przykładu

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 tego jest

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/


5
2017-07-08 11:38



Różne zastosowania wzorca strategii: Walidacje: Kiedy w kodzie musi być wiele weryfikacji. 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 uzyskać informacje w różnych miejscach, np. W bazie danych lub systemie plików. Parsowanie: podczas analizy możemy użyć innej strategii dla różnych danych wejściowych. Strategie filtrowania. Strategie układu. - Jatinder Pal


Dobrym przykładem wzoru strategii byłaby 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 strategia, w której atak () może być metodą / algorytmem, który zależy od używanej broni. Więc jeśli konkretnymi klasami broni były Miecz, Topór, Kusza, BowAndArrow itd., Wszystkie zaimplementowałyby metodę attack (). Jestem pewien, że dalsze wyjaśnienia nie są potrzebne.


3
2018-01-15 16:08



Myślałem, że zaakceptowana odpowiedź powinna mówić o tym przykładzie :) - Jeancarlo Fontalvo


Czy jesteś pewien, że status "zamówienia" nie jest wzorem państwa? Mam przeczucie, że zamówienie nie będzie obsługiwane inaczej, w zależności od jego statusu.

Weźmy na przykład tę metodę Statek w sprawie zamówienia:

order.Ship();
  • Jeśli metoda wysyłki jest różna funkcja jego statusu, to masz otrzymałem wzór strategii.
  • Jeśli jednakże Statek() metoda udaje się tylko kiedy zamówienie zostało zapłacone, oraz zamówienie jeszcze nie zostało wysłane, masz wzór stanu.

Najlepszy przykład wzoru stanu (i innych wzorów), który znalazłem, był w książce "Głowa Pierwsze wzorce projektowe", co jest niesamowite. Zbliża się sekunda Seria blogów Davida Cumpsa.


2
2018-01-22 23:07





Powiedzmy, że chcesz napisać algorytm do obliczenia n-ty Xday danego miesiąca i roku, np. w drugi poniedziałek października 2014 r. Chcesz korzystać z klasy czasu Androida android.text.format.Time do reprezentowania daty, ale chcesz również napisać ogólny algorytm, który również można zastosować java.util.Calendar.

Oto co 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);
}

In 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 ogólnym algorytmem:

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 napiszę klasę CalendarMath, która implementuje trzy metody w DatetimeMath, a następnie użyj

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);

2
2018-01-18 19:27