I segnali / eventi sono l'unico modo per modificare una vista da un'altra?

1

Ho bisogno di impostare una modalità in una vista da una vista diversa. Cercando di rimanere organizzato con MVC. Guardando la risposta a Richiamare la logica del modello in un UserControl da view-model in un altro UserControl senza violare MVVM , ma

  • Sembra ridondante che un controllore aggiorni la sua vista e spari un segnale per qualsiasi altra cosa a cui rispondere; quindi, è meglio per View1 emettere il segnale / evento? Oppure, dovrebbe modellare il fuoco?

  • Chi possiede i segnali?

  • I segnali possono essere particolarmente pericolosi quando si tratta di sviluppo di software collaborativo (il codebase diventa rapidamente caos, le persone diventano pigre e solo i segnali di fuoco dappertutto). Quali sono i casi solidi in questi casi?

Alcuni codici:

class View1 {
 ...
 clickModeOption(e){ 
   this.controller.onSelectSomeMode(e.value); //or, should I call View2's controller?
                                              //or, should I emit the signal myself?
 }
 ...
}

class Controller1 {
  ...
  onSelectSomeMode(mode) {

    model.someMode = mode; //i.e. maybe act on model here

    //these two lines look redundant of each other:
    this.view.setSomething();
    somebodysSignals.modeChanged.fire(); //or, model can fire
  }
}

class View2 {
  constructor(signals) {
    ...
    this.signals.modeChanged.add((newMode)=>this.setMode(newMode)); // i'm a cowboy
  }
  ...
  setSomeMode(mode) {...} //my controller isn't calling this :/
}
    
posta user2132190 25.03.2018 - 11:57
fonte

1 risposta

2

La domanda che hai collegato per discutere esplicitamente di WPF e MVVM, ma non hai detto se stai usando o meno questi. Se ti capita di utilizzare uno qualsiasi dei framework UI basati su XAML (UWP, Xamarin, WPF, ecc.), Dovresti prendere in considerazione il pattern MVVM e investigare su librerie di supporto come Prism, Caliburn Micro o MVVM Light che eliminare una buona parte del boilerplate relativa a INotifyPropertyChanged .

  • It seems redundant for a controller to update its view AND fire a signal for anything else to respond to; so, is it better for View1 to emit the signal/event? Or, should model fire it?

Quasi certamente quest'ultimo. Il 'Modello' in qualsiasi pattern MV * è in genere disponibile per essere aggiornato da altre parti dell'applicazione (forse anche da un elemento non UI come un socket di rete), quindi l'unico posto sensato per attivare il segnale sarebbe all'interno il modello, perché sicuramente non vuoi che qualcosa come un oggetto di rete abbia visibilità sul tuo View o sul tuo Controller.

In questo modo, nello scenario peggiore (ovvero quello in cui non ci sono strutture disponibili nel framework UI per ridurre il boilerplate) il controller è responsabile dell'aggiornamento della View sottoscrivendo le modifiche nel modello, che possono essere attivate dalla vista o da una vista diversa o da un meccanismo esterno.

  • Who owns the signals?

  • Signals can be especially dangerous when it comes to collaborative software development (codebase becomes chaos quickly, people get lazy and just fire signals all over the place). What's a solid pattern such cases?

Idealmente, il modello dovrebbe possedere sia lo stato che il segnale, incapsulato all'interno di un qualche tipo di oggetto libreria osservabile per eliminare il codice boilerplate e impedire che il segnale venga abusato o dimenticato.

Per approfondire questo aspetto, la capacità push del segnale dovrebbe essere l'unica riserva del metodo "setter" per lo stato. L'unica volta che il segnale dovrebbe mai essere generato è quando una qualsiasi parte dell'applicazione ha aggiornato i dati - ha senso creare una relazione molto stretta tra lo stato del modello e il suo segnale; di solito è indesiderabile ricorrere a qualsiasi tipo di soluzione che implichi l'utilizzo dell'uno senza l'altro.

Al contrario, la capacità pull è un gioco leale; qualsiasi parte dell'applicazione potrebbe ragionevolmente voler sapere quando lo stato del modello è cambiato, quindi non c'è alcun vantaggio reale nel limitare l'accesso ai potenziali abbonati. Questo è un altro motivo per cui il segnale dovrebbe essere strettamente accoppiato allo stato, perché qualsiasi cosa che sottoscrive quel segnale dovrebbe essere permesso di fare l'ipotesi che riceverà tutte le modifiche di stato, e che un segnale ricevuto significa che lo stato è cambiato.

Da un punto di vista pratico, questo problema è stato risolto molte volte, anche se in modi diversi da diversi framework dell'interfaccia utente, quindi il modo migliore per affrontarlo dipende strongmente dal framework dell'interfaccia utente che stai utilizzando.

Nello scenario ideale, sarebbe un framework UI che supporta Associazione dati , nel qual caso il numero di Controller il boilerplate sarà ridotto, o forse addirittura eliminato, in alcuni framework più recenti che sono stati progettati pensando a MV * sin dall'inizio.

Un framework UI che supporti l'associazione dei dati si occuperà di sincronizzare le modifiche tra la Vista e il Modello con codice minimo o nullo nel controller, presupponendo che nel Modello esista un meccanismo osservabile / di notifica appropriato.

Se l'associazione dati non è disponibile nel tuo framework dell'interfaccia utente, puoi replicare l'approccio nel controller da solo e risparmiarti la seccatura di reinventare il meccanismo del segnale usando qualcosa come Reactive Extensions (Rx) .

Semplice esempio di un framework dell'interfaccia utente che non ha incorporato il binding dei dati, utilizzando il concetto BehaviourSubject in Rx:

// Model shared by multiple Views
class MyModel {
    data = new BehaviourSubject<String>("");

    updateData(s: string) {
        this.data.next(s);
    }
}

// Controller for View1 - Repeat as necessary for View2, View3, etc.
class Controller1 {
    constructor(view, model) {
        // Update the view when the data changes
        model.data.subscribe(d => view.setText(d));

        // Update the data when the view changes
        view.textBox1.onTextChange(text => model.updateData(text));
    }
}

Il Rx BehaviourSubject è un oggetto osservabile con stato che deve anche avere uno stato iniziale, cioè combina lo stato del modello e il segnale, ma la capacità di spinta per il segnale è nascosta.

Se né Rx né alcun equivalente simile sono disponibili per la lingua che stai utilizzando, dovrebbe essere ragionevolmente semplice replicare una semplice variazione su BehaviourSubject per te.

    
risposta data 25.03.2018 - 13:58
fonte

Leggi altre domande sui tag