Cambia vs polimorfismo quando si tratta di modello e vista


12

Non riesco a trovare una soluzione migliore al mio problema. Ho un controller di visualizzazione che presenta un elenco di elementi. Tali elementi sono modelli che possono essere un'istanza di B, C, D, ecc. Ed ereditati da A. Quindi, in quel controller di visualizzazione, ogni elemento dovrebbe passare a una schermata diversa dell'applicazione e passare alcuni dati quando l'utente seleziona uno di essi . Le due alternative che mi vengono in mente sono (per favore ignora la sintassi, non è un linguaggio specifico)

1) interruttore (so che fa schifo)

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);

    switch(a.type) {
         case b:
             B b = (B)a;
             go to screen X;
             x.v1 = b.v1; // fill X with b data
             x.v2 = b.v2; 
         case c:
             go to screen Y;
         etc...
    }
}

2) polimorfismo

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);
    Screen s = new (a.getDestinationScreen()); //ignore the syntax
    s.v1 = a.v1;   // fill s with information about A
    s.v2 = a.v2;
    show(s);
}

//inside B
Class getDestinationScreen(void) {
    return Class(X);
}

//inside C
Class getDestinationScreen(void) {
    return Class(Y);
}

Il mio problema con la soluzione 2 è che dato che B, C, D, ecc. Sono modelli, non dovrebbero conoscere le cose relative alla vista. O dovrebbero in quel caso?

Risposte:


6

