Wątek przeniesiony 2023-07-29 11:07 z Java przez Riddle.

Jak sprawić aby GUI było niezależne od logiki w mojej aplikacji?

1

Mam napisaną logikę dla szachów, chcę teraz za pomocą Swinga dodać GUI do tej gry, tylko trochę nie wiem jak się za to zabrać. Znam podstawy Swinga, Eventy itd, problem mam w tym, że przeczytałem, że logika powinna być niezależna od GUI. Jak to zrealizować w Javie? Czy do tego musze znać jakieś design patterny, czy jako początkujący nie powinienem się tym na ten moment przejmować i robić tak żeby po prostu "działało"? Jeśli ktoś ma jakieś materiały do nauki powiązane z tym tematem to proszę o podlinkowanie.

3

Jak już masz logikę napisaną to raczej mała szansa że się nie uda zrobić dobrze (chociaż wszystko jest możliwe XD ).
Ta rada z tytułu jest raczej skierowana do osób które piszą jednocześnie GUI i logikę żeby nie było to pomieszane w tych samych metodach i klasach, pakietach

0

@wydra__: Jak się uczysz to dokończ by działało. Development polega na ciągłym refaktorze. Jak wszystko będzie działało, zrób nową iteracje projektu i zacznij robić małe zmiany. Na początek myślę, że MVC będzie najprostszy.

1
wydra__ napisał(a):

problem mam w tym, że przeczytałem, że COŚ TAM (...) Czy do tego musze znać jakieś design patterny, czy jako początkujący nie powinienem się tym na ten moment przejmować i robić tak żeby po prostu "działało"?

To wygląda, jakbyś coś wyczytał o jakiejś zasadzie, ale bez zrozumienia, dlaczego należy ją stosować. Zasada jest mądra, ale stosowanie nawet mądrej zasady bez zrozumienia może poskutkować bezsensownym cargo cultem.

Natomiast GUI oddziela się od logiki np. po to, żeby kod był bardziej czytelny i wygodniej było w nim czynić zmiany. Jeśli pomieszasz wszystko, to żeby coś zmienić w logice, będziesz musiał się przeciskać przez masę kodu związanego z GUI.

Oddziela się GUI od logiki również po to, żeby łatwo było zmienić w razie czego poszczególne części składowe programu.

chcę teraz za pomocą Swinga dodać GUI

Np. może za miesiąc będziesz chciał użyć innej biblioteki do GUI. Jeśli masz pomieszane GUI z logiką, to żeby zmienić, wszystko będziesz musiał zaorać. Jeśli natomiast masz to oddzielone, to po prostu podłączysz pod gotową logikę inną bibliotekę do GUI. Nawet 2 wersje interfejsu naraz będziesz mógł mieć, które korzystają z tej samej logiki.

Albo możesz użyć tej samej logiki, żeby zrobić aplikację konsolową, która w ogóle nie ma GUI, tylko komunikuje się za pomocą tekstu.

Łatwiej też napisać testy jednostkowe do samej logiki (bez GUI).

Albo możesz wystawić logikę na serwerze.

itp.

2

Ale jak ta logika wygląda? Musiałeś przecież jakoś sprawdzić czy działa. Jeśli jest tam jakiś interfejs konsolowy to najpierw musisz go oddzielić od reszty.

1

Trudno przy tysiącletniej i stabilnej pod względem zasad grze w szachy napalać sie na wszystkie postulaty "jak powinniśmy projektować". Nie zajdzie zmiana wymagań biznesowych itd.

Trochę inaczej, gdyby akurat szachy (albo konkretnie 8 polowe) były jedną z implementacji "gry na planszy N*N".
Przemyśl sobie w ten sposób, GUI ma się nadać tak samo dobrze do warcabów czy jakiś innych gier (nie znam się na tym)
jeśli taki eksperyment myślowy wyjdzie OK, tzn masz dobrze zaprojektowany podział GUI / backend.

1
ZrobieDobrze napisał(a):

Trudno przy tysiącletniej i stabilnej pod względem zasad grze w szachy napalać sie na wszystkie postulaty "jak powinniśmy projektować". Nie zajdzie zmiana wymagań biznesowych itd.

GUI może się zmienić.

