Schalten Sie im Umgang mit Modell und Ansicht gegen Polymorphismus um

12

Ich kann keine bessere Lösung für mein Problem finden. Ich habe einen Ansichtscontroller, der eine Liste von Elementen darstellt. Diese Elemente sind Modelle, die eine Instanz von B, C, D usw. sein können und von A erben. In diesem Ansichtscontroller sollte also jedes Element zu einem anderen Bildschirm der Anwendung wechseln und einige Daten übergeben, wenn der Benutzer einen von ihnen auswählt . Die zwei Alternativen, die mir in den Sinn kommen, sind (bitte ignorieren Sie die Syntax, es ist keine bestimmte Sprache)

1) Schalter (ich weiß, dass saugt)

//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) Polymorphismus

//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);
}

Mein Problem mit Lösung 2 besteht darin, dass B, C, D usw. Modelle sind und sie keine Informationen über die Anzeige von Inhalten haben sollten. Oder sollten sie in diesem Fall?

Raphael Oliveira
quelle

Antworten:

6

Ich denke, dass hier vielleicht eine Implementierung des Besuchermusters sinnvoll wäre. Die Klassen B, C und D müssen "besucht" werden, um den Ansichtstyp zu bestimmen, müssen jedoch nichts über Ansichten wissen. Die ViewFactory (unten) würde das Objekt besuchen und mithilfe des Polymorphismus die korrekte zu erstellende Ansicht bestimmen. Keine switch-Anweisungen. Keine Frage nach Modell-Interna, um zu entscheiden, was gebaut werden soll. Die Besucheroberfläche verwendet Polymorphismus, um den richtigen Setter für die Ansicht auszuwählen. Der Setter kann das Element an den Konstruktor des bestimmten Ansichtstyps (X oder Y oder Z) übergeben und diese Ansicht kann dann seine Felder aus dem Element füllen.

   //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;
   }

} 
Chuck Krutsinger
quelle
1
Sollte die Implementierung von accept nicht "theViewFactoryVisitor.setViewFor (this)" sein? Tut mir leid, wenn ich dumm bin!
Ryan
@ Ryan Guter Fang. Dieser Fehler ist seit 3 ​​Jahren hier!
Chuck Krutsinger
1

Mehr ein Kommentar als eine Antwort, aber ich denke, es ist ein Fehler. Entweder muss die Ansicht alles über das Modell wissen, damit es den Bildschirm auswählen kann (Schalter), oder das Modell muss alles über die Ansicht wissen, damit es den Bildschirm auswählen kann (Polymorphismus). Ich denke, Sie müssen sich entscheiden, was Ihrer Meinung nach im Laufe der Zeit am einfachsten sein wird. Es gibt keine richtige Antwort auf die Frage. (Ich hoffe, jemand kann mir das Gegenteil beweisen.) Ich neige selbst zum Polymorphismus.

Ich stoße ein bisschen auf dieses Problem. Der nervigste Fall war eine Wanderer-Klasse, von der einige auf einer Karte herumirrten. Um es zu zeichnen, musste entweder das Display etwas über Wanderer wissen, oder Wanderer musste etwas über das Display wissen. Das Problem war, dass es zwei Displays gab (mit weiteren). Da die Anzahl der verschiedenen Wanderer-Unterklassen immer größer und größer wurde, habe ich den Zeichencode in die Wanderer-Unterklassen eingefügt. Das bedeutet, dass jede große Klasse genau eine Methode hatte, die sich mit Graphics2D auskennt, und genau eine Methode, die sich mit Java3D auskennt. Hässlich.

Am Ende habe ich die Klasse aufgeteilt und mir zwei parallele Klassenstrukturen gegeben. Der Wanderer Klasse wurde aus dem Wissen über die Grafik befreit, aber die DrawWanderer Klasse noch mehr über Wanderer müssen wissen , als war in Ordnung und es brauchte um zu wissen , zwei (und vielleicht auch mehr) ganz andere Grafikumgebungen (Views). (Ich nehme an, diese Idee, die Klasse aufzuspalten, ist vielleicht eine Art Antwort, aber alles, was sie wirklich tut, ist, das Problem ein wenig in den Griff zu bekommen.)