Penso che forse un'implementazione del modello di visitatore sarebbe utile qui. Le classi B, C e D dovrebbero essere "visitate" per determinare il tipo di vista, ma non avrebbero bisogno di sapere nulla sulle viste. ViewFactory (sotto) visiterebbe l'oggetto e userebbe il polimorfismo per determinare la vista corretta da costruire. Nessuna istruzione switch. Non è necessario chiedere agli interni del modello di decidere cosa costruire. L'interfaccia visitatore utilizza il polimorfismo per selezionare il setter corretto per la vista. Il setter può passare l'elemento al costruttore del tipo di vista specifico (X o Y o Z) e tale vista può quindi popolare i suoi campi dall'elemento.

   //inside the view controller
   void onClickItem(int index) {
      ViewFactoryVisitable a = items.get(index);
      ViewFactory aViewFactory = new ViewFactory(
      s = aViewFactory.getViewFor(a);
      show(s);
   }

--------

//Element interface
public interface ViewFactoryVisitable
{
    public void accept(ViewFactory theViewFactory);
}

---------

public interface ViewFactoryVisitor
{
   // one for each concrete type, polymorphism will choose correct setter
   public set setViewFor(B b);
   public set setViewFor(C c);
   public set setViewFor(D d);
}

--------

// B, C, D must implement this visitable interface
class B implements ViewFactoryVisitable
{ 
   ...

   //accept the ViewFactory as a visitor
   public void accept(ViewFactoryVisitor theViewFactoryVisitor)
   {
      theViewFactoryVisitor. setViewFor(this);
   }

   ...
} 

--------

class ViewFactory implements ViewFactoryVisitor
{
   ViewFactory(ViewFactoryVisitable theItem) {
      theItem.accept(this);
   }

   private View mView = null;
   ...

   public void setViewFor(B b) {
      // construct a view x and populate with data from b
      mView = new ViewX(b); 
   }

   public void setViewFor(C c) {
      mView = new ViewY(c); 
   }

   public void setViewFor(D d) {
      mView = new ViewZ(d); 
   }

   View getView() {
      return mView;
   }

} 

1
Se l'implementazione di accept non fosse "theViewFactoryVisitor.setViewFor (this);" Scusa se sono stupido!
Ryan,

@Ryan Buona cattura. Questo errore è qui da 3 anni!
Chuck Krutsinger,

1

Più un commento che una risposta, ma penso che sia un rompiscatole. O la Vista deve sapere tutto sul Modello in modo che possa scegliere lo schermo (interruttore) o il Modello deve sapere tutto sulla Vista in modo che possa scegliere lo schermo (polimorfismo). Penso che devi scegliere quello che pensi sarà il più semplice nel tempo; non c'è una risposta giusta alla domanda. (Spero che qualcuno possa dimostrarmi che mi sbaglio.) Mi appoggio al polimorfismo, me stesso.

Mi imbatto in questo problema un po '. Il caso più fastidioso era una classe Wanderer, i cui esempi vagavano su una mappa. Per disegnarlo, il display doveva conoscere Wanderer o Wanderer doveva conoscere il display. Il problema era che c'erano due display (con altri in arrivo). Poiché il numero di diverse sottoclassi di Wanderer era elevato e in aumento, ho inserito il codice di disegno nelle sottoclassi di Wanderer. Ciò significava che ogni classe di grandi dimensioni aveva esattamente un metodo che aveva bisogno di sapere su Graphics2D e esattamente un metodo che aveva bisogno di sapere su Java3D. Brutto.

Ho finito per dividere la classe, dandomi due strutture di classe parallele. La classe Wanderer è stata liberata dalla conoscenza della grafica, ma la classe DrawWanderer doveva ancora sapere di più su Wanderer di quanto fosse decente e doveva conoscere due (e forse più) ambienti grafici completamente diversi (Views). (Suppongo che questa idea di divisione della classe possa essere una sorta di risposta, ma tutto ciò che fa è contenere un po 'il problema.)

Penso che questo sia un problema molto generale e fondamentale del design orientato agli oggetti.


0

Penso che andare con l'interruttore sia un'opzione migliore rispetto al polimorfismo per questo caso.

È una cosa abbastanza semplice da fare, quindi non credo che debba essere complicato dall'uso del polimorfismo.

Mi piacerebbe coniare in questo post del blog . Le istruzioni switch non sono necessariamente brutte fintanto che le usi correttamente. E nel tuo caso, astrarre modelli come quello da utilizzare in un controller potrebbe essere eccessivo e potrebbe produrre risultati indesiderati. Come violare l'SRP.


Vedo il tuo punto. Bene, non penso che il polimorfismo sia eccessivamente complicato. E la classe A nel mio caso non è astratta, è infatti utilizzata. Grazie per i tuoi pensieri, anche se sto ancora aspettando una soluzione migliore e più propenso all'approccio del polimorfismo.
Raphael Oliveira,

1
non preoccuparti, sto solo dando i miei 2 centesimi sulla questione. Anche se per risolvere il tuo problema devi inserire la logica di visualizzazione nei tuoi modelli, puoi sempre avvolgerli con decoratori in modo che i tuoi modelli rimangano liberi dalla logica di vista. Quindi puoi usare il polimorfismo sulle classi del decoratore anziché sul modello.
Maru,

0

Il mio problema con la soluzione 2 è che dato che B, C, D, ecc. Sono modelli, non dovrebbero conoscere le cose relative alla vista.

Sono d'accordo con questa preoccupazione. Sono anche un po 'preoccupato che gli oggetti che si trovano in una casella combinata abbiano un comportamento. Non sono sicuro che sia una "cosa brutta" non averlo mai fatto, mi sembra una scelta innaturale.

Inoltre, non sembra Ae le sue sottoclassi sono il tipo con cui hai un polimorfismo interessante. Il tipo interessante è in realtà Screen. In questo esempio, Aè solo una classe che contiene informazioni per informare la Screencreazione.

Se si rende la casella combinata contenente un elenco di tutti i a.typeritorni, un'istruzione switch sembra più naturale. Tuttavia, invece di inserirlo nel gestore dell'evento click, lo inserisco in a ScreenFactory. Quindi hai:

//inside the view controller
void onClickItem(int index) {
    A a = items.get(index);

    s = _screenFactory.GetScreen(a);
    show(s);
    }
}

//inside a ScreenFactory implementation
internal Screen GetScreen(A typeIndicator)
{
switch(a.type) {
     case b:
         return new ScreenX();
     case c:
         return new ScreenY();
     etc...        
}

Ciò ti consente di testare il comportamento della costruzione dello schermo e di estrarre piacevolmente alcune funzionalità dall'interfaccia utente. Mantiene intatta la stratificazione di View. Forse semplifica il tuo progetto, se significa Ae le sottoclassi possono essere compresse nella typebandiera che contengono.


Grazie per la risposta tallseth. Sfortunatamente, i modelli contengono molte informazioni, non solo il loro tipo o il controller della vista di destinazione. Inoltre, anche se non ho menzionato, ScreenX, ScreenY ecc. Devono ricevere informazioni su B, C, D sulla costruzione, quindi non posso usare una fabbrica solo passando il tipo, devo passare il modello stesso.
Raphael Oliveira,
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.