Par exemple, considérons que j'ai une classe pour que d'autres classes puissent s'étendre:
public class LoginPage {
public String userId;
public String session;
public boolean checkSessionValid() {
}
}
et quelques sous-classes:
public class HomePage extends LoginPage {
}
public class EditInfoPage extends LoginPage {
}
En fait, la sous-classe n'a aucune méthode à remplacer, je ne voudrais pas non plus accéder à la page d'accueil de manière générique, c'est-à-dire: je ne ferais pas quelque chose comme:
for (int i = 0; i < loginPages.length; i++) {
loginPages[i].doSomething();
}
Je veux juste réutiliser la page de connexion. Mais selon https://stackoverflow.com/a/53354 , je devrais préférer la composition ici car je n'ai pas besoin de l'interface LoginPage, je n'utilise donc pas l'héritage ici:
public class HomePage {
public LoginPage loginPage;
}
public class EditInfoPage {
public LoginPage loginPage;
}
mais le problème vient ici, à la nouvelle version, du code:
public LoginPage loginPage;
duplique quand une nouvelle classe est ajoutée. Et si LoginPage doit être défini et activé, davantage de codes doivent être copiés:
public LoginPage loginPage;
private LoginPage getLoginPage() {
return this.loginPage;
}
private void setLoginPage(LoginPage loginPage) {
this.loginPage = loginPage;
}
Ma question est donc la suivante: la "composition fondée sur l'héritage" viole-t-elle le "principe sec"?
la source
extends LoginPage
partout. CHECK MATE!LoginPage
décoratrice. Pas plus de duplication, juste un simplepage = new LoginPage(new EditInfoPage())
et vous avez terminé. Ou bien vous utilisez le principe open-closed pour créer le module d'authentification pouvant être ajouté à n'importe quelle page de manière dynamique. Il existe de nombreuses façons de gérer la duplication de code, notamment en trouvant une nouvelle abstraction.LoginPage
est probablement un mauvais nom, ce que vous voulez vous assurer, c’est que l’utilisateur est authentifié lorsqu’il navigue sur cette page, tout en étant redirigé vers leLoginPage
ou affiché un message d’erreur approprié s’il ne l’est pas.Réponses:
Euh attendez vous êtes préoccupé par le fait de répéter
à deux endroits viole DRY? Par cette logique
ne peut désormais plus exister que dans un seul objet de la base de code. Bleh.
SEC est une bonne chose à garder à l'esprit mais allez. outre
est en train de se dupliquer dans votre alternative alors même être anal à propos de DRY ne donnera pas de sens à cela.
Les problèmes de DRY valides tendent à se focaliser sur le comportement identique requis dans plusieurs endroits et défini dans plusieurs endroits, de sorte que la nécessité de changer ce comportement finit par nécessiter un changement à plusieurs endroits. Prenez des décisions à un endroit et il vous suffira de les changer à un endroit. Cela ne signifie pas qu'un seul objet peut contenir une référence à votre LoginPage.
DRY ne devrait pas être suivi aveuglément. Si vous dupliquez parce que copier et coller est plus facile que de penser à une bonne méthode ou à un bon nom de classe, vous vous trompez probablement.
Mais si vous voulez mettre le même code dans un endroit différent, car cet endroit est soumis à une responsabilité différente et doit probablement être modifié de façon indépendante, il est probablement sage de relâcher l'application de DRY et de laisser ce même comportement adopter une identité différente. . C'est le même genre de pensée qui consiste à interdire les nombres magiques.
DRY ne concerne pas seulement l'apparence du code. Il s'agit de ne pas répandre les détails d'une idée avec une répétition irréfléchie, ce qui oblige les responsables à résoudre le problème à l'aide d'une répétition inconsidérée. C'est lorsque vous essayez de vous dire que la répétition insensée est juste votre convention que les choses vont vraiment mal.
Je pense que vous essayez vraiment de vous plaindre du code standard. Oui, l'utilisation de la composition plutôt que de l'héritage exige un code standard. Rien n'est exposé gratuitement, vous devez écrire du code qui l'expose. Avec ce passe-partout, la souplesse de l’état, la possibilité de rétrécir l’interface exposée, de donner à différents objets des noms qui conviennent à leur niveau d’abstraction, un bon indial, et vous utilisez ce dont vous êtes composé de l’extérieur, pas le à l'intérieur, de sorte que vous faites face à son interface normale.
Mais oui, il y a beaucoup de frappe au clavier supplémentaire. Tant que je peux empêcher le problème yo-yo de faire rebondir une pile d'héritage pendant que je lis le code, ça vaut le coup.
Maintenant, ce n'est pas que je refuse d'utiliser jamais l'héritage. Une de mes utilisations préférées est de donner aux exceptions de nouveaux noms:
la source
int x;
ne peut exister plus de zéro fois dans une base de codeint a; int b; int x;
je vois, je ferai pression pour que "vous" soit supprimé.Un malentendu courant avec le principe DRY est qu'il est en quelque sorte lié à la non répétition de lignes de code. Le principe DRY est le suivant: "Chaque élément de connaissance doit avoir une représentation unique, non ambiguë et faisant autorité dans un système" . C'est une question de connaissance, pas de code.
LoginPage
sait comment dessiner la page pour se connecter. Si onEditInfoPage
savait comment faire cela, ce serait une violation. Y comprisLoginPage
par la composition est pas en aucune manière une violation du principe DRY.Le principe DRY est peut-être le principe le plus mal utilisé en génie logiciel et il devrait toujours être considéré non pas comme un principe permettant de ne pas dupliquer le code, mais comme un principe permettant de ne pas dupliquer les connaissances du domaine abstrait. En fait, dans de nombreux cas, si vous appliquez correctement DRY, vous dupliquez le code et ce n’est pas nécessairement une mauvaise chose.
la source
Réponse courte: oui, cela est vrai - dans une certaine mesure, à un degré acceptable.
À première vue, l'héritage peut parfois vous faire économiser quelques lignes de code, car il a pour effet de dire "ma classe de réutilisation contiendra toutes les méthodes et tous les attributs publics de manière 1: 1". Donc, s'il y a une liste de 10 méthodes dans un composant, il n'est pas nécessaire de les répéter dans le code de la classe héritée. Lorsque dans un scénario de composition, 9 de ces 10 méthodes doivent être exposées publiquement via un composant de réutilisation, il faut ensuite noter 9 les appels de délégation et laisser le reste, il n'y a pas d'autre solution.
Pourquoi est-ce tolérable? Examinez les méthodes qui sont répliquées dans un scénario de composition - ces méthodes consistent exclusivement à déléguer des appels à l'interface du composant, ce qui ne contient aucune logique réelle.
Le principe de base de DRY est d’éviter d’avoir deux endroits dans le code où les mêmes règles logiques sont codées. ce qui introduit une erreur.
Mais comme les appels de délégation ne contiennent pas de logique, ceux-ci ne sont normalement pas soumis à une telle modification, ce qui ne pose donc pas de réel problème lorsque vous "préférez la composition à l'héritage". Et même si l'interface du composant change, ce qui peut induire des changements formels au niveau de toutes les classes utilisant le composant, dans un langage compilé, le compilateur nous dira quand nous aurons oublié de changer l'un des appelants.
Une note à votre exemple: je ne sais pas à quoi ressemblent votre
HomePage
et votreEditInfoPage
, mais s’ils ont la fonctionnalité de connexion et qu’unHomePage
(ou unEditInfoPage
) est unLoginPage
, l’héritage peut alors être le bon outil ici. Un exemple moins discutable, où la composition sera le meilleur outil d’une manière plus évidente, rendrait probablement les choses plus claires.En supposant que ni l'un
HomePage
ni l' autre neEditInfoPage
soientLoginPage
, et que l'on veuille réutiliser ce dernier, comme vous l'avez écrit, il est fort probable que l'on n'exige que certaines parties duLoginPage
, pas tout. Si tel est le cas, une meilleure approche que d’utiliser la composition de la manière indiquée pourrait consister à:extraire la partie réutilisable de
LoginPage
dans un composant de son propreréutiliser ce composant
HomePage
et deEditInfoPage
la même manière, il est utilisé maintenant à l'intérieurLoginPage
De cette façon, il sera généralement beaucoup plus clair de savoir pourquoi et quand la composition par héritage est la bonne approche.
la source