Program sterowany zdarzeniami.

0

Witam. Mam drobny problem.
Nie będę zamieszczał żadnego kodu gdyż w tym przypadku mija się to z celem. Będę pisał w uproszczeniu.
Napisałem program. Jest w nim funkcja A i funkcja B. Początkowo tak się zkłada, że wywoływana jest funkcja A. Gdy wykona się funkcja A może zostać (powinna) wywołana funkcja B. Gdy funkcja B się wykona może zostać (powinna) zostać wywołana funkcja A, itd...
Na pierwszy rzut oka wygląda na nieskończoną rekurencję..., ale tak nie jest.
Chodzi mi o to, że na początku wywołujemy funkcję A. Jeżeli się ona wykona (podkreślam, wykona w całości) i jeżeli wszystko pójdzie dobrze (zostaną spełnione określone warunki) może zostać wywołana funkcja B. Podobna sytuacja ma się dla funkcji B.
Długo rozmyślałem jak rozwiązać ten problem i doszedłem (doczytałem), że najlepiej byłoby wprowadzić zdarzenia. To znaczy po wykonaniu funkcji A wstawialibyśmy do kolejki zdarzeń zdarzenie, w którym zawierałoby się wywołanie funkcji B, itd...
Jednakże tutaj urywa się moja twórczość. Nie wiem dokładnie jak miałbym to zrobić.
Bardzo proszę o pomoc....

1

Jak pisałem komunikacje socketu i czytanie danych - to miałem podobne dylematy. Jak przeczytał dane np. z dokumentu (całe) to uruchamiał komunikacje na sockecie- wysyłanie danych...i potem (a właściwie w innym wątku) cały czas czekał na kolejne wywolanie odczytania. Oczywiście wysył mógł się nie powieśc i bylo trzeba spróbowac jeszcze raz.

Ja to rozwiązałem nielubianymi nie wiem czemu polami statycznymi - one świetnie się nadają do flagowania takich rzeczy. W uproszczeniu:

Miałem pętle while - nieskonczoną w której to poprzez zwykłe ify sterowałem przebiegiem programu. Parametrami ifów były pola statyczne...coś w ten deseń:

public static boolean flagaZakonczeniaA = false;

...
metodaA();
while(true)
{
if(flagaZakonczeniaA )
{
flagaZakonczeniaA = false;
metodaB();
}
Thread.sleep(100);
}

Oczywiście jak się wykonuje metoda A to na końcu - jak wszystko odbrze pójdzie to trzeba zmienic statyczna metode sterujaca NazwaKlasy.flagaZakonczeniaA = true;

W bardzo fajny sposób mozna dodawać kolejne ify sterujace przebiegiem-jakieś wyjątki, komunikaty...na sockecie potrzebowałem asynchronicznej wymiany danych-też się sprawdziło.

A pola satyczne mozna umieścić w odrębnej klasie i je sprawnie monitorować.

1
  1. stworzenie 2 kolejek (JMS - ewentualnie jednej typu publish-subscribe).Jak cokolwiek wrzucisz na kolejkę A to jej konsument przekaże to do metody A i metoda A jak uzna, że ma zostać wywołana metoda B to wrzuci coś (np jakieś dane do obróbki) do kolejki B.

  2. zrobić takie event listenery jak w swingu

  3. jeśli metoda A i B są w tej samej klasie to nie powinno być problemu z wzajemnym wywoływaniem, jeśli zaś w innych klasach to może klasa A powinna przechowywać referencję do obiektu B i vice versa?

4)wprowadzić jakiś obiekt nadrzędny, który będzie odpowiednio wywoływał metody A i B w zależności od tego co one zwrócą

Trochę mało szczegółów podałeś...jakiego typu komunikacji oczekujesz pomiędzy metodami A i B(np asynchroniczny)? Musisz tutaj zwrócić uwagę, aby nie wpaść w jakąś nieskończoną rekurencję. Podaj więcej szczegółów, ewentualnie wskaż, które rozwiązanie najbardziej odpowiada twoim potrzebom to postaram się coś rozwinąć.

0

Ok. Zatem podaję więcej szczegółów.
Stworzyłem prosty programik z interfejsem graficznym.
Moja metoda main wygląda tak:

MyFrame myApp = new MyFrame();
myApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myApp.setVisible(true); 