Sposób implementacji logiki też może się zmienić (np. "zrób wsparcie dla multiplayera" czy "zmień kod związany z AI gry" albo "napisz testy"). Więc już jak sama logika będzie napisana zbyt sztywno, to nie będzie się dało tych zmian zrobić. Bo np. możliwe, że ktoś napisze tak logikę, że nie dało się będzie w łatwy sposób dodać multiplayera albo zmienić AI czy napisać testów.

Przemyśl sobie w ten sposób, GUI ma się nadać tak samo dobrze do warcabów czy jakiś innych gier (nie znam się na tym)

No i to jest dobry case. Warcaby mają taką samą planszę, ale inne zasady.

0

Dzięki wszystkim za odpowiedzi. Mam teraz takie bardziej konkretne pytanko, tak sobie myślę, że gdybym zrobił sobie interfejs, który będzie implementowany przez moją logikę i np. potem dam go jako pole w mojej klasie reprezentującej GUI. I to GUI będzie operowało na tym interfejsie. Czy to już jest jakieś zrealizowanie pewnej separacji między logiką a GUI oraz zrealizowanie tego co @ZrobieDobrze napisał?

8

@wydra__: baaaardzo prymitywne rozwiązanie, ale powinno ci wystarczyć, to wzorzec obserwatora. GUI obserwuje zmiany w backendzie i odpowiednio się aktualizuje. Przy czym zapomnij klasie Observable i interfejsie Observer z pakietu java.utils, bo one są od dawna oznaczone jako przestarzałe.

Trochę bardziej złożone, ale też ładnie działające to użycie EvenBus np. z Guavy. Przy czym to też jest dość proste rozwiązanie i współcześnie istnieją lepsze, bardziej złożone narzędzia, ale nie są ci potrzebne.

0

Jeszcze raz dzięki wszystkim za odpowiedzi :)

0
Koziołek napisał(a):

@wydra__: baaaardzo prymitywne rozwiązanie, ale powinno ci wystarczyć, to wzorzec obserwatora. GUI obserwuje zmiany w backendzie i odpowiednio się aktualizuje. Przy czym zapomnij klasie Observable i interfejsie Observer z pakietu java.utils, bo one są od dawna oznaczone jako przestarzałe.

Trochę bardziej złożone, ale też ładnie działające to użycie EvenBus np. z Guavy. Przy czym to też jest dość proste rozwiązanie i współcześnie istnieją lepsze, bardziej złożone narzędzia, ale nie są ci potrzebne.

+1

Można sobie postawić pytanie, ile grup tych observerów (ile interface powołać) ...

trochę inny (np o innej liście argumentów) do reakcji "co zrobił przeciwnik" (żywy czy maszyna)
trochę inny "czy ruch jest wg przepisów możliwy", bo rozumiem w wariancie rozdzielonym nie GUI ma tę wiedzę a silnik (wymienny szachy 8 polowe/ warcaby/ inne), inny moment ewaluacji tego zdarzenia (przed, a nie po)

Pewnie jakby postawić sobie dzban kawy, wymyślił by jeszcze jedną grupę albo wiecej.

3
wydra__ napisał(a):

Mam napisaną logikę dla szachów, chcę teraz za pomocą Swinga dodać GUI do tej gry, tylko trochę nie wiem jak się za to zabrać. Znam podstawy Swinga, Eventy itd, problem mam w tym, że przeczytałem, że logika powinna być niezależna od GUI. Jak to zrealizować w Javie? Czy do tego musze znać jakieś design patterny, czy jako początkujący nie powinienem się tym na ten moment przejmować i robić tak żeby po prostu "działało"? Jeśli ktoś ma jakieś materiały do nauki powiązane z tym tematem to proszę o podlinkowanie.

Jeśli zrobisz, po prostu żeby "działało" to spora szansa że zrobisz logikę i GUI powiązane ze sobą, i nie będzie żadnej niezależności.

Co do tego jak to się powinno zrobić, to powinieneś najpierw napisać grę w szachy bez żadnego interfejsu użytkownika, mogłoby to wyglądać jakoś

var board = new Board();
var piece = board.getPiece(4, 2); // pawn at E2
piece.moveTo(4, 4); // move to E4

Tylko upewnij się że w tym kodzie nie ma żadnego wyświetlania niczego, żadnego Swinga, żadnych System.out.println(), nic takiego. Interfejs ma być wystawiony w języku programowania. Jeśli uda Ci się to zrobić, to praktycznie już wygrałeś. Kod jaki tam powinieneś napisać to np: jakie ruchy są legalne jakie nie, jak zachowują się figury kiedy się ruszają, jak zrobić roszade, kiedy jest szach/kiedy nie, kiedy gra się kończy, kiedy pionek awansuje na figure, kiedy można przesunąć figurę żeby nie odsłonić szacha, logika też powinna pamiętać czy wierze się ruszyły na potrzeby roszady, etc.

