2009-12-17 21 views
7

Ich bin ein Neuling auf die Entwurfsmuster und hier ist meine FrageDesign-Muster Frage

wenn wir eine abstrakte Klasse mit wenigen Klassen, die es und jede dieser Klassen verfügt über verschiedene Attribute implementieren.

Jetzt habe ich eine andere (Manager-Klasse), die ein Array der abstrakten Klasse enthält und ich möchte eine Suchmethode darin einfügen ... wie kann ich das tun, ohne auf die konkreten Klassen zu werfen?

Ich habe 2 Ideen bekam:

Ertste: Hinzufügen einer zusätzlichen Ebenen der Schnittstellen, die mit dem Code geht nicht an Schnittstelle (dh nicht auf konkrete Klasse Gießen i auf eine Schnittstelle Gießen wird.) Implementation Rule ... aber so, wenn ich eine andere Klasse hinzufügen muss ich eine Schnittstelle dafür machen, und ich muss auch den Manager (Client) bearbeiten, was nicht sehr gut scheint.

Zweite Lösung: es etwas seltsam aussehen und braucht noch Verbesserungen, aber sein Hauptziel ist es, die Manager oder anderer Client arbeitet mit abstrakter Klasse zu machen, ohne irgend etwas zu wissen, wer sich oder ihre Attribute erweitert.

die solutin ist als folows: jedes neue Element wird hinzugefügt, eine Schnittstelle overide, dass es eine komplette discription seiner Felder zum Beispiel ein Auto Objekt eine Hash-Karte mit der Folowing

zurückkehren müssen generieren erzwingt

Feld: {Feldtyp, fieldvalue}

Beispiel

  • Modell: {Text, "Ford"
  • }
  • manifactureDate: {Date „12/1/89“}

und jedes Objekt wird auch eine Methode namens compareFields zu implementieren, die eine Hash-Karte wie diese nehmen und es auf seinem Gebiet zu vergleichen, und true zurück oder falsch.

jetzt auf diese Weise habe ich viele Probleme gelöst -für die GUI werde ich nur eine Rendering-Engine für dieses hashmap machen müssen, die jedes Element anzeigen kann, ohne zu wissen, was sein Typ. (Wiederum ist die gui ein anderer Client für die abstrakte Klasse)

-für die Suche i eine Hash-Karte bekommen, die die Felder enthalten die Benutzer im Suchformular und Schleife auf dem abstrakten Elemente eintritt, und rufen Sie die vergleichen fieldmethod

ich nicht immer noch, wie ich das komplexe Objekt (das ein anderes Objekt als seine Attribute) behandeln

ich weiß nicht, welche Art von Muster ist dies .. es nur eine Idee ist, dass ich darüber nachgedacht.

EDIT: Konkretes Beispiel

wenn ich ein abstraktes Element der Klasse mit einem Auto und Bus und Boot, das es implementiert ,, und jede dieser Klassen verfügt über verschiedene Attribute .... wie kann ein Manager zum Beispiel Traffic-Manager Suche nach einem bestimmten Artikel die abstrakte Klasse verwenden, ohne wirklich leid für die lange Frage zu Auto oder dem Bus ... Gießen

+0

Huh? Ich bin verwirrt. Welche Sprache verwendest du? –

+1

Egal welche Sprache er benutzt. Design-Patterns sind egal, dass sie in den meisten Sprachen implementiert werden können. – JonH

+0

ich stimme mit jonH ,,, aber irgendwie kann ich Java verwenden –

Antwort

9

Encapsulation

Die OO-Prinzip der Kapselung besagt, dass Sie das Objekt sollte seinen Zustand nach außen aussetzen. Wenn Ihr Objekt interne Informationen epxosiert, unterbricht es die Kapselung. Was nach OO-Design noch in Ordnung ist, ist, das Suchkriterium an das Objekt zu übergeben und es entscheiden zu lassen, ob sie übereinstimmen.

interface IVehicle{ 
    bool doesMatch(Map<String,String> searchCriterion) 
} 

Sie können einen Interator auf allen Fahrzeugen haben und diejenigen abrufen, die übereinstimmen, ohne die Kapselung zu unterbrechen. Eine bestimmte Implementierung von Fahrzeugen kann immer noch wie gewünscht erneut implementiert werden.

