Architettura pulita: che cos'è il modello di visualizzazione?


13

Nel suo libro "Clean Architecture", lo zio Bob afferma che il presentatore dovrebbe mettere i dati che riceve in qualcosa che chiama "Visualizza modello".

inserisci qui la descrizione dell'immagine

È la stessa cosa di 'ViewModel' dal modello di progettazione Model-View-ViewModel (MVVM) o è un semplice Data Transfer Object (DTO)?

Se si tratta non è un semplice DTO, come si relaziona alla vista? La vista riceve aggiornamenti da essa attraverso una relazione Observer?

La mia ipotesi è che sia più simile al ViewModel di MVVM, perché nel capitolo 23 del suo libro, Robert Martin dice:

Il lavoro di [Presentatore] è accettare i dati dall'applicazione e formattarli per la presentazione in modo che la Vista possa semplicemente spostarli sullo schermo. Ad esempio, se l'applicazione desidera visualizzare una data in un campo, consegnerà l'oggetto Presenter a Date. Il relatore formatterà quindi quei dati in una stringa appropriata e li posizionerà in una semplice struttura di dati chiamata modello View, dove la View può trovarli.

Ciò implica che la vista è in qualche modo connessa al ViewModel, invece di riceverla semplicemente come argomento di una funzione (come nel caso di un DTO).

Un altro motivo per cui penso che questo sia perché se guardi l'immagine, il Presenter usa il View Model, ma non la View. Considerando che il presentatore utilizza sia il confine di output che il DTO dei dati di output.

Se non è né un DTO né il ViewModel di MVVM, ti preghiamo di approfondire di cosa si tratta.


Penso che la risposta sia "dipende". Se si tratta di un'applicazione Web, un modello di visualizzazione è fondamentalmente un DTO perché alla fine viene serializzato come stringa HTML. Altrimenti un modello di vista è solo un oggetto specializzato per visualizzare i dati nella vista.
Greg Burghardt,

In MVVM (WPF, applicazioni WinForms) ViewModelè wrapper per Controller, Presentere ViewModelin architettura Clean dello zio Bob.
Fabio,

@Greg Burghardt - Quando ViewModel è una struttura di dati specializzata, come viene notificata la modifica delle modifiche?
Fearnbuster,

@Fabio - Se prendo correttamente il tuo significato, stai dicendo che nel modello MVVM, ViewModel è equivalente a tutti i componenti che si trovano nel gruppo più a sinistra del diagramma? Se questo è vero per l'architettura di zio Bob, allora perché elenca il controller e il presentatore separatamente?
Fearnbuster,

Penso che abbia separato i gestori di input e output in diversi oggetti / classi. In MVVM potrebbe essere Controller-> ICommande Presenter-> data-binding mechanism.
Fabio,

Risposte:


17

È la stessa cosa di 'ViewModel' dal modello di progettazione Model-View-ViewModel (MVVM)

No.

Sarebbe questo :

inserisci qui la descrizione dell'immagine

Questo ha cicli. Lo zio Bob ha accuratamente evitato i cicli .

Invece hai questo:

inserisci qui la descrizione dell'immagine

Che certamente non ha cicli. Ma ti sta chiedendo come la vista sia a conoscenza di un aggiornamento. Ci arriveremo tra un momento.

o è un semplice Data Transfer Object (DTO)?

Per citare Bob dalla pagina precedente:

Se lo desideri, puoi utilizzare strutture di base o semplici oggetti di trasferimento dati. Oppure puoi comprimerlo in un hashmap o costruirlo in un oggetto.

Clean Architecture p207

Quindi, certo, se vuoi.

Ma sospetto fortemente che ciò che ti sta veramente infastidendo sia questo :

inserisci qui la descrizione dell'immagine

Questo piccolo abuso di UML contrasta la direzione della dipendenza dal codice sorgente con la direzione del flusso di controllo. Qui puoi trovare la risposta alla tua domanda.

In una relazione di utilizzo:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

il flusso di controllo va nella stessa direzione della dipendenza dal codice sorgente.

In una relazione di attuazione:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

il flusso di controllo in genere va nella direzione opposta rispetto alla dipendenza dal codice sorgente.

Ciò significa che stai davvero guardando questo:

inserisci qui la descrizione dell'immagine

Dovresti essere in grado di vedere che il flusso di controllo non passerà mai dal Presenter alla Vista.

