Skip to content

Design di dettaglio

Organizzazione del codice

Il codice è complessivamente organizzato in tre package principali:

Program package diagram

  • model: contiene le classi che rappresentano il modello dei dati e la logica di business dell'applicazione.
    • cell: implementa in maniera completa una cella dell'area della simulazione, definendone le sue proprietà e la fisica.
    • dsl: definisce un linguaggio specifico per la creazione facilitata di mappe con determinate percentuali d'acqua nelle celle.
    • grid: rappresenta una griglia di elementi, gestendo la loro disposizione. È utilizzata per rappresentare sia la mappa delle celle che quella del meteo.
    • logic: contiene la logica della simulazione, come la gestione del flusso d'acqua tra le celle, l'applicazione degli eventi metereologici alla mappa, la generazione della mappa ed il controllo del tempo.
    • report: gestisce la generazione di report della simulazione ed il calcolo delle statistiche.
    • state: definisce lo stato della simulazione, include la definizione dei parametri dinamici della simulazione e di quelli statici.
    • store: gestisce la persistenza dei dati della simulazione durante il suo svolgimento.
    • weather: implementa i vari tipi di condizioni metereologiche.
  • controller: contiene le classi che gestiscono la logica di controllo e coordinano l'interazione tra il modello e la vista.
    • engine: si occupa del calcolo dello stato della simulazione ad ogni passo temporale e ne definisce la condizione di terminazione.
  • view: contiene le classi che gestiscono l'interfaccia utente e l'interazione con esso.
    • component: definisce i componenti grafici riutilizzabili nell'interfaccia utente e quelli complessi.
    • config: contiene la configurazione dell'interfaccia utente.
    • navigation: definisce la navigazione tra le diverse schermate dell'applicazione.
    • panel: implementa i pannelli dell'interfaccia utente.
    • util: contiene classi di utilità, principalmente per la gestione delle risorse grafiche.

Struttura dei package

In questa sezione vengono descritte per ogni package le scelte di design più rilevanti. FloodSim adotta un'architettura modulare e fortemente aderente ai principi SOLID, con particolare attenzione alla separazione delle responsabilità e all'estendibilità.

Model

Il model è stato progettato tenendo in considerazione la sua immutabilità come principio cardine. Pertanto ogni suo componente, partendo dalle entità base fino a quelle più complesse, rispetta questo aspetto. Ciò, oltre ad essere una best-practice in questo ambito, riduce i rischi di effetti collaterali indesiderati, facilitando inoltre il testing e la gestione dello stato della simulazione.

Di seguito viene mostrata una panoramica dei principali componenti del model e delle loro relazioni.

Il componente fondamentale del model è la Cell, che rappresenta una singola unità della griglia della simulazione. Essa viene creata sfruttando il pattern Factory, che consente di creare diverse tipologie di celle in maniera semplificata, in modo flessibile e nascondendone i dettagli implementativi.

Internamente le celle hanno una serie di Obstacle ed una determinata CellPhysics, sviluppata mediante il pattern Strategy, questo aspetto verrà approfondito in seguito. Un altro elemento base del model è il Weather, che rappresenta le condizioni meteorologiche che influenzano lo sviluppo della simulazione.

Le mappe della simulazione vengono rappresentate come una griglia di celle, definita nell'elemento DistanceGrid. Dovendo implementare una simulazione semplificata rispetto alla realtà, è stato scelto altresì di rappresentare le condizioni meteo in una Grid, associando quindi una condiziona metereologica differente ad ogni cella. L'elemento DistanceGrid, utilizzato per la rappresentazione delle celle della mappa, arricchisce la Grid definendo alcune operazioni aggiuntive, che verranno analizzate in seguito. La logica di generazione delle mappe di terra e del meteo è stata definita nel componente MapGenerator.

Lo stato della simulazione è rappresentato da SimulationState e contiene la griglia delle celle, quella del meteo e la percentuale di mappa inondata, calcolata dal DamageCalculator. Durante lo svolgimento della simulazione, lo stato viene salvato nel SimulationStore, che ne abilita anche la modifica.

È inoltre presente un componente GridDSL che gestisce la creazione facilitata di mappe con determinate percentuali d'acqua nelle celle.

Controller

In SimulationEngine viene definita la logica di aggiornamento dello stato della simulazione e quella per il controllo della sua terminazione. Questo componente necessita dei SimulationParams, selezionati dall'utente e pensati in modo da fare uso di opaque types e di vincoli per garantire la correttezza dei valori.

Il SimulationController è il vero core della simulazione, ed è incaricato di orchestrarne lo svolgimento nel suo complesso. La sua creazione viene gestita mediante il pattern Factory, che consente di semplificarne enormemente l'istanziazione, nascondendo i dettagli implementativi e garantendo che venga creato in uno stato consistente. Il factory fa anche uso di costrutti avanzati di Scala come summon per la Dependency Injection di SimulationStore e SimulationScheduler.

In questo package vengono definite anche SimulationSpeed e SimulationScheduler, che gestiscono la velocità della simulazione e la sua esecuzione periodica. Quest'ultimo in particolare funge da wrapper di un ScheduledExecutorService di Java, arricchendolo con la possibilità di modificare il timing di esecuzione di tutti i task in base alla velocità della simulazione, selezionata dall'utente.

Durante lo svolgimento della simulazione viene applicato il corretto meteo ad ogni cella ed è calcolato il conseguente flusso dell'acqua, rispettivamente mediante i componenti WeatherEffectService e WaterFlowService.

Raggiunta la soglia di allagamento della mappa, la simulazione termina. L'utente ha la possibilità di generare un report in formato testuale, che riassume le statistiche più rilevanti della simulazione, mediante il ReportGenerator.

Durante la simulazione, il SimulationController comunica i nuovi cambiamenti alla eventuali view registrate, mediante il pattern Observer, che garantisce comunicazioni efficienti e disaccoppiate.

View

La view è strutturata in modo da separare chiaramente i diversi aspetti dell'interfaccia utente. Nello specifico è internamente organizzata nei sotto package:

  • component: che definisce diversi componenti riutilizzabili, sia base che complessi.
  • config: la configurazione generale dell'interfaccia.
  • navigation: la navigazione tra le schermate del programma.
  • panel: i pannelli principali dell'interfaccia.
  • util: classi di utilità, principalmente per la gestione delle risorse grafiche.

UIComponent definisce i componenti grafici più comuni e fornisce metodi per crearli facilmente e con uno stile coerente.

I componenti più complessi, come CellTypeSelector, SimulationControls, SpeedToggleButton, ViewGrid e ViewGridCell sono definiti in modo da incapsulare la loro logica e presentazione, facilitandone il riutilizzo e la manutenzione. La configurazione generale dell'interfaccia è gestita in UIConfig, che definisce i colori, i font e le dimensioni degli elementi grafici.

Infine, questo package contiene anche un sistema di caching delle immagini, implementato in ImageCache, che consente di caricare e memorizzare in memoria le risorse grafiche dell'applicazione, sfruttando anche aspetti di Scala come i given.