Następnie należy napisać GUI, które korzystałoby z tych klas.

wydra__ napisał(a):

Dzięki wszystkim za odpowiedzi. Mam teraz takie bardziej konkretne pytanko, tak sobie myślę, że gdybym zrobił sobie interfejs, który będzie implementowany przez moją logikę i np. potem dam go jako pole w mojej klasie reprezentującej GUI. I to GUI będzie operowało na tym interfejsie. Czy to już jest jakieś zrealizowanie pewnej separacji między logiką a GUI oraz zrealizowanie tego co @ZrobieDobrze napisał?

Są dwa sposoby żeby to zrobić.

  • Jeden, to taki że po każdym ruchu GUI odpytywałoby logikę gdzie są wszystkie figury na planszy (np po ruchu iterowałoby po całej planszy i sprawdzało gdzie są figury)
  • Albo możesz do tego użyć polimorfizmu (robiąc coś na kształt Observer pattern albo Decorator pattern)

Oba są okej: polimorfizm i polling, oba dadzą Ci niezależność logiki od GUI. Polimorfizm byłby chyba jednak bardziej "fajny" do użycia.

ZrobieDobrze napisał(a):

Trudno przy tysiącletniej i stabilnej pod względem zasad grze w szachy napalać sie na wszystkie postulaty "jak powinniśmy projektować". Nie zajdzie zmiana wymagań biznesowych itd.

Zajdzie, jak będzie chciał dodać inne tryby gry, jak np na chess.com: fog of war, customowe figury, i dużo innych trybów.

1
Riddle napisał(a):

Co do tego jak to się powinno zrobić, to powinieneś najpierw napisać grę w szachy bez żadnego interfejsu użytkownika, mogłoby to wyglądać jakoś

var board = new Board();
var piece = board.getPiece(4, 2); // pawn at E2
piece.moveTo(4, 4); // move to E4

Tylko upewnij się że w tym kodzie nie ma żadnego wyświetlania niczego, żadnego Swinga, żadnych System.out.println(), nic takiego. Interfejs ma być wystawiony w języku programowania.

Na tym etapie warto zacząć pisać testy i wtedy to testy będą twoim interfejsem użytkownika.

np. coś takiego możesz dopisać (w pseudokodzie):

board = create Board
piece = board.getPiece(4, 2) # pawn at E2
piece.moveTo(4, 4)

# testujemy, czy plansza odzwierciedliła nasze zmiany:
cleanBoard = create Board
for y in 0..8:
   for x in 0..8:
       piece = board.getPiece(x, y)
       if (x, y) == (4, 2) assert piece is null # zabraliśmy stąd piona
       else if (x, y) == (4, 4) assert piece is Pawn 
       else assert piece is cleanBoard.getPiece(x, y) 

Wtedy pisząc kolejne przypadki testowe i kolejne assercje (coś ma być prawdą, a jak nie to test się wywali i pokaże komunikat) sprawdzasz sobie, jak działa program. Więc możesz logikę pisać bez żadnego GUI. Just like that.

0
LukeJL napisał(a):
Riddle napisał(a):

Co do tego jak to się powinno zrobić, to powinieneś najpierw napisać grę w szachy bez żadnego interfejsu użytkownika, mogłoby to wyglądać jakoś

var board = new Board();
var piece = board.getPiece(4, 2); // pawn at E2
piece.moveTo(4, 4); // move to E4

Tylko upewnij się że w tym kodzie nie ma żadnego wyświetlania niczego, żadnego Swinga, żadnych System.out.println(), nic takiego. Interfejs ma być wystawiony w języku programowania.

Na tym etapie warto zacząć pisać testy i wtedy to testy będą twoim interfejsem użytkownika.

Tak, warto by zacząć pisać testy, ale to nadal nie byłby interfejs użytkownika. To nadal byłby interfejs programistyczny. Być może chodzi Ci o to że testy byłyby klientem tego interfejsu? Jeśli tak, to masz rację - byłyby. Ale interfejs użytkownika to coś czego może użyć faktycznie użytkownik nawet jak nie umie programować, GUI, webUI albo CLI.