Come può essere? Cosa significa?

Significa che la vista o ha il suo thread (che non è così insolito) o (come sottolinea @Euforico) il flusso di controllo sta venendo alla vista da qualcos'altro non rappresentato qui.

Se è lo stesso thread, View saprà quando View-Model è pronto per essere letto. Ma se questo è il caso e la vista è una GUI, sarà difficile riverniciare lo schermo quando l'utente lo sposta mentre aspettano il DB.

Se la vista ha il proprio thread, allora ha il suo flusso di controllo. Ciò significa che per implementare ciò la Vista dovrà eseguire il polling del View-Model per notare i cambiamenti.

Poiché il relatore non sa che la vista esiste e la vista non sa che esiste il presentatore, non possono chiamarsi affatto. Non possono lanciarsi gli eventi a vicenda. Tutto ciò che può accadere è che il Presentatore scriverà sul View-Model e la View leggerà il View-Model. Ogni volta che ne ha voglia.

Secondo questo diagramma l'unica cosa condivisa da View e Presenter è la conoscenza del View-Model. Ed è solo una struttura di dati. Quindi non aspettarti che abbia alcun comportamento.

Ciò potrebbe sembrare impossibile ma può essere fatto funzionare anche se il View-Model è complesso. Un piccolo campo aggiornato è che tutta la vista dovrebbe eseguire il polling per rilevare una modifica.

Ora, naturalmente, puoi insistere sull'uso del modello di osservatore, o avere alcune cose di struttura nascoste che ti nascondono questo problema, ma ti preghiamo di capire che non è necessario.

Ecco un po 'di divertimento che ho illustrato il flusso di controllo:

inserisci qui la descrizione dell'immagine

Nota che ogni volta che vedi il flusso andare contro le direzioni che ho definito prima, quello che vedi è una chiamata che ritorna. Questo trucco non ci aiuterà a raggiungere la vista. Bene, a meno che non torniamo prima a ciò che viene chiamato il Controller. Oppure potresti semplicemente cambiare il design in modo da poter arrivare alla vista. Ciò risolve anche quello che sembra l'inizio di un problema yo-yo con Data Access e la sua interfaccia.

L'unica altra cosa da imparare qui a parte questo è che Use Case Interactor può praticamente chiamare le cose nell'ordine che desidera fintanto che chiama l'ultimo presentatore.


Grazie mille per la risposta amico, ho visto le tue risposte su varie altre domande sull'architettura pulita. Stai suggerendo che la vista controlli costantemente una bandiera, ad esempio, all'interno del modello vista per vedere se ci sono state delle modifiche? La vista dovrebbe quindi visualizzare nuovamente l'intero modello di visualizzazione o dovrei utilizzare un set di flag nidificati per indicare quali dati sono stati modificati?
Fearnbuster,

La vista non deve eseguire costantemente il polling. Ad esempio il web 1.0 esegue il polling solo quando l'utente preme ricaricare. Il polling è costantemente una decisione di progettazione che dovrebbe considerare le reali esigenze degli utenti. Sto solo dicendo che è possibile. Il punto di un campo di aggiornamento è velocizzare il rilevamento di un aggiornamento. Necessario solo se il modello di visualizzazione è complesso. Considera anche cosa succede se la vista legge mentre il presentatore è a metà dell'aggiornamento.
candied_orange,

Ok, grazie mille per l'aiuto. Se / quando segui questa architettura, è questa la tecnica che usi abitualmente?
Fearnbuster,

1
Penso che ci sia un grosso errore nel fatto che questa risposta raggruppa la dipendenza dal design e la dipendenza dal runtime. I due possono essere diversi.
Euforico

1
@Euforico Perché grazie. Li sto mettendo insieme perché se non si ha una dipendenza del codice sorgente da qualcosa non è possibile utilizzare un riferimento di runtime per esso poiché non si capisce di cosa si tratta. Tutto ciò che potresti fare è conservare il riferimento come fa una raccolta. Se è un errore, mi piacerebbe capirlo.
candied_orange,

2

Trovo questo problema troppo confuso e ci vorrebbe un sacco di testo e tempo per spiegare correttamente il problema poiché credo che fraintendiate sia Martin Architecture Clean sia MVVM.

La prima cosa da notare è che il diagramma che hai pubblicato è incompleto. Mostra solo "business logic", ma manca una sorta di "orchestrator" che in realtà fa muovere le parti nell'ordine giusto. inserisci qui la descrizione dell'immagine