Besucher

Ansonsten würde ich vorschlagen, dass Sie am Muster Visitor aussehen. Die Idee ist dann, das ganze Objekt zu durchlaufen und eine zusätzliche Klasse zu behandeln, die für jeden spezifischen Typ behandelt wird. Dies bricht auch die reine Kapselung (weil das Objekt seine Daten dem Besucher aussetzen muss), aber es ist sehr elegant.

class VehicleSearchVisitor 
{ 
    Map<String,String> searchCriterion; 
    void visit(Car car) {...} 
    void visit(Bike bike) { ... } 
    .... 
} 

Meta-Programmierung

Die Idee Objekt, das sind selbstbeschreibende ein anderes Konzept, das Meta-Programmierung genannt wird. Die Präsentationsschicht dann introspect anderes Objekt zu wissen, wie man sie behandelt. Dies wird traditionell als eine fortgeschrittene OO-Technik betrachtet. Sie könnten benutzerdefinierte Anmerkungen erstellen, um das Feld Ihrer Klasse zu beschreiben, sodass die Präsentationsebene das entsprechende Label dynamisch darstellen kann. Dieselbe Idee wird zum Beispiel bei Hibernate-Annotationen verwendet. Meta-Programmierung muss sorgfältig durchgeführt werden, sonst stößt man auf andere Probleme.

Insteanceof

Verwendung insteanceof ist auch eine Form der Selbstbeobachtung (weil Sie das Objekt für seine Klasse fragen) und ist in der Regel abgeraten. Nicht weil es an sich falsch ist, sondern weil es dazu neigt, missbraucht zu werden. Wenn möglich, verlassen Sie sich auf traditionelle OO-Prinzipien. Mit instanceof missbräuchlich ist ein code smell.

Insgesamt würde ich empfehlen, einen Besucher für die Suche zu verwenden, und nicht verwenden Meta-Programmierung für die Präsentationsebene und stattdessen erstellen Sie eine einfache Seite pro Fahrzeugtyp.

+1

Ich würde die erste Antwort empfehlen - das macht für mich am meisten Sinn. Auf diese Weise entscheidet jedes Fahrzeug, ob es den Suchkriterien entspricht. –

4

Sie scheinen zu beschreiben, was Steve Yegge die Universal design pattern.

ruft

Die Verwendung von Schlüssel-Wert-Paaren mit einer GUI kann in einfachen Fällen funktionieren, aber es ist schwierig, es gut aussehen zu lassen. Wenn Ihre Objekthierarchie nicht tief ist und sehr erweiterbar sein soll, sollten Sie für jede konkrete Klasse ein separates Formular erstellen, da dies weniger Arbeit bedeutet und besser aussieht. Sie können weiterhin freigegebene GUI-Komponenten wiederverwenden, daher sollte das Hinzufügen einer neuen konkreten Klasse ziemlich einfach sein.

6

Ok, also erweitern Sie die Klasse, anstatt eine Schnittstelle zu implementieren. Wenn Sie Klassen Bus, Auto, LKW, Zug und alle IVehicle implementiert haben, die eine Funktion benötigen, die einen sortierbaren/durchsuchbaren Wert zurückgibt, könnten Sie alle als Typ IVehicle referenzieren und diese Methode für alle aufrufen .

Actionscript 3-Code:

package com.transportation.methods { 
    public interface IVehicle { 
    function getSpeed():Number; 
    function getOtherSortableOrSearchableValue():*; 
    } 
} 

und

public class Car extends Sprite implements IVehicle 

Sie benötigen getSpeed ​​() und getOtherSortableValue() in der Auto-Klasse zu definieren, und darauf verweisen können entweder ein Auto oder ein Fahrzeug. Da alle Verkehrsträger in meinem Beispiel IVehicle implementieren, können Sie diese beiden Funktionen aufrufen, solange Sie sie als IVehicles bezeichnen.

+0

das ist meine zweite Methode (die Hash-Karte-Methode) ist es nicht? –

+0

Ziemlich viel, ja. – Aaron

+0

