Ho appena terminato un progetto in cui ho creato una simulazione visiva del ciclo di vita di un'istruzione ARM in un processore a ciclo singolo. Ho usato il pattern MVC in questo progetto e ho incontrato un bivio del design quando ho pensato a come avrei gestito una parte specifica dei dati passati tra il modello e la vista.
Nel mio diagramma nella vista, ho una serie di linee tracciate sui percorsi dati del processore, che evidenzierò in base al passo nel ciclo di vita ARM di cui sto parlando (ad esempio, se l'attuale step fa riferimento a due sorgenti passate all'ALU, le linee corrispondenti a quelle due sorgenti si accenderanno). Per ogni passo c'è una spiegazione testuale di ciò che sta accadendo, così come una serie di linee che vengono evidenziate.
Ecco un gif della vista in azione:
Èsemplicepassareidatitestualitrailmodelloelavistasenzaviolarel'incapsulamento,tuttavianonerosicurodicomegestireidatigrafici(lelinee).HolavoratoconunprogettosimileprimainuncorsochehoseguitoperOOP,ecièstatodettodiignorarelaviolazionedell'incapsulamentoinquestaistanza:quellocheabbiamofattoèstatocreareuninsiemedistatichesonostatipassatidallavistaalmodello,aggiornatoerinviatoallavista.
Misembrachequestasoluzionepotrebbeesseredannosainunasituazionerealeacausadiproblemidilarghezzadibandatrailservereilclient(passandooggettididatirelativamentegrandiavantieindietroperqualsiasicambiamentodistatonellavista).Inoltre,seavessiusatoquestasoluzione,avreiancheviolatountitolarebasedelpatternMVC,mantenendolavistaeilmodellocompletamenteseparati.Selavistadovessepassareisuoioggettilineaalmodello,ilmodellosaprebbeesattamentecomesonostatiimplementatiidati.
Perilmioprogettohodecisodiattenermiallepratichediprogrammazioneaccettateepreservarel'incapsulamentofacendoinmodocheleclassiconcretenelmodelloinviinooggettistringaalcontrollercherappresentaundeterminatoinsiemedilinee.Ilcontrollertradurrebbequestastringainuninsiemepre-inizializzatodisegmentidilineaeliattiverebbe(disattiverebbeancheisegmentidilineanonmenzionati).Questasoluzione,tuttavia,misembracomplicataenonsembraessereingradodiscalaremoltobeneperchéilcontrollerdovrebbecreareunnuovosetdilineeemapparloaqualsiasinuovastringapossibileinviatadalmodello.
Eccounosnippetdelmiocodicecontroller:
privateMap<String,HashSet<Line>>lineSet;publicvoidinitController(){//groupsoflineobjectsinitialized...HashSet<Line>ctrlIn=newHashSet<>();//asetoflinesthatwillbemappedtoastringctrlIn.add(instrH);ctrlIn.add(cond);ctrlIn.add(op);ctrlIn.add(funct);ctrlIn.add(rd);ctrlIn.add(instrV1);lineSets.put("CtrlIn", ctrlIn);//maps the string expected from the model to the set of lines in the view
...
}
public void updateLines(ArrayList<String> s){//called by the model to update states of line objects
activateLines(s);
}
//helper function for handling lines, called by the updateLines function above
public void activateLines(ArrayList<String> s){
for(Line l: currLine)//currline is the current set of displayed line objects
l.setVisible(false);
for(String set : s){
HashSet<Line> c = lineSet.get(set);
for(Line l : c){
currLine.add(l);
l.setVisible(true);
}
}
}
Ecco un codice dalle mie classi di implementazione nel modello:
//'step' is a ArrayList<String> that contains the set of steps in order for the instruction
step.add("Step 1: Step description here...");
//creates an ArrayList<String> object to hold all strings representing lines that should be displayed in the view (translated by the controller)
ArrayList<String> step1 = new ArrayList<>();
step1.add("CtrlIn");
//more strings added to 'step1'...
lines.add(step1);//an array of strings is added to an outer array, corresponding to step 1
Un indice tiene traccia del passo corrente e restituisce due pezzi di dati al controllore: la descrizione del passo dall'oggetto% array% arraylist, e un array di oggetti stringa (corrispondente a un insieme di oggetti linea nel view) dall'oggetto% array% arraylist. Il controller visualizza i dati testuali in una casella di testo, quindi utilizza step
per restituire l'insieme di oggetti linea mappati alla stringa restituita dal modello e attiva tutte le linee nel set.
Violare l'incapsulamento qui sembra che potrebbe funzionare meglio per il mio caso perché scalerebbe meglio. Né il controllore né le classi concrete del mio modello avrebbero dovuto fare molto lavoro pesante se fossero entrambi a bordo di ciò che stava accadendo nella vista. Potevo iniettare riferimenti degli oggetti linea nella vista alle classi concrete del mio modello. Quando la vista richiede un cambiamento di stato (passaggio successivo), il modello può semplicemente passare il set di righe da visualizzare - il controllore potrebbe saltare la traduzione della risposta dal modello e aggiornare semplicemente lo stato delle linee nella vista.
Mi sono sbagliato a optare per preservare l'incapsulamento nel mio progetto, o si potrebbe fare un'eccezione per gestire i dati in alternativa, ad esempio nel modo in cui ho descritto sopra? In definitiva, qual è il modo più corretto per gestire i dati degli oggetti di linea in questa situazione, in conformità con le pratiche di programmazione accettate?
Ho provato a limitare la quantità di codice non elaborato per risparmiare spazio: fammi sapere se esiste un codice pertinente relativo alla domanda che dovrei includere.
Grazie per qualsiasi consiglio.