Angenommen, ich habe eine Klasse für andere Klassen, die erweitert werden sollen:
public class LoginPage {
public String userId;
public String session;
public boolean checkSessionValid() {
}
}
und einige Unterklassen:
public class HomePage extends LoginPage {
}
public class EditInfoPage extends LoginPage {
}
Tatsächlich hat die Unterklasse keine Methoden zum Überschreiben. Außerdem würde ich nicht generisch auf die HomePage zugreifen, dh: Ich würde nicht Folgendes tun:
for (int i = 0; i < loginPages.length; i++) {
loginPages[i].doSomething();
}
Ich möchte nur die Anmeldeseite wiederverwenden. Laut https://stackoverflow.com/a/53354 sollte ich hier jedoch die Komposition bevorzugen, da ich die Schnittstelle LoginPage nicht benötige. Daher verwende ich hier keine Vererbung:
public class HomePage {
public LoginPage loginPage;
}
public class EditInfoPage {
public LoginPage loginPage;
}
aber das problem kommt hier, bei der neuen version, der code:
public LoginPage loginPage;
dupliziert, wenn eine neue Klasse hinzugefügt wird. Und wenn LoginPage Setter und Getter benötigt, müssen mehr Codes kopiert werden:
public LoginPage loginPage;
private LoginPage getLoginPage() {
return this.loginPage;
}
private void setLoginPage(LoginPage loginPage) {
this.loginPage = loginPage;
}
Meine Frage lautet also: Verstößt "Komposition über Vererbung" gegen das "Trockenprinzip"?
quelle
extends LoginPage
überall ein Duplikat . CHECK MATE!LoginPage
einem Dekorateur. Keine Vervielfältigung mehr, nur ein einfachespage = new LoginPage(new EditInfoPage())
und Sie sind fertig. Oder Sie verwenden das Open-Closed-Prinzip, um das Authentifizierungsmodul zu erstellen, das dynamisch zu jeder Seite hinzugefügt werden kann. Es gibt viele Möglichkeiten, mit Code-Duplikaten umzugehen, vor allem, wenn eine neue Abstraktion gefunden werden soll.LoginPage
Wahrscheinlich handelt es sich um einen schlechten Namen. Wenn dies nicht der Fall ist, möchten Sie sicherstellen, dass der Benutzer beim Browsen auf dieser Seite authentifiziert wird und zurLoginPage
entsprechenden Fehlermeldung umgeleitet wird.Antworten:
Warten Sie, Sie sind besorgt, dass es sich wiederholt
an zwei Stellen verletzt DRY? Nach dieser Logik
kann jetzt immer nur in einem Objekt in der gesamten Codebasis existieren. Bleh.
TROCKEN ist eine gute Sache zu beachten, aber komm schon. Außerdem
wird immer in Ihrer Alternative dupliziert, so dass auch anal über DRY keinen Sinn ergibt.
Gültige DRY-Bedenken konzentrieren sich in der Regel auf dasselbe Verhalten, das an mehreren Stellen erforderlich ist, und werden an mehreren Stellen definiert, sodass eine Änderung dieses Verhaltens an mehreren Stellen erforderlich ist. Treffen Sie Entscheidungen an einem Ort und Sie müssen sie nur an einem Ort ändern. Dies bedeutet nicht, dass immer nur ein Objekt einen Verweis auf Ihre LoginPage enthalten kann.
DRY sollte nicht blind verfolgt werden. Wenn Sie duplizieren, weil das Kopieren und Einfügen einfacher ist, als sich eine gute Methode oder einen guten Klassennamen auszudenken, liegen Sie wahrscheinlich falsch.
Wenn Sie jedoch den gleichen Code an einer anderen Stelle ablegen möchten, da diese andere Stelle einer anderen Verantwortung unterliegt und wahrscheinlich unabhängig geändert werden muss, ist es wahrscheinlich ratsam, die Durchsetzung von DRY zu lockern und dass dieses identische Verhalten eine andere Identität aufweist . Es ist die gleiche Art des Denkens, die darin besteht, magische Zahlen zu verbieten.
Bei DRY geht es nicht nur darum, wie der Code aussieht. Es geht darum, die Details einer Idee nicht mit sinnlosen Wiederholungen zu verbreiten und die Betreuer dazu zu zwingen, Dinge mit sinnlosen Wiederholungen zu reparieren. Wenn du versuchst dir selbst zu sagen, dass die gedankenlose Wiederholung nur deine Konvention ist, dass die Dinge auf eine wirklich schlechte Art und Weise geleitet werden.
Was ich denke, dass Sie wirklich versuchen, sich zu beschweren, wird Kesselschildcode genannt. Ja, die Verwendung von Komposition anstelle von Vererbung erfordert Code für das Textfeld. Nichts wird kostenlos verfügbar gemacht, Sie müssen Code schreiben, der es verfügbar macht. Mit diesem Boilerplate kommt die Flexibilität des Zustands, die Fähigkeit, die exponierte Schnittstelle einzugrenzen, den Dingen unterschiedliche Namen zu geben, die für ihre Abstraktionsebene geeignet sind, eine gute indirekte Funktion, und Sie verwenden, woraus Sie von außen zusammengesetzt sind, nicht die innen, so dass Sie es normale Schnittstelle konfrontiert sind.
Aber ja, es ist eine Menge zusätzlicher Tastatureingaben. Solange ich das Jojo-Problem des Auf- und Abspringens eines Vererbungsstapels verhindern kann, während ich den Code lese, lohnt es sich.
Jetzt ist es nicht so, dass ich es ablehne, jemals Vererbung zu verwenden. Eine meiner Lieblingsanwendungen ist es, Ausnahmen neue Namen zu geben:
quelle
int x;
kann nicht mehr als null Mal in einer Codebasis existierenint a; int b; int x;
ich darauf drängen, dass Sie entfernt werden.Ein häufiges Missverständnis mit dem DRY-Prinzip besteht darin, dass es in irgendeiner Weise damit zusammenhängt, dass Codezeilen nicht wiederholt werden. Das DRY - Prinzip ist „Jedes Stück Wissen eine einzige, eindeutige, verbindliche Darstellung innerhalb eines Systems haben muß“ . Es geht um Wissen, nicht um Code.
LoginPage
weiß, wie man die Seite zum Einloggen zeichnet. Wenn manEditInfoPage
weiß, wie man das macht, wäre das ein Verstoß. EinschließlichLoginPage
über Zusammensetzung ist nicht eine Verletzung des DRY - Prinzip in keiner Weise.Das DRY-Prinzip ist vielleicht das am häufigsten missbrauchte Prinzip in der Softwareentwicklung, und es sollte immer nicht als Prinzip betrachtet werden, Code nicht zu duplizieren, sondern als Prinzip, abstraktes Domänenwissen nicht zu duplizieren. Tatsächlich duplizieren Sie in vielen Fällen Code , wenn Sie DRY richtig anwenden , und dies ist nicht unbedingt eine schlechte Sache.
quelle
Kurze Antwort: Ja, bis zu einem gewissen Grad.
Auf den ersten Blick kann die Vererbung manchmal einige Codezeilen einsparen, da sie besagt, dass "meine wiederverwendende Klasse alle öffentlichen Methoden und Attribute 1: 1 enthält". Wenn eine Komponente also eine Liste von 10 Methoden enthält, müssen diese in der geerbten Klasse nicht im Code wiederholt werden. Wenn in einem Kompositionsszenario 9 dieser 10 Methoden durch eine Wiederverwendungskomponente öffentlich verfügbar gemacht werden sollen, müssen 9 delegierende Aufrufe notiert und der verbleibende ausgelassen werden, da führt kein Weg daran vorbei.
Warum ist das erträglich? Sehen Sie sich die Methoden an, die in einem Kompositionsszenario repliziert werden. Diese Methoden delegieren ausschließlich Aufrufe an die Schnittstelle der Komponente und enthalten daher keine echte Logik.
Der Kern des DRY-Prinzips besteht darin, zu vermeiden, dass zwei Stellen im Code dieselben logischen Regeln enthalten. Wenn sich diese logischen Regeln ändern, ist es im Nicht-DRY-Code einfach, eine dieser Stellen anzupassen und die andere zu vergessen. was einen Fehler einführt.
Da das Delegieren von Aufrufen jedoch keine Logik enthält, werden diese normalerweise nicht geändert und verursachen daher kein echtes Problem, wenn "die Komposition der Vererbung vorgezogen wird". Und selbst wenn sich die Schnittstelle der Komponente ändert, was zu formalen Änderungen bei allen Klassen führen kann, die die Komponente verwenden, teilt uns der Compiler in einer kompilierten Sprache mit, wenn wir vergessen haben, einen der Aufrufer zu ändern.
Ein Hinweis zu Ihrem Beispiel: Ich weiß nicht, wie Sie
HomePage
und IhrEditInfoPage
Aussehen aussehen, aber wenn sie Anmeldefunktionen haben und einHomePage
(oder einEditInfoPage
) ein istLoginPage
, ist die Vererbung hier möglicherweise das richtige Werkzeug. Ein weniger umstrittenes Beispiel, bei dem die Komposition in offensichtlicherer Weise das bessere Werkzeug sein wird, würde die Dinge wahrscheinlich klarer machen.Vorausgesetzt, weder a
HomePage
nochEditInfoPage
a istLoginPage
, und man möchte das letztere wiederverwenden, wie Sie geschrieben haben, als es ziemlich wahrscheinlich ist, dass man nur einige Teile vonLoginPage
, nicht alles benötigt. Wenn dies der Fall ist, könnte ein besserer Ansatz als die Verwendung der Komposition in der gezeigten Weise darin bestehen,extrahieren Sie den wiederverwendbaren Teil von
LoginPage
in eine eigene KomponenteVerwenden Sie diese Komponente erneut
HomePage
undEditInfoPage
genauso, wie sie jetzt im Inneren verwendet wirdLoginPage
Auf diese Weise wird in der Regel viel klarer, warum und wann Komposition über Vererbung der richtige Ansatz ist.
quelle