Ich denke, dies ist ein sehr allgemeines und grundlegendes Problem des objektorientierten Designs.

RalphChapin
quelle
0

Ich denke, mit dem Schalter zu gehen, ist eine bessere Option als mit Polymorphismus für diesen Fall zu gehen.

Es ist eine ziemlich einfache Sache, deshalb denke ich nicht, dass es durch die Verwendung von Polymorphismus überkompliziert werden muss.

Ich würde gerne in diesen Blog-Beitrag einmünzen . Switch-Anweisungen sind nicht unbedingt hässlich, solange Sie sie ordnungsgemäß verwenden. Und in Ihrem Fall kann das Abstrahieren von Modellen wie diesen für die Verwendung in einem Controller zu viel des Guten sein und zu unerwünschten Ergebnissen führen. Als würde man das SRP verletzen.

Maru
quelle
Ich verstehe dein Argument. Nun, ich denke nicht, dass Polymorphismus zu kompliziert ist. Und die A-Klasse ist in meinem Fall nicht abstrakt, sie wird tatsächlich verwendet. Vielen Dank für Ihre Gedanken, obwohl ich immer noch auf eine bessere Lösung warte und eher zum Polymorphismus neige.
Raphael Oliveira
1
Keine Sorge, ich gebe nur 2 Cent dafür. Um das Problem zu lösen, dass Ansichtslogik in Ihre Modelle eingefügt werden muss, können Sie sie immer nur mit Dekoratoren umschließen, damit Ihre Modelle frei von Ansichtslogik bleiben. Dann können Sie Polymorphismus für die Dekoratorklassen anstelle des Modells verwenden.
Maru
0

Mein Problem mit Lösung 2 besteht darin, dass B, C, D usw. Modelle sind und sie keine Informationen über die Anzeige von Inhalten haben sollten.

Ich stimme diesem Anliegen zu. Ich bin auch ein wenig besorgt, dass Objekte, die in einer Combobox sitzen, Verhalten haben würden. Ich bin mir nicht sicher, ob das eine "schlechte Sache" ist, weil ich es noch nie getan habe. Es scheint mir nur eine unnatürliche Entscheidung zu sein.

Es scheint auch nicht so Aund seine Unterklassen sind der Typ, mit dem Sie einen interessanten Polymorphismus haben. Der interessante Typ ist eigentlich Screen. In diesem Beispiel Ahandelt es sich nur um eine Klasse, die Informationen zum Informieren der ScreenErstellung enthält.

Wenn Sie in der Combobox eine Liste der zurückgegebenen Werte a.typeerstellen, erscheint eine switch-Anweisung natürlicher. Anstatt es in den Click-Event-Handler zu schreiben, würde ich es in einen einfügen ScreenFactory. Dann haben Sie:

//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...        
}

Auf diese Weise können Sie das Verhalten beim Erstellen von Bildschirmen testen und einige Funktionen in der Benutzeroberfläche nutzen. Die View-Ebenen bleiben dabei erhalten. Vielleicht vereinfacht es Ihr Design, wenn es bedeutet, dass AUnterklassen in dem typeFlag, das sie enthalten, zusammengefasst werden können.

tallseth
quelle
Vielen Dank für die Antwort tallseth. Leider enthalten die Modelle viele Informationen, nicht nur ihren Typ oder den Zielansichts-Controller. Obwohl ich nicht erwähnt habe, müssen ScreenX, ScreenY usw. Informationen über B, C, D bezüglich der Konstruktion erhalten, sodass ich keine Fabrik verwenden kann, die nur den Typ besteht. Ich muss das Modell selbst übergeben.
Raphael Oliveira