MVC come design con interfacce: come gestire gli oggetti di base?

1

Ho un'applicazione (Android) e ho deciso che il livello di presentazione otterrà i dati solo attraverso interfacce rigorose dal controller. Sono al punto in cui alcuni oggetti di base devono essere passati al livello di presentazione, ma gli oggetti stessi devono essere in modalità rigorosa di sola lettura, poiché tutte le modifiche verranno gestite dal controller.

Per fare un semplice esempio, immagina una App ToDo, in cui lo schermo deve visualizzare un elenco di elementi da fare.

L'articolo Todo ha i campi "titolo" e "contenuto" con i rispettivi getter e setter. Anche se passo un elenco non modificabile, gli oggetti in esso contenuti possono essere modificati. Posso pensare a queste alternative:

  1. metti controller e dati in un unico pacchetto e rendi i setter protetti.
  2. aggiungi un'interfaccia all'elemento todo contenente solo i getter e passando questi oggetti.
  3. aggiungi un'interfaccia solo al controller e metti tutti i getter lì.

per 1)

public class TodoItem {
    private String title; private String content;
    public String getTitle() ...
    protected String setTitle(..) ...
}

per 2)

public interface ITodoItem {
    public String getTitle();
}

public class TodoItem implements ITodoItem {
    private String title; private String content;

    @Override
    public String getTitle() ...

    public String setTitle(..) ...
}

Ora mi chiedo quale sia l'opzione più comune, più intelligente, ecc. (o ce ne sono altri?) e quali sono gli argomenti?

Alternativa 1) per il mio sentimento dipende molto dal fatto che gli elementi devono trovarsi nello stesso pacchetto rendendolo difficile da espandere il programma.

L'alternativa 2) mi sembra strana, perché tutti gli oggetti piccoli diventerebbero un'interfaccia con i metodi getter. Lo sto usando e tecnicamente sono soddisfatto (in quanto mi impedisce di cambiare accidentalmente un oggetto). Tuttavia, non è immutabile, poiché l'oggetto può essere ricondotto a ToDo-Item e quindi l'originale viene modificato. Quindi questo non è buono per gli apis pubblici. Devo davvero clonare ogni oggetto se voglio immutabilità?

L'alternativa 3) diventa rapida perché un controller contiene tutti i getter dei piccoli oggetti. Questo non è conforme a "lasciare che l'oggetto faccia il lavoro".

Quali sono le tue esperienze e pensieri su questo?

    
posta Gunnar Bernstein 28.02.2015 - 18:20
fonte

2 risposte

2

Avendo ingegnerizzato uno o due sistemi nella mia carriera, cercherò di rispondere da un punto di vista pragmatico.

Il mio primo approccio sarebbe quello di stabilire la convenzione, che i dati non devono essere modificati nel livello di presentazione. Se lavori da solo, conosci comunque le tue convenzioni. In una piccola squadra dovresti essere in grado di comunicarli facilmente. Sì, questo non impedisce le violazioni in fase di compilazione, ma le violazioni involontarie devono essere rare e facili da individuare (ad esempio, chiamare la gerarchia di un certo setter).

Se hai assolutamente bisogno / vuoi implementare questa regola in codice, mettere i controller e i rispettivi oggetti dati in un singolo pacchetto ciascuno, è la cosa migliore. Inoltre, non utilizzerei protected , ma l'ambito predefinito / pacchetto (ad esempio omettendo la parola chiave visibilità). Delle tre opzioni che hai presentato, questo è l'approccio più leggero (e quindi meglio mantenibile e scalabile). Non dovresti essere troppo preoccupato per "... rendendo difficile l'espansione del programma.", Merley deve vivere con potenzialmente un sacco di classi in un unico pacchetto. Forse puoi mitigarlo raggruppando abilmente alcuni controller con alcuni oggetti nel loro pacchetto.

Se anche questo approccio fallisce, gli oggetti immutabili sono ciò che vuoi. Potresti implementarli in modo semplice (costruttore costruttore e copia, nessun setter), con factory / metodi factory, o anche usare il Builder Pattern . Tieni a mente che l'overhead (cioè il codice della piastra della caldaia) aumenta in modo significativo con questa soluzione e dovresti chiederti se il guadagno (meno tempo speso a cacciare / riparare bug) è veramente maggiore del costo (scrivere, testare e mantenere il piatto della caldaia codice). In effetti, consiglierei questa soluzione solo se è possibile garantire che gli oggetti non devono mai essere modificati e quindi è possibile abbandonare il codice di copia. Tuttavia, questo non sembra il tuo esempio.

L'introduzione di un'interfaccia getter speciale per ciascuna (!) classe di dati significa un sovraccarico maggiore rispetto ai setter protetti / predefiniti ed è solo la metà rigorosa, come hai detto tu stesso: si potrebbe sempre abbattere l'attuale classe dei dati. Per migliorare questo, potresti dare protezione alle tue classi di dati o visibilità dei pacchetti e metterli nello stesso pacchetto dei controller, ma poi sei tornato al punto di partenza, con interfacce e complessità aggiuntive.

Mantenere separati i controller e il modello dati è una buona idea. Per la maggior parte delle applicazioni è molto più probabile che il modello cambi rispetto alla logica del controller. E tu vuoi mantenere quella parte mutevole contenuta, dal momento che ogni cambiamento potenzialmente introduce un bug e significa ulteriori adattamenti nel codice del test e del client. Spostando e / o spostando i getter direttamente nel controller (con o senza interfacce speciali) si aumenta anche l'ambito della modifica. Dovresti improvvisamente mantenere il codice test e client del controller, invece del solo modello, se vuoi aggiungere / modificare un campo. Un altro, più semplice problema è, se hai bisogno di una raccolta di dati non primitivi nel tuo modello.

Qualsiasi decisione di progettazione del software si riduce alla domanda su quale soluzione consente a te, ai tuoi colleghi e agli utenti di lavorare in modo più efficiente. Questi tre gruppi avranno probabilmente esigenze contraddittorie e quindi è difficile trovare la soluzione migliore e i trade-off ne fanno quasi sempre parte.

    
risposta data 01.03.2015 - 11:36
fonte
2

Che ne dici di progettare i tuoi "oggetti di base" come oggetti immutabili al 100%?

public final class TodoItem {
    final private String title; 
    final private String content;

    public TodoItem (String title, String content) {
        this.title= title;
        this.content = content;
     }

    public String getTitle() ...
    // no setter at all, title and content can only set when creating a new object
}

Implementa il tuo programma in termini di tali oggetti, quindi puoi passarli tra livelli arbitrari, senza il rischio di ottenere effetti collaterali indesiderati. Qui trovi una descrizione più dettagliata esempio.

    
risposta data 28.02.2015 - 19:39
fonte

Leggi altre domande sui tag