Wie vermeide ich es, viele Passthrough-Funktionen in einen Wrapper zu schreiben?

13

Ich habe eine Klasse, die eine andere Klasse eines gemeinsamen Basistyps umschließt. Da die Basistyp-Schnittstelle ziemlich groß ist, müssen viele Passthrough-Funktionen geschrieben werden. Ich suche nach einem Weg, dies zu vermeiden.

Machen wir ein Beispiel:

      Car
     /   \
Volvo     VolvoWithTrailer

Jetzt muss ich jede Funktion in der Autoschnittstelle für VolvoWithTrailer implementieren und die entsprechende Funktion für das umschlossene Volvo-Objekt aufrufen, außer vielleicht GetMaxSpeed ​​(), das etwas niedriger zurückgibt. Grundsätzlich werde ich gerne viele Funktionen haben

int VolvoWithTrailer::GetNumSeats() {
  return mVolvo.GetNumSeats()
}

Eine naheliegende Möglichkeit, dies zu lösen, besteht darin, VolvoWithTrailer zu einer Unterklasse von Volvo zu machen

    Car
     |
   Volvo
     |
VolvoWithTrailer

aber das scheint das Prinzip der Bevorzugung der Komposition gegenüber der Vererbung zu verletzen.

Wie könnte ich sonst vermeiden, all diese Wrapper zu schreiben (die Sprache ist C ++)? Oder - wenn das Ihre Position ist - warum sollte ich sie einfach schreiben / einfach Vererbung verwenden? Gibt es Template-Magie, die dabei helfen könnte?

Sarien
quelle
2
Habe das nicht zu viel ausprobiert und in Java nachgedacht. Was ist mit einer "Trailer" -Schnittstelle? VolvoWithTrailer würde den Volvo erweitern und die Trailer-Schnittstelle implementieren?
7
Bevorzugen Sie die Komposition nur dann gegenüber der Vererbung, wenn dies sinnvoll ist.
Robert Harvey
@RobertHarvey: +1. Das ist eine Richtlinie, kein "Prinzip" und schon gar kein absolutes Gebot.
Mason Wheeler
In Python können Sie dies mit Selbstbeobachtung lösen (was C # Reflexion nennt).
user16764
1
Kann Ihre IDE keinen Code generieren?
Amy Blankenship

Antworten:

5

Meiner Meinung nach sollten Sie alles schreiben, was Sie schreiben müssen, um die Langzeitstabilität und Wartbarkeit des Programms zu verbessern. Was nützt es, heute nicht mehr als 20 Codezeilen zu schreiben, wenn Sie jedes Mal, wenn Sie etwas berühren, das diesen Code verwendet, oder wenn Sie zur Aufrechterhaltung dieser Klasse zurückkehren, zusätzliche 5 Minuten oder mehr kosten, weil Sie heute keine Zeit eingegeben haben?

Die Frage lautet dann also: "Was müssen Sie schreiben?" Ich glaube nicht, dass Sie genug Informationen zur Verfügung stellen, um eine endgültige Antwort zu geben, daher liste ich nur einige Dinge auf, über die ich bei einer Entscheidung nachdenken würde:

1. Müssen Sie das Trailer-Objekt jetzt oder in absehbarer Zukunft für sich haben?
Wenn ja, haben Sie ein ziemlich gutes Argument dafür, den Wrapper um beide zusammengesetzten Objekte zu legen, da Sie bereits den einen oder anderen Wrapper verwenden müssen. Könnte auch konsequent sein.

2. Befindet sich einer der drei Typen (Car, Trailer, CarWithTrailer) in einem Bereich des Codes, auf den Entwickler häufig eingehen, oder wird dies wahrscheinlich der Fall sein?
Wenn ja, seien Sie sehr vorsichtig, denn die Folgen einer falschen Auswahl können sich jedes Mal, wenn der Code berührt wird, sehr kostspielig amortisieren. Wenn nicht, kann es keinen Unterschied machen, was Sie entscheiden. Wähle einfach eine Richtung und mache mit.

3. Was ist am einfachsten zu verstehen?
Springt ein Ansatz auf Sie zu, dass jemand, der hinter Sie kommt, sofort "bekommt", was Sie versuchen zu tun? Haben Ihre Teamkollegen bestimmte Vorurteile, die es ihnen möglicherweise erschweren, einen Ansatz beizubehalten? Wenn alle Dinge gleich sind, wählen Sie die Lösung mit der niedrigsten kognitiven Belastung.

4. Gibt es zusätzliche Vorteile bei der Verwendung eines Ansatzes gegenüber einem anderen?
Eine Sache, die mir auffällt, ist, dass die Wrapper-Idee einen deutlichen Vorteil hat, wenn Sie schreiben, dass Ihr CarWithTrailerWrapper jedes Auto und jeden Anhänger in einen CarWithTrailer einwickeln kann. Anschließend schreiben Sie alle Ihre Passthrough-Methoden auf, aber Sie schreiben sie nur einmal und nicht einmal für jede Fahrzeugklasse.

Dies macht Ihre anfängliche Investition in das Schreiben der Durchleitungsmethoden viel billiger, wenn Sie mehr Autos und Anhänger hinzufügen. Dies verringert auch die Versuchung, Dinge zu direkt an den Volvo oder VolvoWithTrailer zu koppeln, und verringert gleichzeitig die Folgen der Kopplung an den CarWithTrailerWrapper, da Sie mit nur dieser einen Implementierung viel mehr tun können.

Vielleicht gibt es bestimmte Vorteile, die Sie durch die Verwendung der Erweiterungsmethode kostenlos erhalten würden, aber ich sehe sie nicht.

OK, es sieht so aus, als hätte ich mich überlegt, ob ich die Pass-Through-Methoden für nicht-triviale Anwendungen ausfüllen soll.

Ich bin kein C ++ - Programmierer, daher habe ich keine Ahnung, welche Tools zur Codegenerierung Ihnen zur Verfügung stehen. Die von mir verwendete IDE kann Methoden aus einem Interface generieren und ich kann Codevorlagen hinzufügen , mit denen das Schreiben von Pass-Throughs ziemlich einfach ist. Wenn Sie keinen Zugriff auf eine IDE haben, die dies tut, können Sie Excel tatsächlich weitestgehend Code für Sie schreiben lassen .

Amy Blankenship
quelle
3

Da die Basistyp-Schnittstelle ziemlich groß ist, müssen viele Passthrough-Funktionen geschrieben werden.

Und da ist dein Problem.

Schnittstellen sollten nur eine Verantwortung übernehmen. Wenn Sie sie dünn und fokussiert halten, wird der Umfang der Passthrough-Arbeit begrenzt, die Sie im Fall eines Decorators, Proxys oder eines anderen Wrappers möglicherweise ausführen müssen (zusätzlich dazu, dass der Klassensimpiler verwendet, getestet, gewartet und erweitert werden kann).

Telastyn
quelle
5
UH, was? Eine Klasse kann mehrere Schnittstellen implementieren und Schnittstellen können voneinander erben. Selbst wenn ein Interface ziemlich klein ist, hat das nichts damit zu tun, ob Klassen, die es implementieren, viele Funktionen oder Pass-Through-Funktionen benötigen. Je kleiner und die gut definierten Klassen , desto wahrscheinlicher ist es , dass Sie werden Funktionen Durch passieren muss.
Amy Blankenship