J'ai trois questions concernant la loi de Demeter.
En dehors des classes qui ont été spécifiquement désignées pour renvoyer des objets - telles que les classes d'usine et de constructeur - est-il acceptable qu'une méthode renvoie un objet, par exemple un objet détenu par l'une des propriétés de la classe ou cela violerait-il la loi de Demeter (1) ? Et si elle viole la loi de déméter, cela importerait-il si l'objet retourné est un objet immuable qui représente une donnée et ne contient que des getters pour ces données (2a)? Ou un tel ValueObject est-il un anti-modèle en soi, parce que tout ce qui est fait avec les données de cette classe se fait en dehors de la classe (2b)?
En pseudo code:
class A {}
class B {
private A a;
public A getA() {
return this.a;
}
}
class C {
private B b;
private X x;
public void main() {
// Is it okay for B.getA to exist?
A a = this.b.getA();
a.doSomething();
x.doSomethingElse(a);
}
}
Je soupçonne que la loi de Demeter interdit un modèle tel que ci-dessus. Que puis-je faire pour m'assurer qu'on doSomethingElse()
peut appeler sans violer la loi (3)?
la source
x
se prépare-t-on?x
est juste une propriété différente dont la valeur est juste un objet capable d'invoquerdoSomethingElse
.user.currentSession.isLoggedIn()
. car il couple les clients deuser
non seulement ,user
mais aussi son collaborateur,session
. Au lieu de cela, vous voulez pouvoir écrireuser.isLoggedIn()
. ceci est généralement réalisé en ajoutant uneisLoggedIn
méthode àuser
laquelle l'implémentation délèguecurrentSession
.Réponses:
Dans de nombreux langages de programmation, toutes les valeurs renvoyées sont des objets. Comme d'autres l'ont dit, ne pas pouvoir utiliser les méthodes des objets retournés vous oblige à ne jamais rien retourner du tout. Vous devriez vous demander: "Quelles sont les responsabilités des classes A, B et C?" C'est pourquoi l'utilisation de noms de variables métasyntaxiques comme A, B et C sollicite toujours la réponse "cela dépend" car il n'y a pas de responsabilités héritées dans ces termes.
Vous voudrez peut-être examiner la conception pilotée par domaine, qui vous donnera un ensemble plus nuancé d'heuristiques pour raisonner où la fonctionnalité devrait aller et qui devrait invoquer quoi.
Votre deuxième question concernant les objets immuables concerne la notion d'
objet de valeur par rapport à un objet d'entité. en DDD, vous pouvez transmettre des objets de valeur sans presque aucune restriction. Cela ne vaut pas pour les objets d'entité.
Le LOD simpliste est beaucoup mieux exprimé en DDD que les règles d'accès aux agrégats . Aucun objet externe ne doit contenir de références à des membres d'agrégats. Seule une référence racine doit être conservée.
Mis à part DDD, vous voulez au moins développer vos propres ensembles de stéréotypes pour vos classes qui sont raisonnables pour votre domaine. Appliquez des règles sur ces stéréotypes lors de la conception de votre système.
Souvenez-vous également que toutes ces règles visent à gérer la complexité et non à vous mettre en difficulté.
la source
Law of Demeter
,tell, don't ask
,getters are evil
,object encapsulation
etsingle responsibility principle
semblent se résumer à cette question: quand doit - délégation à un objet de domaine utilisé par opposition à la valeur de l'objet et de faire quelque chose avec elle? Il serait très utile de pouvoir appliquer des qualifications nuancées aux situations où une telle décision doit être prise.Oui, certainement.
Regardons les points principaux :
Tous les trois vous laissent poser une question: qui est un ami?
Au moment de décider quoi retourner, la loi de Déméter ou le principe de la moindre connaissance (LoD) ne dicte pas que vous vous défendez contre les codeurs qui insistent pour le violer. Cela veut dire que vous ne forcez pas les codeurs à le violer.
Obtenir ces confus est exactement pourquoi tant de gens pensent qu'un setter doit toujours retourner nul. Non. Vous devez autoriser un moyen d'effectuer des requêtes (getters) qui ne modifient pas l'état du système. C'est la séparation de requête de commande de base .
Cela signifie-t-il que vous êtes libre de vous plonger dans une base de code enchaînant ce que vous voulez? Non. N'enchaînez que ce qui devait être enchaîné. Sinon, la chaîne pourrait changer et soudainement votre matériel est cassé. C'est ce que l'on entend par amis.
De longues chaînes peuvent être conçues pour. Les interfaces fluides, iDSL, une grande partie de Java8 et le bon vieux StringBuilder sont tous destinés à vous permettre de construire de longues chaînes. Ils ne violent pas LoD parce que tout dans la chaîne est censé fonctionner ensemble et a promis de continuer à travailler ensemble. Vous violez Demeter lorsque vous enchaînez des choses qui n'ont jamais entendu parler les unes des autres. Les amis sont ceux qui ont promis de faire fonctionner votre chaîne. Les amis des amis ne l'ont pas fait.
Cela ne crée qu'une occasion de violer Demeter. Ce n'est pas une violation. Ce n'est même pas nécessairement mauvais.
Immuable est bon mais hors de propos ici. Obtenir des trucs à travers une chaîne plus longue ne fait pas mieux. Ce qui le rend meilleur, c'est de séparer l'obtention de l'utilisation. Si vous utilisez, demandez ce dont vous avez besoin comme paramètre. N'allez pas à sa recherche en plongeant dans les getters d'étrangers.
Avant de parler de
x.doSomethingElse(a)
comprendre que vous avez écrit simplementb.getA().doSomething()
Maintenant, LoD n'est pas un exercice de comptage de points . Mais lorsque vous créez une chaîne, vous dites que vous savez comment obtenir un
A
(en utilisantB
) et que vous savez comment utiliser unA
. Eh bien maintenantA
etB
il vaut mieux être des amis proches parce que vous venez de les coupler.Si vous venait de demander quelque chose à la main vous une
A
comme chose que vous pouvez utiliserA
et n'aurait pas pris soin d' où il vient etB
pourrait vivre une vie heureuse et libre de votre obsession à obtenirA
deB
.En ce qui concerne
x.doSomethingElse(a)
sans détailsx
sur l'origine, LoD n'a rien à dire à ce sujet.LoD peut encourager la séparation de l'utilisation de la construction . Mais je soulignerai que si vous traitez religieusement chaque objet comme non convivial, vous serez coincé à écrire du code dans des méthodes statiques. Vous pouvez construire un graphique d'objet merveilleusement complexe de cette façon, mais vous devez finalement appeler une méthode pour démarrer la chose. Vous devez simplement décider qui sont vos amis. Il n'y a pas moyen de s'en sortir.
Alors oui, une classe est autorisée à renvoyer un de ses membres sous LoD. Lorsque vous le faites, vous devez indiquer clairement si ce membre est ami avec votre classe, car si ce n'est pas le cas, certains clients peuvent essayer de vous coupler à cette classe en vous utilisant pour l'obtenir avant de l'utiliser. C'est important car maintenant ce que vous retournez doit toujours supporter cette utilisation.
Il existe de nombreux cas où ce n'est tout simplement pas un problème. Les collections peuvent ignorer cela simplement parce qu'elles sont censées être amies avec tout ce qui les utilise. De même, les objets de valeur sont conviviaux avec tout le monde. Mais si vous avez écrit un utilitaire de validation d'adresse qui exigeait un objet employé dont il a extrait une adresse, plutôt que de simplement demander l'adresse, vous feriez mieux d'espérer que l'employé et l'adresse proviennent tous deux de la même bibliothèque.
la source
settings.darkTheme().monospaceFont()
est juste du sucre syntaxique poursettings.darkTheme(); settings.monospaceFont();
Je ne comprends pas très bien cet argument. Tout objet peut renvoyer un objet à la suite d'un appel de message. Surtout si vous pensez à "tout comme objet".
Cependant, les classes sont les seuls objets qui peuvent instancier de nouveaux objets de leur propre type en leur envoyant le "nouveau" message (ou souvent remplacé par un nouveau mot-clé dans les langages POO les plus courants)
Quoi qu'il en soit, concernant votre question, je serais plutôt méfiant à propos d'un objet retournant l'une de ses propriétés afin d'être utilisé ailleurs. Il se pourrait que cet objet divulgue des informations sur son état ou ses détails d'implémentation.
Et vous ne voudriez pas que l'objet C connaisse les détails d'implémentation de B. Il s'agit d'une dépendance indésirable de l'objet A du point de vue de l'objet C.
Donc, vous voudrez peut-être vous demander si C, en connaissant A, n'en sait pas trop pour le travail qu'il a à faire, indépendamment de la LOD.
Il y a des cas où cela peut être légitime, par exemple, demander à un objet de collection pour l'un de ses éléments est approprié et ne violer aucune règle Demeter. En revanche, demander à un objet Book son objet SQLConnection est risqué et doit être évité.
Le LOD existe parce que de nombreux programmeurs ont tendance à demander des informations aux objets avant d'effectuer un travail à partir de celui-ci. LOD est là pour nous rappeler que parfois (sinon toujours) nous ne faisons que trop compliquer les choses en voulant que nos objets en fassent trop. Parfois, nous devons simplement demander à un autre objet de faire le travail pour nous. LOD est là pour nous faire réfléchir à deux fois sur l'endroit où nous plaçons le comportement dans nos objets.
la source
Pourquoi ne serait-ce pas? Vous semblez suggérer qu'il est nécessaire de signaler à vos collègues programmeurs que la classe est spécifiquement destinée à renvoyer des objets, ou qu'elle ne doit pas renvoyer d'objets du tout. Dans les langues où tout est un objet, cela limiterait considérablement vos options, car vous ne seriez pas en mesure de retourner quoi que ce soit.
la source
b.getA().doSomething()
etx.doSomethingElse(b.getA())
A a = b.getA(); a.DoSomething();
- retour à un point.b.getA().doSomething()
et quex.doSomethingElse(b.getA())
vous auriez dû le demander explicitement. En l'état, votre question semble concernerthis.b.getA()
.A
n'est pas membre deC
, ni crééC
ni fourniC
, il semble que l'utilisationA
deC
(de quelque manière que ce soit) viole LoD. Compter des points n'est pas la raison d'être de LoD, c'est seulement un outil illustratif. Il est possible de convertirDriver().getCar().getSteeringWheel().getFrontWheels().toRight()
des instructions distinctes qui contiennent chacune un point, mais la violation de LoD est toujours là. J'ai lu dans de nombreux messages sur ce forum que l'exécution d'actions devrait être déléguée à l'objet qui implémente le comportement réel, c'est-à-diretell don't ask
. Le retour des valeurs semble entrer en conflit avec cela.Cela dépend de ce que vous faites. Si vous exposez un membre de votre classe, quand ce n'est l'affaire de personne que c'est un membre de votre classe, ou quel est le type de ce membre, c'est une mauvaise chose. Si vous changez ensuite le type de ce membre, et le type de la fonction renvoyant le membre, et que tout le monde doit changer son code, c'est mal.
D'un autre côté, si l'interface de votre classe indique qu'elle fournira une instance d'une classe A bien connue, ça va. Si vous avez de la chance et que vous pouvez le faire en fournissant un membre de votre classe, bon pour vous. Si vous avez changé ce membre d'un A en un B, vous devrez alors réécrire la méthode qui renvoie un A, il devra évidemment en construire un et le remplir avec les données disponibles, au lieu d'un simple retour un membre. L'interface de votre classe serait inchangée.
la source