LukeJL napisał(a):

Wtedy pisząc kolejne przypadki testowe i kolejne assercje (coś ma być prawdą, a jak nie to test się wywali i pokaże komunikat) sprawdzasz sobie, jak działa program. Więc możesz logikę pisać bez żadnego GUI. Just like that.

To jest dobra rada.

0

Nie ma powodu, żeby UI pobierało jakieś pionki z jakichś pozycji na planszy. W ogóle nie powinno go to interesować. UI ma wysłać do gry wybrany przez gracza ruch, a potem wyświetlić planszę.

// gdzieś w kodzie obsługującym UI
var currentBoard = _game.Move(e4, e5);
RefreshBoard(currentBoard);

// w testach analogicznie
var currentBoard = gameUnderTest.Move(e4, e5);
currentBoard.SholdBeSameAs(expectedBoardAfterFirstMove);
2
somekind napisał(a):

Nie ma powodu, żeby UI pobierało jakieś pionki z jakichś pozycji na planszy. W ogóle nie powinno go to interesować. UI ma wysłać do gry wybrany przez gracza ruch, a potem wyświetlić planszę.

Ale jakoś musi tą plansze z pionkami dostać. Albo jako uporządkowaną liste, albo jako id, albo jako tablica dwuwymiarowa, albo jako jakaś maska bitowa, albo jeszcze jakoś inaczej.

Poza tym, UI musi jakoś zainicjować ruch, czyli powiedzieć: rusz pionek z pozycji x/y na i/k, ewentualnie powiedzieć który pionek ma się ruszyć gdzie. Tak czy tak, żeby wykonać ruch, UI musi wskazać figurę która ma się ruszyć, jakoś. Potem oczywiście ma wyświetlić całą plansze, i do tego nie potrzebuje konkretnej figury, tylko może wszystkie na raz, tak jak mówił @somekind.

Jest to jednak szczegół implementacyjny.

Celem przykładu nie było to jak to konkretnie ma wyglądać, tylko to że logika nie powinna wystawiać żadnego UI.

0
Riddle napisał(a):

Ale jakoś musi tą plansze z pionkami dostać.

Tak, funkcja Move ją zwraca. Czy to będzie jakaś lista, czy coś innego, nie ma znaczenia - UI musi umieć ją wyświetlić.

Poza tym, UI musi jakoś zainicjować ruch, czyli powiedzieć: rusz pionek z pozycji x/y na i/k, ewentualnie powiedzieć który pionek ma się ruszyć gdzie. Tak czy tak, żeby wykonać ruch, UI musi wskazać figurę która ma się ruszyć, jakoś.

Nie, wystarczy pozycja startowa i początkowa. To obiekt gry ma wiedzieć, co tam stoi, a nie UI.

0

Najprościej jest mieć gdzieś zdefiniowany aktualny stan planszy, gdzie gui nasłuchuje zmian stanu i się odświeża jeśli trzeba. Gdy gracz wykona ruch, powinien być wysyłany odpowiedni event, który spowoduje wygenerowanie nowego stanu.

1

Ja to widzę tak:

• Albo GUI po każdej zmianie planszy dostaje dane całej planszy naraz i wyświetla całą planszę za jednym zamachem. To się przydaje, jeśli GUI jest pisane w stylu bardziej "immediate" albo deklaratywnym - tj. kiedy kod odpowiedzialny za GUI nie trzyma obiektów wizualnych, tylko po prostu wyświetla stan planszy w danej chwili

Czyli
Logika ---> przekazuje Planszę ---> GUI ------> przekazanie informacji do renderera na podstawie Planszy
pseudokod

for each tile in board: 
   if tile not empty:
      render(getGraphics(tile), tile.x * TILE_SIZE, tile.y * TILE_SIZE)

• Albo GUI dostaje delty, tzn. dostaje informacje, że pionek z B2 ruszył się na B3. Wtedy kod odpowiedzialny za GUI będzie nanosił analogiczne zmiany w obiektach wizualnych widoku (zakładam, że wtedy GUI używa jakiegoś obiektowego API do grafiki w stylu "retained"). Ew. jak świat gry jest duży i jako technika optymalizacji, ale to nie dotyczy tego wątku, bo mówimy o prostej planszy w szachy.

Czyli
Logika ---> Przekazuje listę zmian --> GUI -------> iteracja po liście zmian i aktualizacja obiektów wizualnych

