2011-07-07 8 views
10

Programuję w języku Java, ale jest to raczej pytanie projektowe, więc każdy programista OO mógłby prawdopodobnie odpowiedzieć na to pytanie. Mam pytanie dotyczące wzoru projektu strategii. Oto kilka atramentów, które okazały się przydatne:Strategiczny wzór - wybór strategii z licznikami

  1. Strategy Pattern Explained-OO Design.

Używam wzorca strategii dwa razy z jedną grupą czterech strategii i jedną grupą trzech. W każdym przypadku decyduję, którą strategię zastosować, utrzymując rozpadający się licznik. jeśli strategia, którą oprogramowanie zdecyduje się zastosować, licznik zostanie zwiększony o jeden. Jeśli zastosowana strategia nie powiedzie się, licznik zostanie zmniejszony o jeden. Bez względu na sukces lub porażkę WSZYSTKIE liczniki są mnożone przez liczbę około .9, aby "z czasem" zepsuć liczniki. Oprogramowanie wybierze, którą strategię zastosować, w oparciu o którą strategię ma najwyższy licznik. Przykład mojego bardzo prostego UML pokazano poniżej:

Example UML.

A w postaci Link (dla łatwiejszego odczytu): Example UML

UML powyżej jest makieta chciałbym użyć. Jeśli nie możesz powiedzieć z powyższego UML, piszę grę Rock, Paper, Scissors z zamiarem pokonania wszystkich moich znajomych.

teraz do problemu:

nie mogę się zdecydować, jak wdrażać „system licznik” w celu podjęcia decyzji, która strategia w użyciu. Myślałem o jakiejś klasie "danych", w której można przechowywać wszystkie liczniki i ciągi historii, ale wydawało mi się to po prostu niezrozumiałe. Przez cały czas utrzymuję około 2 struny i około ośmiu liczników (może więcej, może mniej). Dlatego właśnie myślałem o klasie "danych", w której wszystko może być przechowywane. Mogę tylko utworzyć instancję klasy do użycia w metodach ChooseStrategy() i chooseMetaStrategy(), ale po prostu nie wiem. To mój pierwszy projekt, nad którym będę pracował sam i po prostu nie mogę nic zdecydować. Czuję, że jest zdecydowanie lepsze rozwiązanie, ale nie mam wystarczająco dużo doświadczenia, aby wiedzieć.

Dzięki!

------------------------------------ kontynuacja 1 ------ --------------------------------------

Dziękuję bardzo za odpowiedzi wszystkich i miłe słowa. Mam jednak kilka pytań uzupełniających. Jestem nowy w StackOverflow (i kocham to), więc jeśli to nie jest właściwe miejsce na pytanie uzupełniające, proszę dać mi znać. Edytuję mój oryginalny wpis, ponieważ moja obserwacja jest trochę długa.

Patrzyłem na radę Paula Soniera na temat stosowania wzoru kompozytowego i wyglądało to bardzo interesująco (dzięki Paul!). Na potrzeby strategii HistoryMatching i "intelligent" AntiRotation chcę zaimplementować ciąg wszystkich gier przeciwników dostępnych dla obu klas. Ponadto, chcę, aby ciąg historii był edytowany bez względu na strategię, którą grał mój program, dzięki czemu mogę zachować dokładny zapis dramatów przeciwnika. Bardziej obszerny ciąg (właściwie prawdopodobnie użyję LinkedList, ale jeśli ktoś zna lepszą (pod-String/sub-List) metodę/kolekcję wyszukiwania proszę daj mi znać) im lepsza strategia może przewidzieć zachowanie przeciwnika.

Zastanawiam się, w jaki sposób mogę zaimplementować ten "ciąg" lub kolekcję, nadal używając złożonego wzoru.

Ponadto, TheCapn wychował, że byłoby dobrym pomysłem przechowywanie różnych liczników i kolekcji historii dla każdego przeciwnika. Dowolna myśl o tym, jak wprowadzić to w złożony wzór?

+3

biorąc pod uwagę swój pierwszy projekt, naprawdę doceń swój kierunek myślenia i zrozumiesz znaczenie dobrego projektu. –

Odpowiedz

5

Idealnym rozwiązaniem jest powiązanie liczników ze strategiami, ponieważ liczą one sukcesy strategii. Jednak niekoniecznie chcesz, aby strategie wiedziały coś o fakcie, że są policzone. Według mnie oznacza to wzór złożony, w którym zawijasz swoją klasę strategii w klasę, która ma logikę śledzenia/degradacji/modyfikowania liczby zastosowań.

