2016-05-20 19 views
1

Mein Plan ist es, ein ControlPanelFactory zu haben, die ein maßgeschneidertes ControlPane für jede der verschiedenen Model Klassen bauen:Fabrikmuster und Polymorphie

abstact class Model { 
} 

class ModelA extends Model { 
} 

class ModelB extends Model { 
} 

Jetzt ist mein Plan, die Factory-Klasse zu haben, war die unterschiedlichen ControlPane Instanzen erzeugt basierend auf die Model Klasse, die mit der Methode Überlastung übergeben wurde:

class ControlPaneFactory { 
    private ControlPaneFactory() { 
    } 

    public static ControlPanel build(ModelA model) { 
     return new ControlPaneA(model); 
    } 

    public static ControlPanel build(ModelB model) { 
     return new ControlPaneB(model); 
    } 
} 

dies ist jedoch sehr problematisch, da beim Aufruf der factorys Methode, ich habe nur eine Vari Typ Model, so würde ich zuerst instanceof verwenden müssen, das ist ein riesiger Codesmell. Die gleichen apllies zu einer kondensierten Factory-Methode mit:

public static ControlPane build(Model model) { 
    if (model instanceof ModelA) 
     return new ControlPaneA(model); 
    else if (model instanceof ModelB) 
     return new ControlPaneB(model); 
    else throw new IllegalArgumentException("Unsupported model"); 
} 

Ich dachte über eine ENUM in den Modellklassen usign, die die Art von Model angeben würden, aber dies scheint auch wie eine schlechte Option, die DRY verletzen würde.

Zusätzlich wäre es mir viel lieber, wenn die Instanz ControlPane unabhängig (d. H. In einer speziellen Klasse) der Model Klassen wäre. Gibt es einen "netten" Weg, dieses Problem zu lösen?

Antwort

1

Der Versuch, Ihren Code generischer zu machen, was bedeutet, dass ControlPanel nicht von einem bestimmten Modell abhängig sein sollte. Allerdings, wenn es wirklich notwendig ist, können Sie dies versuchen:

public class ControlPanelFactory { 
    private static Map<Class<? extends Model>, Class<? extends ControlPanel>> modelPanelMap = new HashMap<>(); 

    public static void addModelPaneRelation(Class<? extends Model> model, Class<? extends ControlPanel> pane) { 
     modelPanelMap.put(model, pane); 
    } 

    public static ControlPanel build(Model model) { 
     try { 
      return modelPanelMap.get(model.getClass()) 
        .getConstructor(model.getClass()) 
        .newInstance(model); 
     } catch (Exception exception) { 
      // Handle exceptions 
     } 

     return null; 
    } 
} 

Wenn Sie die Anwendung starten, sollten Sie irgendeine Art von Konfiguration haben. Das führt es so aus:

ControlPanelFactory.addModelPaneRelation(ModelA.class, ControlPanel.class); 

Zumindest wird dies die Logik extrahieren, wie Panel abhängig von Model sind. Wieder denke ich nicht, dass dies die sauberste Lösung ist.

2

Wenn Sie alle Ihre Factory-Methoden in das gleiche Objekt möchten, dann werden Sie auf die eine oder andere Weise eine Switch/instanceof-Gruppe oder eine Karte von Models zu ControlPanels benötigen.

Alternativ können Sie die Factory-Methode in die Klasse Model verschieben. Im Wesentlichen ist dies das abstrakte Fabrikmuster, aber Sie implementieren es mit Ihren Model Objekten. Es gibt ein paar Möglichkeiten, dies zu betrachten. Es könnte argumentiert werden, dass es die Kopplung zwischen Ihren Model und ControlPanel Paaren erhöht, aber ich würde vorschlagen, dass Sie versuchen, das zu erreichen. Es könnte auch argumentiert werden, dass es Ihren Werkscode weniger wiederverwendbar macht, da Sie ein Model Objekt benötigen, um es auszuführen, aber die Beispielschnittstelle, die Sie für das Fabrikobjekt angegeben haben, erfordert einen gebauten Model sowieso. Mit der Abkehr von anämischen Modellen denke ich, dass es eine vernünftige Art ist, es zu implementieren und die Komplexität in Ihrem Fabrikobjekt zu reduzieren (wenn Sie es noch brauchen).

ich mit so etwas wie dies gehen würde:

abstact class Model { 
    public abstract ControlPanel buildControlPanel(); 
} 

class ModelA extends Model { 
    public ControlPanel buildControlPanel() { 
     return new ControlPanelA(this); 
    } 
} 

class ModelB extends Model { 
    public ControlPanel buildControlPanel() { 
     return new ControlPanelB(this); 
    } 
} 

// Don't really need this anymore... 
class ControlPaneFactory { 
    public static ControlPanel build(Model model) { 
     return model.buildControlPanel(); 
    } 
} 

Dies wird immer noch erlauben, Ihnen die Flexibilität von wild unterschiedlichen Herstellern für Ihr ControlPanel s zu haben, wenn erforderlich, und Sie werden nicht um eine Karte registrieren müssen von Objekten, wenn Ihr Programm startet.

Sie könnten die buildControlPanel() Methode aus Ihrer Model s, und erstellen Sie eine ordnungsgemäße abstrakte Fabrikmuster, und stattdessen eine konkrete Fabrik von Ihrem Model Objekte zurück. Aber ich glaube, das würde nur die Anzahl der Klassen erhöhen, die Sie haben, ohne wirklich wirkliche Verbesserungen zu erzielen. Wenn Sie eine Menge von Model Klassen haben, die den gleichen Build-Code verwenden (wie ModelA, ModelB, alle entsprechen ControlPanelX), dann könnte es ein guter Weg zu gehen, aber es klingt nicht wie Sie das tun.

Am Ende des Tages, aber ein Schalter oder wenn Anweisung, die konkrete Klasse zu instanziieren wählen, ist nicht das Schlimmste auf der Welt. Andere Bibliotheken haben ähnliche Dinge wie Eclipse's EMF Switch Class verwendet.

+1

Die Instantiierung der 'ControlPane' in die entsprechenden' Model' Klassen wäre die naheliegende Lösung, aber wie Sie gesagt haben, erhöht dies die Kopplung zwischen Ansicht und Modell, was ich zu vermeiden versuche. Abstract Factory wäre sicher zu viel. Ich habe mich nur gefragt, ob es eine offensichtliche und elegante Lösung gibt, die gelöst werden könnte, aber es scheint, als würde man das 'ControlPanel' vom' Model' entkoppeln wollen, wird es immer etwas geben, das sich nicht sehr elegant anfühlt. – Marv