for each delta in deltas: 
    sprite = sprites.find(delta.figureId)
    sprite.x = delta.newX * TILE_SIZE;
    sprite.y = delta.newY * TILE_SIZE;

Przy czym zakładam, że:

  • logika gry zawiera tylko suche semantyczne informacje puste pole / pionek / wieża i że logika nie trzyma żadnych obrazków, sprajtów czy przycisków. I logika nie myśli o pikselach, a jedynie o szachownicy
  • dane związane z wizualnością (np. grafika do szachów, pozycja w pikselach sprajta) są trzymane/obsługiwane tylko i wyłącznie przez GUI.
0
LukeJL napisał(a):

Ja to widzę tak:

• Albo GUI po każdej zmianie planszy dostaje dane całej planszy naraz i wyświetla całą planszę za jednym zamachem. To się przydaje, jeśli GUI jest pisane w stylu bardziej "immediate" albo deklaratywnym - tj. kiedy kod odpowiedzialny za GUI nie trzyma obiektów wizualnych, tylko po prostu wyświetla stan planszy w danej chwili

• Albo GUI dostaje delty,

Troche inaczej rozkładam akcenty.

Np GUI musi pomigać ramką pola, (hipotetycznie dać beep) jeśli ruch jest nielegalny wg przepisów. Nie ma to nic do "statycznego" stanu, ale jeśli sobie wprowadzimy kategorię "stan dynamiczny" ...

Dlatego listenery we frameworkach Javowsich choć podobne, różnią się typami argumentów, to właśnie ta idea.

W tym przypadku masz tylko jednego. Obserwuje stan planszy. Jeżeli stan zmieni się, plansza rozgłasza nowy stan, a GUI tylko go renderuje. Zero logiki gry. — Koziołek 2023-07-29 21:19

No właśnie niekoniecznie. Oprócz tego "głównego stanu" są różne staniki

2
ZrobieDobrze napisał(a):

Troche inaczej rozkładam akcenty.

Np GUI musi pomigać ramką pola, (hipotetycznie dać beep) jeśli ruch jest nielegalny wg przepisów.

No ale informacja o tym, czy dany ruch jest dozwolony, też powinna być sprawdzana po stronie logiki. GUI nie potrzebuje znać zasady gry w szachy, wystarczy, że będzie wiedziało, że logika gry uważa proponowany ruch za niedozwolony

ZrobieDobrze napisał(a):

Nie ma to nic do "statycznego" stanu, ale jeśli sobie wprowadzimy kategorię "stan dynamiczny" ...

chociaż stan samej czynności migania (jakiś timer, który animuje kolor) może być już procesowany przez GUI, bo to już nie jest logika gry.

Albo np. jeśli GUI chce odpalać animacje, jak się rusza figurą. To niech sobie trzyma dane z dokładnością piksela, ale już od strony logiki figura szachowa zawsze będzie na jakimś polu, nawet jeśli GUI będzie to animować i wizualnie będzie stan, gdzie figura będzie między polami (który ten stan będzie miał sens dla GUI, ale nie dla logiki).

0

No chyba gui może sobie animować co chce, ważne żeby poinformować logikę, że wykonano taki a nie inny ruch (czy była tam jakaś animacja czy nie to nie ma znaczenia, ważne że wykonano ruch). Logika decyduje co z tym ruchem zrobić, albo odpowiedzieć kolejnym ruchem, co gui ma narysować albo kazać wycofać ruch bo był niedozwolony.

Co to znaczy "stan dynamiczny"? Stan powinien być immutable, był jakiś stan, jak coś się zmieni to logika emituje nowy stan. Jako bonus można sobie zapisywać historię stanów żeby potem można było przejrzeć historię gry

1
gajusz800 napisał(a):

Co to znaczy "stan dynamiczny"?

Próba przesunięcia (którą backend odrzuca ew akceptuje), to też jest "jakiś" choć niższej kategorii stan

0

Chyba nie bardzo rozumiem. Jeśli gracz wykona niedozwolony ruch, to backend powinien ustawić stan błędu z niezmienionym układem planszy. Wtedy gui dostanie 2 informacje: że wykonano niedozwolony ruch i standardowo stan planszy (w tym przypadku taki, jaki był przed wykonaniem błędnego ruchu).

GUI nie powinno wiedzieć co backend akceptuje. Gui nasłuchuje stanu planszy i czy jest błąd czy nie ma. Wtedy gui jest niezależne od stanu.