To daje lokalność (liczba jest przechowywana ze strategią, którą zlicza) i skład funkcjonalny (funkcja liczenia jest uwięziona w klasie kompozycji). Jak dobrze, utrzymuje izolację klasy strategii od innych wpływów.

Twój podział projektu do tej pory wygląda dobrze; z pewnością jesteś na dobrej i interesującej ścieżce. Mam nadzieję że to pomoże!

+0

Hej! Dziękuję za odpowiedź, która naprawdę pomogła. Zrobiłem jednak edycję mojego posta z kilkoma dodatkowymi pytaniami, więc jeśli dostaniesz szansę, aby je przejrzeć, WIELKIM to doceniam. Dzięki jeszcze raz! –

2

Po pierwsze, sugeruję wypróbowanie czegoś nieco prostszego: move-to-front. Zachowaj swoje strategie na liście, początkowo w dowolnej kolejności. Przetestuj je tak:

List<Strategy> strategies; 
Strategy successfulStrategy = null; 
for (Strategy strategy: strategies) { 
    boolean success = strategy.attempt(); 
    if (success) { 
     break; 
     successfulStrategy = strategy; 
    } 
} 
if (successfulStrategy == null) throw new NoSuccessfulStrategyException(); 
// now move the successful strategy to the front of the list 
strategies.remove(successfulStrategy); 
strategies.add(0, successfulStrategy); 

Zasadniczo udana strategia przechodzi bezpośrednio na czoło kolejki; z biegiem czasu dobre strategie gromadzą się w pobliżu głowy. To nie jest tak subtelne jak coś oparte na liczeniach, ale jest proste, a w praktyce, do różnych zastosowań, działa bardzo dobrze.

Jednakże, jeśli nie jesteś ustawiony na liczbę, to powinienem stworzyć Decorator, który opakowuje strategię i utrzymuje liczbę, którą można porównać z innymi takimi obiektami. Kod jest najprostszym wyjaśnieniem:

public class ScoredStrategy implements Strategy, Comparable<ScoredStrategy> { 
    private final Strategy delegate; 
    private float score; 

    public ScoredStrategy(Strategy delegate) { 
     this.delegate = delegate; 
    } 

    public boolean attempt() { 
     boolean success = delegate.attempt(); 
     score = (score * 0.9f) + (success ? 1 : -1); 
     return success; 
    } 

    public int compareTo(ScoredStrategy that) { 
     return -Float.compare(this.score, that.score); 
    } 
} 

W głównym obiekcie, zabrać swoje rzeczywiste strategie i owinąć każdy w ScoredStrategy. Umieść je na liście. Kiedy potrzebujesz strategii, pracuj nad listą, wywołując każdą strategię, aż uderzysz w taką, która działa. Następnie po prostu sort the list. Strategie będą wtedy uporządkowane, od najlepszych do najgorszych.

+1

Myślę, że chcesz przerwać po wykonaniu zadania w pętli for. – Lii

1

Chociaż "przylegający", myślę, że twoja intuicja jest poprawna. Zachowaj wszystkie data w collection, do których można uzyskać dostęp za pomocą wybranej strategii. Zachowaj oddzielne obiekty dla każdego z przeciwników i stwórz nowe obiekty data podczas definiowania nowych przeciwników, przechowywanie ich w kolekcji, takiej jak Map, zapewni łatwy dostęp.

Powodem tego jest to, że jeśli zdecydujesz się zmienić "MetaStrategie", będziesz potrzebować informacji o istnieniu, które będą dostępne dla twoich obiektów, przechowując je w innych, abstrakcyjnych obiektach, które możesz znaleźć szukając/analizując/zbierając dane trudniejsze niż powinno być. To również ściśle pasuje do modelu mentalnego, który sam wygenerowałeś, więc próba pójścia wbrew temu przepływowi może prowadzić do błędów w projektowaniu.

Jedynym innym logicznym rozwiązaniem tego problemu (z mojej krótkiej burzy mózgów) jest lepsze zdefiniowanie otaczającej logiki lub heurystyki, które planujesz wdrożyć. Jeśli utworzysz bardziej konkretny sposób wyboru MetaStrategy, będziesz lepiej rozumieć, w jaki sposób dane muszą być gromadzone w celu uzyskania dostępu. Metoda Toma Andersona wygląda obiecująco dla twojego projektu!

+0

to doskonały punkt, o którym mówiłeś, śledząc różne zestawy danych dla różnych przeciwników. Nie myślałem o tym, dopóki tego nie podniosłeś. Dziękuję Ci! –