Frage

Ich habe zwei parallel Vererbungsketten:

Vehicle <- Car
        <- Truck <- etc.

VehicleXMLFormatter <- CarXMLFormatter
                    <- TruckXMLFormatter <- etc.

Meine Erfahrung ist, dass parallel Vererbungshierarchien eine Wartung Kopfschmerzen werden können, wie sie wachsen.

d. NICHT Hinzufügen toXML(), toSoap(), toYAML() Methoden zu meinen Hauptklassen.

Wie vermeide ich eine Parallele Vererbungshierarchie, ohne das Konzept der Trennung von Bereichen zu brechen?

War es hilfreich?

Lösung

Ich denke das Besuchermuster zu verwenden.

public class Car : Vehicle
{
   public void Accept( IVehicleFormatter v )
   {
       v.Visit (this);
   }
}

public class Truck : Vehicle
{
   public void Accept( IVehicleFormatter v )
   {
       v.Visit (this);
   }
}

public interface IVehicleFormatter
{
   public void Visit( Car c );
   public void Visit( Truck t );
}

public class VehicleXmlFormatter : IVehicleFormatter
{
}

public class VehicleSoapFormatter : IVehicleFormatter
{
}

Damit vermeiden Sie einen zusätzlichen Vererbungsbaum, und halten Sie die Logik Formatierung von Ihren Fahrzeug-Klassen getrennt. Offcourse, wenn Sie ein neues Fahrzeug zu erstellen, müssen Sie eine andere Methode, um die Formatter-Schnittstelle hinzufügen müssen (und implementieren diese neue Methode in allen Implementierungen der Formatierungsprogramm-Schnittstelle).
Aber ich denke, dass dies besser als eine neue Fahrzeugklasse zu schaffen und für jeden IVehicleFormatter Sie haben, erstellen Sie eine neue Klasse, die diese neue Art von Fahrzeug verarbeiten kann.

Andere Tipps

Ein weiterer Ansatz ist es, ein Push-Modell zu übernehmen, anstatt ein Pull-Modell. Normalerweise müssen Sie verschiedene Formatierer, weil Sie Verkapselung sind zu brechen, und haben so etwas wie:

class TruckXMLFormatter implements VehicleXMLFormatter {
   public void format (XMLStream xml, Vehicle vehicle) {
      Truck truck = (Truck)vehicle;

      xml.beginElement("truck", NS).
          attribute("name", truck.getName()).
          attribute("cost", truck.getCost()).
          endElement();
...

, wo Sie Daten von dem spezifischen Typ in die Formatierer sind zu ziehen.

Erstellen Sie stattdessen eine formatunabhängige Datensenke und den Fluss invertieren, so die spezifische Art Daten zur Senke drückt

class Truck  implements Vehicle  {
   public DataSink inspect ( DataSink out ) {
      if ( out.begin("truck", this) ) {
          // begin returns boolean to let the sink ignore this object
          // allowing for cyclic graphs.
          out.property("name", name).
              property("cost", cost).
              end(this);
      }

      return out;
   }
...

Das heißt, Sie haben immer noch die Daten verkapselt, und Sie sind Fütterung nur Daten zur Senke markiert. Eine XML-Senke könnte dann bestimmte Teile der Daten ignorieren, vielleicht einen Teil davon neu ordnen, und die XML schreiben. Es könnte sogar intern auf verschiedene sink Strategie delegieren. Aber das Waschbecken muss nicht unbedingt über die Art des Fahrzeugs sorgen, nur, wie die Daten in einem gewissen Format darzustellen. Mit internierten globale IDs statt Inline-Strings hilft, den Rechenaufwand niedrig hält (nur Fragen, wenn Sie ASN.1 oder andere enge Formate sind zu schreiben).

Sie könnten versuchen, die Vererbung für Ihre Formatierer zu vermeiden. Einfach ein VehicleXmlFormatter machen, die mit Cars, Trucks umgehen können ... Reuse soll durch Zerhacken der Verantwortlichkeit zwischen Methoden zu erreichen, leicht und durch eine gute dispatch-Strategie herauszufinden. Vermeiden Magie Überlastung; so spezifisch wie möglich sein, in Methoden in Ihrem Formatierungsprogramm zu benennen (zum Beispiel formatTruck(Truck ...) statt format(Truck ...)).

Nur Besucher verwenden, wenn Sie den Double-Dispatch müssen: Wenn Sie Objekte vom Typ Vehicle haben, und Sie wollen, dass sie in XML zu formatieren, ohne den eigentlichen konkreten Typen zu kennen. Besucher selbst lösen nicht das Grundproblem der Wiederverwendung in Ihrem Formatierungsprogramm zu erzielen, und können zusätzliche Komplexität einführen Sie nicht benötigen. Die oben genannten Regeln für die Wiederverwendung von Methoden (und Versand Hacken) sowie auf Ihre Besucher Umsetzung gelten.

Sie können mit Bridge_pattern

Bridge Muster eine Abstraktion von ihrer Implementierung zu entkoppeln, so dass die zwei unabhängig voneinander variieren kann.

Zwei orthogonale Klassenhierarchien (Die Abstraction Hierarchie und Implementierung Hierarchie) verbunden ist, unter Verwendung von Zusammensetzung (und nicht Vererbung) .Dieses Zusammensetzung beiden Hierarchien hilft unabhängig zu verändern.

Die Umsetzung nie bezieht sich Abstraction . Abstraktion enthält Implementierung Schnittstelle als Mitglied (durch Zusammensetzung).

Kommen wir zurück zu Ihrem Beispiel:

Vehicle ist Abstraction

Car und Truck sind RefinedAbstraction

Formatter ist Implementor

XMLFormatter, POJOFormatter sind ConcreteImplementor

Pseudo-Code:

 Formatter formatter  = new XMLFormatter();
 Vehicle vehicle = new Car(formatter);
 vehicle.applyFormat();

 formatter  = new XMLFormatter();
 vehicle = new Truck(formatter);
 vehicle.applyFormat();

 formatter  = new POJOFormatter();
 vehicle = new Truck(formatter);
 vehicle.applyFormat();

im Zusammenhang Beitrag:

Wann verwendet man die Brücke Muster? Wie unterscheidet es sich von Adapter-Muster?

Warum macht IXMLFormatter keine Schnittstelle mit ToXml (), toSoap (), auf YAML () -Methoden und macht das Fahrzeug, Auto und Lastwagen das alles umsetzen? Was ist falsch mit diesem Ansatz?

Ich möchte Frederiks Antwort Generika hinzuzufügen.

public class Car extends Vehicle
{
   public void Accept( VehicleFormatter v )
   {
       v.Visit (this);
   }
}

public class Truck extends Vehicle
{
   public void Accept( VehicleFormatter v )
   {
       v.Visit (this);
   }
}

public interface VehicleFormatter<T extends Vehicle>
{
   public void Visit( T v );
}

public class CarXmlFormatter implements VehicleFormatter<Car>
{
    //TODO: implementation
}

public class TruckXmlFormatter implements VehicleFormatter<Truck>
{
    //TODO: implementation
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top