Il codice dell'orchestratore sarebbe semplice come

string Request(string request) // returns response
{
    Controller.Run(data);
    Presenter.Run();
    return View.Run();
}

Credo di aver sentito Martin parlarne in uno dei suoi discorsi su Clean Architecture.

Un'altra cosa da sottolineare è che l'osservazione di candied_orange sulla mancanza di cicli è sbagliata. Sì, il ciclo non esiste (e non dovrebbe) nell'architettura del codice. Ma i cicli tra le istanze di runtime sono comuni e spesso portano a una progettazione più semplice.

Questo è il caso di MVVM. In MVVM View dipende da ViewModel e ViewModel utilizza gli eventi per notificare a View le sue modifiche. Ciò significa che nella progettazione delle classi esiste solo dipendenza dalle classi View to Model, ma durante il runtime esiste una dipendenza ciclica tra le istanze View e ViewModel. Per questo motivo, non è necessario l'orchestrator, poiché ViewModel fornirà a View il modo di capire quando aggiornarsi. Questo è il motivo per cui le "notifiche" in questo diagramma usano la linea "squigly" e non la linea diretta. Significa che View osserva le modifiche in ViewModel, non che ViewModel dipende da View.

inserisci qui la descrizione dell'immagine

La cosa più importante che dovresti prendere dall'architettura pulita di Martin non è il design stesso, ma il modo in cui gestisci le dipendenze. Uno dei punti critici che sottolinea nei suoi discorsi è che quando c'è un confine, allora tutte le dipendenze del codice che attraversano quel confine lo attraversano in una sola direzione. Nel diagramma, questo confine è rappresentato da una doppia linea. E c'è un sacco di inversione di dipendenza attraverso interfacce ( InputBoundary, OutputBoundarye DataAccessInterface) che fissa la direzione codice dipendenza.

Al contrario, ViewModelin Clean Architecture è semplicemente DTO senza logica. Ciò è reso evidente dal <DS>tag. E questo è il motivo per cui orchestratorè necessario, poiché Viewnon saprà quando eseguirne la logica.

Se dovessi "appiattire" il diagramma su come apparirà durante il runtime, sarà simile a questo:

inserisci qui la descrizione dell'immagine

Quindi, durante il runtime, le dipendenze sono nella direzione "sbagliata", ma va bene.

Consiglio di guardare i suoi discorsi su Clean Architecture per capire meglio il suo ragionamento.


Il tuo "orchestratore" non dovrebbe chiamare il presentatore. L'Inter Case Case lo fa.
candied_orange,

@candied_orange Vero, questo è un errore.
Euforico,

Grazie per la risposta, è sempre utile avere alcune opinioni diverse. Ho votato entrambe le risposte dei tuoi ragazzi. Qualcuno di voi sa se Robert Martin ha una base di codice da qualche parte in cui ha implementato una forma della sua architettura? Ho guardato il suo progetto FitNess, ma non riuscivo a vedere la foresta per gli alberi. Inoltre, ho ragione nel speculare che, anche se l'immagine che ho pubblicato è il diagramma che lo zio Bob usa sempre nei suoi discorsi, in realtà è solo un esempio di come potrebbe apparire la tua architettura? Considerando che può apparire molto diverso purché il flusso di dipendenza sia corretto?
Fearnbuster,

@Fearnbuster Alla tua ultima domanda, sì. La direzione delle dipendenze è più importante della struttura. Credo che "Clean architecture", "Onion architecture" e "Hexagonal architecture" siano in realtà implementazioni della stessa idea di "Tenere sotto controllo le dipendenze".
Euforico

@Euforico Onestamente, direi che questo è probabilmente il caso, perché in un'immagine diversa del suo libro (Figura 8.2 del Capitolo 8), mostra un'architettura che sembra diversa. In quel diagramma, il Controller è in realtà un intermediario tra l'Interactor e il Presenter. Non vi è inoltre alcun limite di output per l'interattatore; sembra che l'Interactor riceva richieste attraverso un'interfaccia e quindi restituisca le risposte attraverso la stessa interfaccia (suppongo che ciò avvenga tramite un semplice valore di ritorno della funzione, poiché non riesco a pensare a nessun altro meccanismo che funzioni in questo modo).
Fearnbuster,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.