Quali altri approcci ci sono per interrompere la dipendenza circolare in MVC?

1

Sto tentando di creare un front-end Web basato sul pattern MVC, anziché sulla base delle librerie coinvolte (ad esempio React.js).

Sto utilizzando l'integrazione delle dipendenze basata sul costruttore e le interfacce per disaccoppiare le implementazioni di ciascun elemento (di MVC), ma questo lascia comunque una dipendenza circolare.

  • Il controller dipende dal modello (per rispondere agli eventi dell'utente finale).
  • Il modello dipende da Visualizza (per ri-renderizzare il modello aggiornato).
  • La vista dipende da React Component (per l'implementazione).
  • Il componente di reazione dipende dai metodi del controller (per consentire l'input dell'utente finale).

Ho infranto questa dipendenza circolare usando l'iniezione di dipendenza basata sul mutatore con la vista, in questo modo:

view = new View()
model = new Model(view)
controller = new Controller(model)

component = new Component(controller.onClick)
view.component = component

... che funziona, ma mi lascia un problema.

Poiché la dipendenza del componente nella vista è iniettata da un mutatore, non sarà disponibile (per l'uso) fino a quando l'iniezione non si verifica effettivamente.

Eppure il mio modello sta facendo un'inizializzazione prima che l'iniezione del mutatore avvenga e poiché la vista non ha un componente per funzionare, non fa nulla (finché non ha un componente).

Sto pensando di poterlo risolvere memorizzando gli aggiornamenti in attesa della vista (all'interno dell'implementazione View) e poi riproducendoli in ordine quando il componente diventa disponibile.

C'è un approccio migliore a questo?

Modifica 1

Grazie a John per aver segnalato che ho il mio modello < - > Visualizza le dipendenze nel modo sbagliato.

Tuttavia, se li aggiusto per ottenere questo:

model = new Model()
controller = new Controller(model)
component = new Component(controller.onClick)
view = new View(model, component)

... che mi consente di utilizzare l'iniezione delle dipendenze basata sul costruttore tutt'intorno, ma il mio problema originale persiste ancora.

vale a dire. Se il mio modello esegue un'inizializzazione prima che la View si iscriva alle sue trasmissioni di aggiornamento, quelle trasmissioni iniziali non lo faranno alla View (e quindi all'utente finale).

Penso che dovrò provare a ritardare queste inizializzazioni del Modello fino a dopo che la Vista si è abbonata agli aggiornamenti del Modello.

Mi manca un altro approccio?

    
posta pleasedesktop 15.08.2017 - 04:00
fonte

2 risposte

1

Penso che il tuo problema sia questo:

React Component depends on Controller (to allow for end-user input).

Il controller dovrebbe inserire i callback nella tua vista. Ad esempio, se si desidera chiamare un metodo di controllo al clic, si esporrà una proprietà onClick sul componente React e il controller imposterà la proprietà per chiamare il metodo su se stesso.

La vista non dovrebbe sapere nulla sul controller. Dovrebbe solo avere conoscenza del modello.

// model
function todo(title) {
    return {
        title: title,
    }
}

// view
function View({
    todos,
    onAddTodo
}) {
    return (
        <button onClick={onAddTodo}>Add todo</button>
        {
            todos.map(todo => (
                <p>{todo.title}</p>
            ))
        }
    )
}

// controller
function controller() {
    const todos = [todo('foo'), todo('bar')]

    const addTodo = todos.push(todo('new'))

    return (<View todos={todos} onAddTodo={addTodo}/>);
}

In questo esempio:

controller -> model
controller -> view
view -> model

Non ci sono dipendenze circolari. Puoi vedere la mia risposta su come usare DI con reagire perché i componenti sono di solito annidati profondamente in React, e DI aiuta a risolvere il problema del passaggio delle dipendenze a componenti profondamente annidati.

Modifica 1:

Il tuo snippet di Modifica 1 è come dovrebbe essere progettato. Ma tu dici

If my Model does some initialization before the View subscribes to its update broadcasts, then those initial broadcasts will not make it to the View

La vista non dovrebbe essere abbonata direttamente al modello. In React, le modifiche al modello devono essere trasferite attraverso le proprietà dal controller nella vista. La vista dovrebbe essere una pura rappresentazione visiva del modello - non dovrebbe essere coinvolta con la sottoscrizione e l'aggiornamento stesso.

    
risposta data 15.08.2017 - 04:39
fonte
0

Basandosi sul feedback fornito da Bart e Filip (nei commenti delle domande) ...

Questo design delle dipendenze funziona bene:

model = new Model()
controller = new Controller(model)
component = new Component(controller.onClick)
view = new View(model, component)

Come per superare il problema in cui il Modello si aggiorna prima che la Vista si "legga" al Modello, un buon approccio sarebbe che la Vista interrogasse il Modello (per ciò di cui ha bisogno) quando viene costruito e poi si abbona al Modello Modello (per aggiornamenti).

    
risposta data 15.08.2017 - 11:38
fonte