Impedisci le dipendenze circolari senza introdurre la libreria intermediari

1

TL; DR sotto

Sto lavorando su un server di gioco (in Java, ma quella parte è meno importante) e ho deciso di suddividere la logica del server dalla logica del motore; in parte perché si trovano in due diversi domini logici. Il server interagirà direttamente con il client e gestirà l'aspetto della rete delle cose, mentre il motore gestirà gli eventi di gioco. Vorrei che fossero i più distinti possibile, in modo che il motore non si occupi di pacchetti e reti, e il server non si occupi del movimento del giocatore o degli eventi di gioco. Tuttavia, questi due dovranno rimanere accoppiati perché, sebbene si trovino in domini diversi, fanno parte dello stesso processo.

Ad esempio, quando l'utente avvia il proprio client e si connette al server, il server deve inviare un messaggio al motore per creare una nuova sessione di gioco. Il motore quindi inizializza il mondo, aggiunge il giocatore ad esso e registra gli ascoltatori di eventi in-game appropriati. A questo punto, il motore dice al server che il mondo è pronto. Il server invia quindi un client di messaggi per caricare risorse (immagini, dati del paesaggio, testo, ecc.), Registra i gestori di pacchetti e tutto è pronto per essere riprodotto.

Dobbiamo anche prenderlo dall'altra parte. Se il giocatore disconnette il proprio client o si disconnette, il server è il primo a sapere di questo cambiamento di stato. La sessione del server può rimanere attiva, nel caso in cui si tratti solo di un leggero rallentamento della rete, ma alla fine, deve dire al motore di gioco di eseguire qualsiasi operazione di pulizia.

Possono esserci molte implementazioni del motore e del server. Ad esempio, possono essere in esecuzione nella stessa istanza JVM, potrebbero trovarsi su diverse istanze, utilizzare i canali socket per comunicare tra loro, oppure potremmo avere il motore su un altro server, usando qualcosa come un servizio REST per comunicare i messaggi tra loro.

TL; DR

Che tipo di schemi di progettazione o strategie posso utilizzare per inviare messaggi tra due componenti accoppiati, che dovrebbero rimanere il più separati possibile, senza introdurre una libreria intermediario?

    
posta Zymus 21.09.2015 - 04:54
fonte

1 risposta

2

Potresti definire le interfacce, e le loro classi di implementazione vengono utilizzate come ascoltatori. Guarda il Pattern di progettazione degli osservatori .

Le interfacce ti costringono anche a pensare in termini di "servizi", quindi sarebbe relativamente facile implementare diverse versioni del tuo server e motore, e usare sempre le interfacce per astrarre i loro dettagli di implementazione.

Ad esempio, immagina di voler implementare una partita a scacchi. Crea le seguenti interfacce (i nomi forniti sono a scopo di chiarezza):

  • ServerInterface : rappresenta un server per un client. Deve contenere metodi come addClient(ClientInterface) , che consente di connettere un nuovo client; move(Piece,Position) , per spostare un pezzo sulla scacchiera; %codice%; ecc. Un LocalServer è probabilmente l'implementazione più semplice di questa interfaccia, poiché ogni chiamata al server corrisponderà direttamente a una chiamata a un metodo server. Un TCPServer inoltra in modo trasparente la chiamata tramite TCP, in modo che dal punto di vista del cliente non ci saranno differenze comportamentali con un server locale.
  • addWatcher(WatcherInterface) : rappresenta un client per un server. Deve contenere metodi come ClientInterface per definire il server che deve essere chiamato quando il client suona; setServer(ServerInterface) per informare il cliente che un altro cliente ha lasciato il gioco; ecc. Ancora una volta, un LocalClient che implementa questa interfaccia terrà semplicemente in considerazione i messaggi ricevuti, mentre TCPClient inoltrerà i messaggi tramite TCP e MailClient invierà un'e-mail al client e attenderà una risposta (se richiesta) dallo stesso supporto.
  • clientLeft(ClientInterface) : rappresenta tutti i mezzi di comunicazione con il motore. In genere, avrai metodi per chiedere se una mossa è accettabile o meno, ecc. Puoi immaginare un LocalEngine che implementa le tue regole, o un DistantEngine che usa un'API REST sul web per subappaltare questa parte del tuo sistema.
  • EngineInterface : le classi che implementano questa interfaccia sono interessate solo ad osservare il gioco. Il tuo server comunicherà ogni spostamento a tali classi. Un WatcherInterface reagirà ai messaggi inviati scrivendo le mosse in un file. Una Logger rappresenterà graficamente lo stato attuale del gioco. Ecc.

Con questa soluzione, i componenti sono altamente disaccoppiati, perché ogni parte sa solo che può essere collegata a zero, una o molte altre parti, senza specificare come si comportano effettivamente queste parti. Ogni parte si basa solo su un piccolo insieme di interfacce facili da mantenere.

Nel tuo codice, avrai bisogno di un po 'di colla per instanziare le lezioni concrete. Ad esempio, nel tuo cliente, il giocatore selezionerà un gioco TCP, quindi inserirà un indirizzo IP e farà clic su "Partecipa". Il codice associato alla GUI deve quindi creare un TCPServer e presentarlo come una ServerInterface al client associato alla GUI. Il client non dovrebbe mai sapere che è connesso a un server TCPS, quindi non ci sarà nulla di specifico per questa particolare implementazione utilizzata dal client. Sul lato server, l'applicazione TCP ascolterà le connessioni in entrata. All'avvio di una connessione, l'applicazione crea un TCPClient e lo presenta al server esistente come ClientInterface, che si occuperà di ignorarlo è un client TCP.

    
risposta data 21.09.2015 - 08:28
fonte

Leggi altre domande sui tag