2

GUI nie powinno wiedzieć co backend akceptuje.

Zależy, jak jest zrobione sterowanie - czy jakimiś komendami, czy graficznie/wizualnie - ale prawdopodobnie druga opcja, więc i tak GUI musi być zaangażowany w proces wykonania ruchu przez usera. Inaczej - w jaki sposób chcesz przekazać do logiki polecenie od usera wykonania ruchu?

Ja bym to widział tak (to moja wizja, możecie się nie zgadzać), że GUI odczytuje proponowany ruch, potem przekazuje informację do logiki w stylu wieża z B3 na W309 i czeka na odpowiedź. Jeśli dostanie zgodę, to wykonuje animację ruchu, który zlecił użytkownik. Jeśli nie, to animuje odmowę - np. coś zapala na czerwono, ryzuje jakiś krzyżyk, cofa figurę na miejsce pierwotne itp.

0
cerrato napisał(a):

w jaki sposób chcesz przekazać do logiki polecenie od usera wykonania ruchu?

To już są szczegóły implementacyjne. Ogólnie, gui ma robić tylko 2 rzeczy: nasłuchiwać stanu i wysyłać eventy do backendu, np event o wykonaniu ruchu. Gdy backend otrzyma taki event to zrobi 2 rzeczy:

  • jeśli ruch jest dozwolony, to najpierw ustawi stan planszy uwzględniający ten ruch, co spowoduje wyrenderowanie planszy z pionkiem przesuniętym przez użytkownika. Jeśli błędny ruch, to nowy stan planszy będzie zawierał stan pionków taki jak przed wykonaniem ruchu i flagę błędu
  • zareaguje na ruch, czyli najpierw obliczy jaki ruch wykonać, a potem ustawi stan planszy odpowiednio.

To, co jest aktualnie wyświetlone przez gui jest tylko odzwierciedleniem aktualnego stanu. Gui ma czekać tylko na zmianę stanu (a w zasadzie nowy stan z nowymi informacjami do wyświetlenia).

Oczywiście znajdą się krytycy scentralizowanego stanu i zaraz wyliczą tu jego wady, ale ja wolę to niż takie podejście jak twoje czyli jakąś komunikację w 2 strony, nie wiadomo jak i kiedy.

Ale największą wadą twojego podejścia są takie rzeczy:

  • jak umożliwisz użytkownikowi cofnięcie ruchu (tu wystarczy przywrócić poprzedni stan, a poprzedni stan może być trzymany w pamięci lub na dysku)
  • jak pokażesz historię gry - tu wystarczy zapisać historię stanów w pamięci lub lokalnej bazie danych i będzie można od razu cofnąć się do dowolnego stanu planszy z historii
0
gajusz800 napisał(a):
cerrato napisał(a):

w jaki sposób chcesz przekazać do logiki polecenie od usera wykonania ruchu?

To już są szczegóły implementacyjne. Ogólnie, gui ma robić tylko 2 rzeczy: nasłuchiwać stanu i wysyłać eventy do backendu, np event o wykonaniu ruchu.

Już daj spokój z tymi eventami. Event to jest nic innego jak tylko kolejna warstwa abstrakcji na observer.

2

To już są szczegóły implementacyjne. Ogólnie, gui ma robić tylko 2 rzeczy: nasłuchiwać stanu i wysyłać eventy do backendu
[...]
To, co jest aktualnie wyświetlone przez gui jest tylko odzwierciedleniem aktualnego stanu. Gui ma czekać tylko na zmianę stanu (a w zasadzie nowy stan z nowymi informacjami do wyświetlenia).''

Super, ale totalnie pomijasz bardzo ważny element, jakim jest fizyczne wykonanie ruchu przez gracza.
To, o czym piszesz miałoby sens, jakby to była jakaś gra przez net, a polecenia ruchu przychodzą z jakiejś innej maszyny. Wtedy rzeczywiście mamy prosty schemat - logika/kontroler (nazwijmy to jakkolwiek - chodzi o ten element, który wysyła polecenia do GUI i modyfikuje stan planszy) otrzymuje skądś-tam polecenie przesunięcia pionka, a następnie przekazuje do GUI polecenie w stylu animuj wieżę z C4 na B7. A potem GUI, w odpowiedzi na to żądanie, wykonuje stosowną animację.

