Je lis un livre intitulé Rails AntiPatterns et ils parlent d'utiliser la délégation pour éviter d'enfreindre la loi de Demeter. Voici leur premier exemple:
Ils croient qu'appeler quelque chose comme ça dans le contrôleur est mauvais (et je suis d'accord)
@street = @invoice.customer.address.street
La solution proposée consiste à procéder comme suit:
class Customer
has_one :address
belongs_to :invoice
def street
address.street
end
end
class Invoice
has_one :customer
def customer_street
customer.street
end
end
@street = @invoice.customer_street
Ils affirment que puisque vous n'utilisez qu'un seul point, vous n'enfreignez pas la loi de Déméter ici. Je pense que c'est incorrect, car vous passez toujours par le client pour passer par l'adresse pour obtenir la rue de la facture. J'ai principalement eu cette idée dans un article de blog que j'ai lu:
http://www.dan-manges.com/blog/37
Dans le billet de blog, le premier exemple est
class Wallet
attr_accessor :cash
end
class Customer
has_one :wallet
# attribute delegation
def cash
@wallet.cash
end
end
class Paperboy
def collect_money(customer, due_amount)
if customer.cash < due_ammount
raise InsufficientFundsError
else
customer.cash -= due_amount
@collected_amount += due_amount
end
end
end
Le blog indique que bien qu'il n'y ait qu'un seul point customer.cash
au lieu de customer.wallet.cash
, ce code viole toujours la loi de Demeter.
Maintenant, dans la méthode Paperboy collect_money, nous n'avons pas deux points, nous en avons juste un dans "customer.cash". Cette délégation a-t-elle résolu notre problème? Pas du tout. Si nous examinons le comportement, un paperboy atteint toujours directement le portefeuille d'un client pour retirer de l'argent.
ÉDITER
Je comprends parfaitement et je suis d'accord que c'est toujours une violation et je dois créer une méthode Wallet
appelée appelée qui gère le paiement pour moi et que je devrais appeler cette méthode à l'intérieur de la Customer
classe. Ce que je ne comprends pas, c'est que selon ce processus, mon premier exemple viole toujours la loi de Déméter, car il cherche Invoice
toujours directement Customer
à obtenir la rue.
Quelqu'un peut-il m'aider à dissiper la confusion? Je cherchais depuis 2 jours à essayer de laisser ce sujet pénétrer, mais c'est toujours déroutant.
la source
Réponses:
Votre premier exemple ne viole pas la loi de Demeter. Oui, avec le code tel qu'il est, disant
@invoice.customer_street
qu'il arrive qu'il obtienne la même valeur qu'une hypothétique@invoice.customer.address.street
, mais à chaque étape de la traversée, la valeur renvoyée est décidée par l'objet demandé - ce n'est pas que "le paperboy atteint le portefeuille du client ", c'est que" le paperboy demande au client de l'argent, et le client arrive à obtenir l'argent de son portefeuille ".Quand vous dites
@invoice.customer.address.street
, vous assumez la connaissance des clients et adresse internes - c'est la mauvaise chose. Quand vous dites , vous demandez au "hé, j'aimerais la rue du client, vous décidez comment vous l'obtenez ". Le client dit alors à son adresse: "hé j'aimerais votre rue, vous décidez comment vous l'obtenez ".@invoice.customer_street
invoice
L'idée maîtresse de Demeter n'est pas "vous ne pouvez jamais connaître les valeurs d'objets éloignés dans le graphique de vous"; c'est plutôt "vous- même ne devez pas parcourir loin le long du graphique d'objet afin d'obtenir des valeurs".
Je suis d'accord que cela peut sembler être une distinction subtile, mais considérez ceci: dans le code conforme à Demeter, combien de code doit changer lorsque la représentation interne d'un
address
change? Qu'en est-il du code non conforme à Demeter?la source
Le premier exemple et le second ne sont en fait pas très identiques. Alors que le premier parle de règles générales de "un point", le second parle davantage d'autres choses dans la conception OO, en particulier " Tell, Don't ask "
Encore une fois, " uniquement pour le comportement, pas pour les attributs "
Si vous demandez des attributs, vous êtes censé le demander . "Hé, mec, combien d'argent as-tu en poche? Montre-moi, je vais évaluer si tu peux payer ça." C'est faux, aucun commerçant ne se comportera comme ça. Au lieu de cela, ils diront: "Veuillez payer"
Ce sera le devoir du client d'évaluer s'il doit payer et s'il peut payer. Et la tâche du commis est terminée après avoir dit au client de payer.
Alors, le deuxième exemple prouve-t-il que le premier est faux?
À mon avis. Non , tant que:
1. Vous le faites avec auto-contrainte.
Bien que vous puissiez accéder à tous les attributs du client
@invoice
par délégation, vous en avez rarement besoin dans les cas normaux.Pensez à une page affichant une facture dans une application Rails. Il y aura une section en haut pour montrer les détails du client. Donc, dans le modèle de facture, allez-vous coder comme ça?
C'est faux et inefficace. Une meilleure approche est
Ensuite, laissez le client partiel pour traiter tous les attributs appartient au client.
Donc, en général, vous n'en avez pas besoin. Mais vous pouvez avoir une page de liste affichant toutes les factures récentes, il y a un champ d'information dans chacun
li
affichant le nom du client. Dans ce cas, vous devez afficher l'attribut du client et il est tout à fait légitime de coder le modèle comme2. Aucune action supplémentaire ne dépend de cet appel de méthode.
Dans le cas ci-dessus de la page de liste, la facture a demandé l'attribut de nom du client, mais son but réel est de " montrer mon nom ", donc c'est fondamentalement toujours un comportement mais pas d' attribut . Il n'y a aucune autre évaluation et action basée sur cet attribut comme, si votre nom est "Mike", je vous aimerai et je vous donnerai 30 jours de crédit supplémentaire. Non, facturez simplement "montrez-moi votre nom", pas plus. C'est donc tout à fait acceptable selon la règle "Ne dites pas" de l'exemple 2.
la source
Lisez la suite dans le deuxième article et je pense que l'idée deviendra plus claire. L'idée que le client offre simplement la possibilité de payer et de cacher complètement où le boîtier est conservé. Est-ce un champ, un membre d'un portefeuille ou autre chose? L'appelant ne sait pas, n'a pas besoin de savoir et ne change pas si ce détail d'implémentation change.
Je pense donc que votre deuxième référence donne une recommandation plus utile.
L'idée du «point unique» est un succès partiel, en ce sens qu'elle cache des détails profonds, mais accroît toujours le couplage entre les composants séparés.
la source
On dirait que Dan a dérivé son exemple de cet article: The Paperboy, The Wallet et The Law Of Demeter
Je comprends que votre exemple est en Ruby, mais cela devrait s'appliquer à toutes les langues POO.
la source