Das ignoriert tatsächlich eine der ursprünglich angegebenen Einschränkungen, nämlich dass jedes Fahrzeug unterschiedliche Attribute haben kann. Also könnte ein Auto eine getSpeed ​​() haben, aber vielleicht würde ein Pogo-Stick nicht.Ein Pogo-Stick könnte ein getBounceHeight() haben, aber das macht für ein Auto keinen Sinn. Die Hash-Map-Idee würde mit diesen Situationen umgehen, aber diese spezielle Antwort würde dies nicht tun. –

0

Ich sehe nicht, welche Sprache Sie verwenden, aber warum nicht die abstrakte Klasse die Schnittstelle implementieren? Auf diese Weise ist es egal, wie viele konkrete Klassen Sie haben, solange sie alle von der abstrakten Klasse erben, die die Schnittstelle implementiert.

Hier ist, wie die Hierarchie aussehen würde, wenn Sie Java verwenden:

public interface IVehicle {/*your code goes here*/} 

public abstract class AbstractVehicle implements IVehicle{/*your code goes here*/} 

public class Car extends AbstractVehicle{/*your code goes here*/} 

Diese würden alle, natürlich, in verschiedenen Dateien definiert werden.

+0

wenn ich eine neue Klasse für Boot zum Beispiel hinzugefügt habe und es Attribute hat, die nicht mit dem Auto verbunden sind und nicht zur Fahrzeugschnittstelle hinzugefügt werden können ... was kann ich dann tun? –

+0

@Ahmed Kotb - Eine Möglichkeit, dies zu tun wäre, eine getAttributes() -Methode an die Schnittstelle hinzuzufügen, die eine HashMap zurückgibt, die Schlüssel/Wert-Paare der Attribute enthält. Aufrufende Klassen könnten dann die HashMap abfragen, für welche Attribute (Schlüssel) es gegebenenfalls existiert. Ihre Suche() -Methode könnte in der abstrakten Klasse definiert werden, um diese Überprüfung durchzuführen, bevor die eigentliche Suche stattfindet. – ssakl

+0

Ja, das, was ich in dem zweiten Vorschlag versucht habe, aber ich wollte wissen, ob es etwas Besseres gab oder ob das schon ein Designmuster war, das ich über –

0

wie kann ein Manager zum Beispiel Traffic-Manager Sucht nach einem bestimmten Artikel der abstrakte Klasse verwenden, ohne zu Auto Gießen oder Bus

So wie ich, dass für die abstrakte Klasse tun würde, eine haben abstrakte Methode, die einen ID-Wert (z. B. einen Enum-Wert) zurückgibt, der angibt, welcher konkrete Typ das Objekt ist, und jede konkrete Klasse überschreiben, die ihren ID-Wert zurückgibt.

Oder ich würde versuchen, C++ RTTI-Funktionen oder Pythons instanceof() Funktion zu verwenden.

Hilft das, oder beantworte ich die falsche Frage?

+0

Alle diese sind nicht sehr OO-ish-Lösungen und sie brechen LSP. –

+1

Ja, ich werde das tun, wenn ich keine anderen Optionen habe, da instance-of bedeutet, dass die Manager-Klasse die konkreten Klassen kennen muss, die ich nicht für die beste Sache in oop halte, da martinho sagte –

2

Dies scheint mir eine Art "zentralisiertes" Repository-Muster zu sein. Ich denke nicht, dass das eine gute Idee ist.

Was Sie empfehlen Sie tun, ist, statt einer zentralisierten Art und Weise der allgemeinen Suche, sollten Sie mehrere spezialisierte Repositories haben: eine für Autos, eine für Boote, eine für Flugzeuge usw.

Jede der Repositorys kennt die Details der jeweiligen Objekte.Sie können dann allgemeine Operationen zwischen diesen spezialisierten Repositorys, einer Repository-Basisklasse oder -Schnittstelle ausschließen. Wenn Sie sich nicht um die Details kümmern, verwenden Sie die Basis-Repository-Schnittstelle. Wenn Sie sich um die Details kümmern, verwenden Sie das spezialisierte Repository.

Hier ist ein einfaches Beispiel in Java (Ich verlasse Getter/Setter aus für Kürze - nicht Ihre Felder öffentlich machen):

interface Vehicle { int id; int maxSpeed; } 
class Car implements Vehicle { int doors; int id; int maxSpeed; } 
class Boat implements Vehicle { int buoyancy; int id; int maxSpeed; } 
class Plane implements Vehicle { int wingSpan; int id; int maxSpeed; } 

