Verstößt die Mehrfachvererbung gegen das Prinzip der Einzelverantwortung?

18

Wenn Sie eine Klasse haben, die von zwei verschiedenen Klassen erbt, bedeutet dies nicht, dass Ihre Unterklasse automatisch (mindestens) zwei Dinge erledigt, eines von jeder Superklasse?

Ich glaube, es gibt keinen Unterschied, wenn Sie mehrere Schnittstellenvererbung haben.

Bearbeiten: Um klar zu sein, glaube ich, dass, wenn Unterklassen mehrerer Klassen gegen die SRP verstoßen, die Implementierung mehrerer (Nicht-Marker- oder Basisschnittstellen (z. B. vergleichbare)) Schnittstellen ebenfalls gegen die SRP verstößt.

m3th0dman
quelle
Um es klar zu sagen, glauben Sie auch, dass die Implementierung mehrerer Schnittstellen die SRP verletzt?
Ich glaube, dass die Implementierung mehrerer (Nicht-Marker-) Schnittstellen auch die SRP verletzt, wenn die Unterklasse mehrerer Klassen gegen die SRP verstößt.
m3th0dman
Ich würde diese Frage positiv bewerten, aber Sie haben Ihre Meinung in die Frage eingebettet, der ich nicht zustimme.
Nicole
1
@ NickC: Ich habe meine Meinung nicht gesagt (du hättest den obigen Kommentar lesen sollen); Ich werde die Frage bearbeiten, um sie klarer zu machen.
m3th0dman
1
@NickC Ich habe keine Meinung, deshalb habe ich gefragt. Ich habe nur gesagt, dass die beiden verwandt sind (Interface-Vererbung und Klassenvererbung); Verstößt es gegen die Klassenvererbung, so verstößt es auch gegen die Schnittstellenvererbung. Verstößt es nicht gegen die eine, so verstößt es auch nicht gegen die andere. Und ich interessiere mich nicht für die Abstimmung ...
m3th0dman

Antworten:

17

Im engeren Sinne lautet die Antwort "Ja": Unter der Annahme, dass Ihre Basisklassen oder Schnittstellen für einen einzigen Zweck konzipiert sind, wird durch die Übernahme beider Klassen eine Klasse mit mehreren Verantwortlichkeiten erstellt. Ob dies jedoch "eine schlechte Sache" ist oder nicht, hängt von der Art der Klassen oder Schnittstellen ab, die Sie erben.

Sie können Ihre Klassen und Interfaces in zwei Hauptgruppen unterteilen - die, die sich mit der wesentlichen Komplexität Ihres Systems befassen, und die, die sich mit seiner zufälligen Komplexität befassen. Wenn Sie von mehr als einer Klasse der "wesentlichen Komplexität" erben, ist dies schlecht. Wenn Sie von einer "wesentlichen" und einer oder mehreren "zufälligen" Klassen erben, ist dies in Ordnung.

In einem Abrechnungssystem können Sie beispielsweise Klassen für die Darstellung von Rechnungen und Abrechnungszyklen (die sich mit der wesentlichen Komplexität befassen) und Klassen für dauerhafte Objekte (die sich mit der zufälligen Komplexität befassen) verwenden. Wenn Sie so erben

class BillingCycleInvoice : public BillingCycle, public Invoice {
};

Es ist schlimm: Sie BillingCycleInvoicehaben eine gemischte Verantwortung in Bezug auf die wesentliche Komplexität des Systems.

Auf der anderen Seite, wenn Sie so erben

class PersistentInvoice : public Invoice, public PersistentObject {
};

Ihre Klasse ist in Ordnung: Technisch gesehen werden zwei Probleme gleichzeitig behandelt. Da jedoch nur eines von ihnen von entscheidender Bedeutung ist, können Sie das Erben des Versehens als "Geschäftskosten" abschreiben.

dasblinkenlight
quelle
3
Ihr zweites Beispiel ähnelt sehr dem, was bei der aspektorientierten Programmierung ein Querschnittsthema wäre. Dies bedeutet, dass Ihre Objekte manchmal Aufgaben ausführen müssen (z. B. Protokollierung, Zugriffskontrolltests oder Dauerhaftigkeit), die nicht zu ihrem Kernfokus gehören, und dass dies mit Mehrfachvererbung möglich ist. Das ist eine angemessene Verwendung des Tools.
Das Einfachste ist, wenn das Implementieren einer neuen Schnittstelle das Abschließen der Klassenfunktionalität erfordert, ist dies nicht der Fall. Wenn Sie jedoch eine neue Zuständigkeits-Implementierungsschnittstelle mit einer anderen Klasse hinzufügen, wird "
Injizieren
9