dlatego od razu rozwiązanie z pętlą while i sprawdzaniem warunków odrzucamy.
Mój program to klasyczna gra logiczna, w której użytkownik i komputer wykonują naprzemiennie ruchy.
Załóżmy na razie dla uproszczenia, że grę zaczyna użytkownik. Użytkownik wskazuje myszą pole na planszy, na którym ma pojawić się piłeczka.
Po tym zdarzeniu (kliknięcie w odpowiednie pole) Następuje odtworzenie animacji (piłeczka spada w odpowiednie pole). Gdy to się wykona rolę powinien przejąć komputer. On sobie wybiera pole i tam też opada piłeczka (oczywiście po każdym ruchu sprawdzamy, czy gra się nie zakończyła). Następnie rolę może przejąć użytkownik, tzn. oczekujemy na zdarzenie kliknięcia od użytkownika itd...
Teraz jak to jest zorganizowane.
Mam klasę A.
W klasie są dwie publiczne metody.

  1. metoda: wykonajRuchUżytkownik
  2. metoda: wykonajRuchKomputer
    Metody te głównie odpowiadają za otworzenie animacji i zaznaczenie na planszy (tablicy) zajętego pola. (współpraca z metodą paintComponent i praoma atrybutami klasy).

Oczywiście w klasie A są dodatkowo zaimplementowane interfjsy do obsługi zdarzeń pochodzących od myszki, tak więc z tym, z nie ma problemu.
Początkowo robiłem to tak:
stworzyłem sobie flagę (atrybut w danej klasie, gdzie są zaimplementowane metody) wskazujące czy ruch może wykonać użytkownik (tzn. czy kliknięcie przez użytkownika będzie mogło być właściwie obsłużone).
Jeżeli flaga pozwalała na ruch użytkownika, to metoda obsługująca to zdarzenie (zdarzenie kliknięcia) zawierała w sobie kod odpowiedzialny za ruch użytkownika (wykonajRuchUżytkownik) a następnie wykonywała kod wykonajRuchKomputer).
Niestety takie rozwiązanie (mimo iż może nie wydawać się wprost logiczne) doprowadziło do błędnie działającego problemu). Odpowiednie metody były wywoływane w różnej kolejności (tzn. część instrukcji najpierw, później inna część instrukcji i prowadziło to do błędów, nie wiem czemu ale jakoś dziwnie była wówczas obsługiwana kolejka zadań systemowych). Dlatego doszedłem do wniosku, że lepiej byłoby rozwiązać to w ten sposób:
Gdy pojawi się zdarzenie kliknięcia to generujemy zdarzenie wykonania metody: wykonajRuchUżytkownik.
Gdy metoda wykonajRuchUżytkownik się wykona to generujemy zdarzenie wykonania ruchu przez komputer (zdarzenie będzie polegało de facto na wykonaniu metody wykonajRuchKomputer), następnie po obsłużeniu tego zdarzenia ustawiamy flagę logiczną w klasie umożliwiająca odpowiednie obsłużenie zdarzenia kliknięcia (a tym samym ponownego wykonania ruchu przez użytkownika) itd.....

Mój proble objawia też się w innej sytuacji. Załóżmy, że grę rozpoczyna komputer. Po kliknięciu na przycisku ZacznijGrę powinna zostać wykonanna metoda obsugująca to zdarzanie. Taka metoda jest zaimplementowana. Jej działanie polega wykonaniu pewnej części kodu (m. in. ustawia dany przycik na niewidoczny setVisible(false) a następnie odtwarza animację związaną z ruchem komputera). Jednakże uruchomienie programu daje inne rezultaty. Przebieg programu jest inny niż wskazywałby kod. Najpierw zostaje odtwrzonoa animacja związana z ruchem komputera (na błędnych wartościach) a dopiero później są wykonywane czynności (które były zapisane przed tą instrukcją) m.in ustawianie odpowiednich atrybutów i ustawienie przycisku jako niewidoczny. Tak więc jeżeli ktoś to rozumie co napisałem i wie mniej wiecej o co chodzi to prosiłbym o odpowiedź jak można to obejść. Bardzo mi na tym zależy, gdyż nie mam pomysłu a trochę się napisałem ;)

Niestety nie wiem jak wykonać obsługę tych zdarzeń. Może wykonać należy to w inny sposób? Jaki? Proszę o pomoc.

0

OK. Przepraszam za ewentualny kłopot ;).
Problem już został przeze mnie rozwiązany. Miałem jeden drobny błąd w kodzie plus dołożyłem jedną flagę.
Pozdrawiam.

0

autor:
Polecam Scala Actors: http://www.scala-lang.org/node/242

1 użytkowników online, w tym zalogowanych: 0, gości: 1