Switch vs Polymorphism quando si ha a che fare con il modello e la vista

12

Non riesco a trovare una soluzione migliore al mio problema. Ho un controller di visualizzazione che presenta un elenco di elementi. Questi elementi sono modelli che possono essere un'istanza di B, C, D, ecc. Ed ereditano 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 ignori la sintassi, non è una lingua specifica)

1) interruttore (lo 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 poiché B, C, D, ecc. sono modelli, non dovrebbero sapere cose relative alla vista. O dovrebbero in quel caso?

    
posta Raphael Oliveira 23.10.2013 - 14:37
fonte

4 risposte

6

Penso che forse un'implementazione del modello di visitatore sarebbe utile qui. La classe B, C e D avrebbe bisogno di essere "visitata" per determinare il tipo di vista, ma non avrebbe bisogno di sapere nulla sulle viste. ViewFactory (sotto) visiterebbe l'oggetto e userà il polimorfismo per determinare la vista corretta da costruire. Nessuna dichiarazione di commutazione. Non chiedere informazioni sui modelli interni per decidere cosa costruire. L'interfaccia visitatore utilizza il polimorfismo per selezionare il setter corretto per la vista. Il setter può passare l'articolo al costruttore del tipo di vista specifico (X o Y o Z) e tale visualizzazione 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;
   }

} 
    
risposta data 24.10.2013 - 04:11
fonte
1

Più di un commento che una risposta, ma penso che sia un disastro. 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 esso possa scegliere lo schermo (polimorfismo). Penso che devi scegliere quello che pensi sia il più semplice nel tempo; non c'è una giusta risposta alla domanda. (Spero che qualcuno possa provare che ho torto.) Mi piace il polimorfismo, me stesso.

Mi imbatto in questo problema un po '. Il caso più fastidioso era una classe Wanderer, le cui istanze vagavano per una mappa. Per disegnarlo, era necessario che lo schermo di Wanderer o Wanderer fosse a conoscenza del display. Il problema era che c'erano due display (con altri che arrivavano). Poiché il numero di diverse sottoclassi di Wanderer era ampio e in crescita, ho inserito il codice di disegno nelle sottoclassi 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 aveva ancora bisogno di sapere di più su Wanderer di quanto fosse e decenti di cui aveva bisogno di sapere due (e forse più ) ambienti grafici completamente diversi (viste). (Suppongo che questa idea di spaccare la classe possa essere una sorta di risposta, ma tutto ciò che realmente fa è contenere un po 'il problema.)

Penso che questo sia un problema molto generale e fondamentale della progettazione orientata agli oggetti.

    
risposta data 23.10.2013 - 16:56
fonte
0

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

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

Mi piacerebbe coniare in questo blog postare . Le dichiarazioni di commutazione non sono necessariamente brutte finché le utilizzi correttamente. E nel tuo caso, l'astrazione di modelli del genere per l'uso in un controller potrebbe essere eccessivo e potrebbe portare a risultati indesiderati. Come violare l'SRP.

    
risposta data 23.10.2013 - 15:01
fonte
0

My problem with solution 2 is that since B, C, D, etc are models, they shouldn't know about view related stuff.

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

Inoltre, non sembra come A e le 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 creazione di Screen .

Se la casella combinata contiene un elenco di ciò che a.type restituisce, allora un'istruzione switch sembra più naturale. Tuttavia, invece di inserirlo nel gestore di eventi click, lo inserisco in 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...        
}

In questo modo puoi testare il comportamento di generazione dello schermo e estrarre piacevolmente alcune funzionalità dall'interfaccia utente. Mantiene intatta la stratificazione View. Forse semplifica il tuo design, se significa A e le sottoclassi possono essere compresse nel flag type che contengono.

    
risposta data 23.10.2013 - 15:46
fonte