Die SRP ist eine Richtlinie, um Gottklassen zu vermeiden, die schlecht sind, aber sie kann auch zu wörtlich genommen werden, und ein Projekt startet einen Ballon mit Tonnen von Klassen, die nichts wirklich können, ohne dass eine Handvoll kombiniert wird. Mehrfachvererbung / Mehrfachschnittstellen können ein Zeichen dafür sein, dass eine Klasse zu groß wird, aber es kann auch ein Zeichen dafür sein, dass Ihr Fokus für die jeweilige Aufgabe viel zu genau ist und Ihre Lösung überarbeitet wurde, oder es kann einfach eine perfekte sein Ein gültiger Anwendungsfall für die Mehrfachvererbung und Ihre Sorgen sind unbegründet.

Software-Design ist ebenso Kunst wie Wissenschaft, es ist ein Balanceakt vieler konkurrierender Interessen. Wir haben Regeln, die helfen, Dinge zu vermeiden, von denen wir wissen, dass sie zu weit in eine Richtung gehen und Probleme verursachen. Diese Regeln können uns jedoch genauso leicht an einen Ort bringen, der genauso schlecht oder schlechter ist als das, was wir ursprünglich vermeiden wollten.

Ryathal
quelle
4

Etwas. Konzentrieren wir uns auf das Hauptprinzip für SRP:

Eine Klasse sollte nur einen Grund haben, sich zu ändern.

Mehrfachvererbung erzwingt dies nicht, führt aber dazu. Wenn die Klasse ihre Basisklassen überschreibt oder implementiert, sind dies tendenziell mehr Gründe für eine Änderung. Auf diese Weise ist die Mehrfachvererbung von Schnittstellen in der Regel problematischer als die Klassenvererbung. Wenn zwei Klassen geerbt, aber nicht überschrieben werden, liegt der Grund für die Änderung in den Basisklassen und nicht in der neuen Klasse.

Die Orte, an denen die Mehrfachvererbung nicht gegen SRP verstößt, sind die Orte, an denen alle Basistypen bis auf einen nicht von der Klasse implementiert werden, sondern als eine Eigenschaft fungieren, die die Klasse zufällig aufweist. Betrachten Sie IDisposablein C #. Dinge, die verfügbar sind, haben keine zwei Verantwortlichkeiten, die Schnittstelle fungiert lediglich als Sicht auf Klassen, die das Merkmal teilen, aber entschieden unterschiedliche Verantwortlichkeiten haben.

Telastyn
quelle
2

Meiner Meinung nach nicht. Für mich besteht eine einzige Verantwortung darin, "einen Benutzer meiner Anwendung zu modellieren". Möglicherweise muss ich diese Daten über einen Webdienst bereitstellen und in einer relationalen Datenbank speichern. Ich könnte diese Funktionalität bereitstellen, indem ich ein paar Mix-In-Klassen erbe.

Das bedeutet nicht, dass die Klasse drei Verantwortlichkeiten hat. Dies bedeutet, dass ein Teil der Verantwortung für die Modellierung eines Benutzers die Unterstützung von Serialisierung und Persistenz erfordert.

Kevin Cline
quelle
2

Keineswegs. In Java können Sie eine Schnittstelle haben, die eine Art Domänenobjekt darstellt, z. B. ein Foo. Es muss möglicherweise mit anderen Foo-Objekten verglichen werden und implementiert daher Comparable (mit der in einer Comparator-Klasse ausgelagerten Vergleichslogik). Möglicherweise muss dieses Objekt auch serialisierbar sein, damit es über das Netzwerk transportiert werden kann. und es muss möglicherweise klonbar sein. Somit implementiert die Klasse drei Schnittstellen, verfügt jedoch über keine Logik, um sie zu unterstützen, die über die von Foo unterstützte hinausgeht.

Das heißt, es ist dumm, die SRP auf die Spitze zu treiben. Wenn eine Klasse mehr als eine Methode definiert, verstößt sie gegen die SRP? Alle Java-Objekte haben eine toString () -Methode und eine equals (Object) -Methode, dh JEDES Objekt in Java ist sowohl für die Gleichheit als auch für die Selbstdarstellung verantwortlich. Ist das etwas schlechtes?

Matthew Flynn
quelle
1

Ich denke, es hängt davon ab, wie Sie die Verantwortung definieren. Im klassischen iostream-Beispiel wird die SRP nicht verletzt. Der IOstream ist für die Verwaltung eines bidirektionalen Streams verantwortlich.

Nachdem Sie keine primitiven Verantwortlichkeiten mehr haben, wechseln Sie zu allgemeineren, übergeordneten Verantwortlichkeiten. Wie definieren Sie die Verantwortung Ihres Haupteinstiegspunkts? Seine Verantwortung muss "der Haupteinstiegspunkt sein" und nicht "alles, was meine App tut", sonst ist es unmöglich, die SRP nicht zu verletzen und eine Anwendung zu erstellen.

Steinmetalle
quelle