interface VehicleRepository<T extends Vehicle> { 
    T getFastest(); 
    T getSlowest(); 
    T getById(int id); 
} 

interface CarRepository inherits VehicleRepository<Car> { 
    List<Car> getCarsWithTwoDoors(); 
} 

interface BoatRepository inherits VehicleRepository<Boat> { 
    Boat getMostBuoyantBoat(); 
} 

interface PlaneRepository inherits VehicleRepository<Plane> { 
    List<Plane> getPlanesByWingspan(); 
} 
+0

ja das war mein erster Vorschlag ... durch die Schaffung einer zusätzlichen Ebene von Schnittstellen denke ich, das einzige Problem hier, wenn ich ein anderes Element wie Raketen hinzufügen, ich muss ein Raketen-Repository richtig erstellen? –

+0

Ja. Und ich glaube nicht, dass daran etwas verkehrt ist, weil Sie die vorhandenen Klassen nicht ändern müssen, um Raketen hinzuzufügen. Wenn Sie eine zentralisierte Sache verwenden, müssen Sie sie jedes Mal ändern, wenn Sie etwas anderes hinzufügen. –

2

Das klingt wie ein Job für das Besuchermuster. Sie haben eine Sammlung von abstrakten Objekten und Sie wissen nicht, was jeder ist. Mit Visitor können Sie über alle Objekte iterieren und für jede eine spezifische Aktion ausführen. Das GOF Patterns-Buch bietet gute Details zum Besuchermuster, aber ich werde versuchen, hier ein gutes Java-Beispiel zu bieten.

public class Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public interface VehicleVisitor { 
    public void visit(Vehicle vehicle); 
    public void visit(Car car); 
    public void visit(Bus bus); 
    public void visit(Truck truck); 
    // Augment this interface each time you add a new subclass. 
} 

public class Car extends Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class Bus extends Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class Truck extends Vehicle { 
    public void accept(VehicleVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class VehicleSearch implements VehicleVisitor { 
    protected String name; 
    public List<Vehicle> foundList = 
     new ArrayList<Vehicle>(); 
    public VehicleSearch(String name) { 
     this.name = name; 
    } 
    public void visit(Vehicle vehicle) { 
     return; 
    } 
    public void visit(Car car) { 
     if (car.getModel().contains(name)) { 
      foundList.add(car); 
     } 
    } 
    public void visit(Bus bus) { 
     if (bus.getManufacturerModel().contains(name)) { 
      foundList.add(bus); 
     } 
    } 
    public void visit(Truck truck) { 
     if (truck.getLineModel().contains(name)) { 
      foundList.add(truck); 
     } 
    } 
} 

public class Manager { 
    protected List<Vehicle> vehicleList; 
    public List<Vehicle> search(String name) { 
     VehicleSearch visitor = 
      new VehicleSearch(name); 
     for (Vehicle vehicle : vehicleList) { 
      vehicle.accept(visitor); 
     } 
     return visitor.foundList; 
    } 
} 

Auf den ersten Blick, ja, man könnte einfach ein Suchverfahren zum Fahrzeugklasse hinzufügen und die der Liste für jedes Mitglied nennen, aber das Besuchermuster ermöglicht es Ihnen, mehrere Operationen über die Liste der Fahrzeuge so zu definieren, dass Sie müssen der Vehicle-Klasse für jede Klasse keine neue Methode hinzufügen.

Ein Nachteil des Visitor-Musters ist jedoch, dass der Besucher selbst geändert werden muss, wenn Sie eine neue Objektklasse hinzufügen. Wenn Sie in diesem Beispiel ein RocketShip-Fahrzeug zum System hinzufügen, müssen Sie dem Besucher eine visit(RocketShip rocketShip)-Methode hinzufügen.

Sie können dieses Problem beheben, indem Sie ein VehicleVisitorTemplate erstellen, bei dem alle Besuchsmethoden überschrieben werden. Ihre Operationen bilden die Vorlagenklasse ab und müssen nur die erforderlichen Methoden überschreiben. Wenn Sie eine neue Klassen- und Besuchsmethode hinzufügen müssen, fügen Sie sie der Schnittstelle und der Vorlagenklasse hinzu, und dann müssen alle anderen Operationen nicht aktualisiert werden, es sei denn, dies ist erforderlich.