Ale tutaj mamy jeden ważny (bardzo ważny) aspekt, którego nie uwzględniasz - gracz musi jakoś fizycznie chwycić kursorem pionek i go przeciągnąć po planszy. Czy będzie to zaznaczenie pionka, który się podświetli, a potem pola, na które ma się przesunąć, czy będzie go fizycznie przesuwać po planszy - to kwestia wtórna. Ale chodzi o to, że GUI nie jest tak, jak Ty chcesz (albo - może masz coś innego na myśli i po prostu nie możemy się dogadać) takim bezmyślnym realizatorem poleceń otrzymanych od kontrolera, tylko musi brać czynny udział w interakcji z userem - nie tylko przekazuje informacje w postaci rysowania planszy z nowym układem, ale także odbiera od usera propozycje/żądania ruchu. Także - komunikacja musi być dwustronna. Kilka przykładowych scenariuszy:

  • user zaznacza pionek, GUI przesyła do kontrolera zapytanie jakie pola są dostępne do wykonania ruchu dla tego pionka/figury i podświetla je na zielono
  • user chwyta pionek i przesuwa po planszy. Podczas przesuwania GUI na bieżąco odpytuje kontroler na zasadzie czy pionek z E2 może stanąć na polu C5 i w zależności od otrzymanej odpowiedzi zapala pole pod pionkiem na czerwono/zielono
  • user stawia pionek na dowolnym polu (brak asysty podczas wykonywania ruchu), po czym GUI pyta kontroler, czy taki ruch jest dozwolony i albo pionek tam zostaje, albo jest animacja powrotu.

Niezależnie od wybranego scenariusza (albo jakiegokolwiek innego, który byś zastosował) - GUI nie jest tylko ekranem do wyświetlania, ale musi także zbierać input od gracza i komunikować się z kontrolerem. Także - nie zgadzam się z tym, co napisałeś wcześnie - GUI nie powinno wiedzieć co backend akceptuje. Gui nasłuchuje stanu planszy i czy jest błąd czy nie ma. Wtedy gui jest niezależne od stanu.. Chyba, że masz inną wizję i coś innego niż GUI ma odpowiadać za przyjmowanie ruchów/poleceń od gracza.

0

Dalej nie rozumiem problemu. Przytrzymanie pionka to może być też event do backendu, a stan może zawierać pola, które trzeba podświetlić na zielono, czerwono czy jak tam chcesz.

Oddzielenie gui od logiki właśnie na tym polega, że gui nie ma żadnej logiki i jest właśnie tylko wyświetlaczem, który można podłączyć albo odłączyć. Jeśli gui wie co wyświetla to znaczy że zawiera logikę.

Cały input od gracza jest zapisany w stanie aplikacji. Jak to zostanie zaprezentowane to już nie ma znaczenia. Właśnie o to chodzi, żeby to oddzielić.

0

OK, dobrze - a w jaki sposób w takim razie chcesz przyjmować polecenia od gracza?

To, jak ja to rozumiem/jak widzę nadal nie jest trzymaniem logiki w GUI - bo on nie zna zasad gry. Jedynie reaguje na działania usera:

  • gracz chwyta pionek, więc to "to coś chwycone" (GUI nie musi wiedzieć, czym dany pionek jest, ani jakie ma dozwolone ruchy) zostaje podświetlone/powiększone/jakoś zaznaczone że jest aktywne. GUI nie wie, co ten pionek może zrobić. Z kolei logika nie wie, że pionek jest chwycony
  • gracz przesuwa pionek po planszy. GUI nie rozumie zasad działania pionka, po prostu odpytuje logikę na zasadzie czy to coś, co stało wcześniej na F2 może stanąć na G5 i w reagując na otrzymaną odpowiedź ma wykonać ruch albo jakoś zasygnalizować błąd
  • logika/kontroler nie ma pojęcia, w jaki sposób są animowane ruchy, jak wyglądają figury - jednie pamięta stan planszy oraz odpowiada na otrzymane pytania z GUI. Ale nadal - te pytania to nie jest przeniesienie logiki (nawet jej części) do GUI, bo są to jednie pytania czy takie coś może tutaj stanąć
  • GUI nie zna zasad gry, nie posiada żadnej logiki, jedynie mechanizm, który bezmyślnie odpytuje backend na zasadzie czy to coś może tutaj stanąć i działa w reakcji na otrzymaną odpowiedź. Żadne myślenie ani przetwarzanie tutaj się nie odbywa, GUI jest debilem ;)

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