http://www.dan-manges.com/blog/37
The Law of Demeter is not easy to understand when reading it for the first time. Quoting the definition from Wikipedia:
More formally, the Law of Demeter for functions requires that a method M of an object O may only invoke the methods of the following kinds of objects: 1. O itself 2. M's parameters 3. any objects created/instantiated within M 4. O's direct component objects In particular, an object should avoid invoking methods of a member object returned by another method.
The Wikipedia article summarizes it as "Only talk to your immediate friends." Developers seems to recognize violations by looking for more than one "dot", such as:
foo.bar.baz
Here, "foo" is talking to "baz" through "bar". The solution (in Ruby) is to use Forwardable and set up a delegation. Here is an example:
class Foo extend Forwardable def_delegator :bar, :baz def bar # an associated object # could be a belongs_to in Rails end end
With this delegation in place, we can reduce the two "dots" into one by doing: foo.baz. Forwardable and def_delegator take care of going from "foo" through "bar" to "baz." While this may seem to solve our problem, the solution has a significant misconception.
Delegation is an effective technique to avoid Law of Demeter violations, but only for behavior, not for attributes.
To explain, I need a better example than foo/bar/baz. One of the classic examples I have seen to explain this is a Paper Boy, a Customer, and a Wallet. A paper boy needs to collect money from his customers. A customer stores his/her money in a wallet. Here is how this might be modeled violating the Law of Demeter:
class Wallet attr_accessor :cash end class Customer has_one :wallet end class Paperboy def collect_money(customer, due_amount) if customer.wallet.cash < due_ammount raise InsufficientFundsError else customer.wallet.cash -= due_amount @collected_amount += due_amount end end end
Hopefully apparent from this example is that a paperboy should not be taking cash out of a customer's wallet. This is a clear Law of Demeter violation. Going back to the simple way to recognize this mistake, we can see two dots in "customer.wallet.cash". Here is how this might change with attribute delegation.
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
This example is only slightly different. A customer now has cash, which simply delegates to cash in the wallet. Now in the Paperboy collect_money method, we don't have two dots, we just have one in "customer.cash". Has this delegation solved our problem? Not at all. If we look at the behavior, a paperboy is still reaching directly into a customer's wallet to get cash out. That's not good. However, if instead of delegating attributes, we delegate behavior, we will end up with a much better OO design.
class Wallet attr_accessor :cash def withdraw(amount) raise InsufficientFundsError if amount > cash cash -= amount amount end end class Customer has_one :wallet # behavior delegation def pay(amount) @wallet.withdraw(amount) end end class Paperboy def collect_money(customer, due_amount) @collected_amount += customer.pay(due_amount) end end
Again, the change is simple, but our OO-ness and class responsibilities are much better. Notice the way delegation is done now. The pay method on customer simply delegates to the withdraw method on the wallet. Even with the argument on the pay method, this could also be implemented by using Forwardable.
Is the Customer#pay method, with doing nothing but a simple delegation, valuable? Yes. We want our paper boy to know as little as possible about the customer. It's okay for the paper boy to know the customer has a method available to pay him. The paper boy knowing the customer has a wallet (and even further, that the wallet has cash), is not okay. This is what the Law of Demeter is about.
Thinking again about attribute/getter/setter delegation, it gives classes too much knowledge about other classes. This includes classes that are far away from each other in the domain model. Going back to the first example, we don't want a paper boy to know a customer has a wallet. Onto the second example, we really don't want a customer to know a wallet has cash if we don't need to. The behavior delegation is a much better way to solve this problem.
Because I'm not confident this example is perfectly clear (after all, why wouldn't a customer know he has cash, why should that be hidden (read: encapsulated) in the wallet?), here is another example, and what I think has caused some overuse of attribute delegations:
An Order belongs_to a Customer. Let's say we're developing a Rails view to display order information, and this should include details on the customer. We might write the view like this:
<%= @order.customer.name %>
Two dots! Demeter violation? No. If you thought it was, you might have a delegation in your model, something like:
class Order extend Forwardable def_delegator :customer, :name, :customer_name end
Our view now becomes:
<%= @order.customer_name %>
We've traded a dot for an underscore. And thinking about this further, why should an order have a customer_name? We're working with objects, an order should have a customer who has a name. Adding these attribute delegations also decreases maintainability.
The crux of this is that webpage views aren't domain objects and can't adhere to the Law of Demeter. Clearly from the examples of behavior delegation the Law of Demeter leads to cleaner code. However, when rendering a view, it's natural and expected that the view needs to branch out into the domain model. Also, anytime something in a view dictates code in models, take caution. Models should define business logic and be able to stand alone from views. If this "train-wreck" method calling in your views is bothersome, there is a better solution that I will blog about later.
Focus on delegating behavior more than attributes. This ties well into the "Tell, don't ask." principle. With the paper boy example, our code is much better when a customer tells his/her wallet to withdraw an amount, rather than asking the wallet for how much cash it has and then doing the logic in the customer model.
相关推荐
There is rampant misuse and misunderstanding of Scrum, which diminishes and possibly eliminates the amazing possibilities that people discover by using it correctly. I've observed too many examples ...
The field of geographic information systems GISs is emerging as one of today’s most exciting and progressive technical areas GIS technology has evolved during the past four decades and is only now ...
Errors may be due to a misunderstanding of the format. With more than 25 years experience in CAM software we at Ucamco know which areas are most often misunderstood. This revision of the RS-274X ...
It is estimated that ( 75 percentages of parents often complain their children’s unreasonable behavior while children usually think their parents too old fashioned ). Why have there been so much ...
【精选】避免被误解(Avoid,Misunderstanding).doc
英语900There’s a basic information error in this talk. He uses the word crucified in relation to the Gorillas that were killed...which is a misunderstanding of the photograph.
In his eagerness for a sociological case, he reveals his misunderstanding of behavioral psychology as a technology only for changing the individual’s response to the situation and not seeking to ...
8. She had overcome both her misunderstanding of and her subtle feelings towards Gouvernail. 《读写教程 IV》: Ex. III, p. 8 1. Idle 2. melting 3. imposes 4. penetrate 5. presence 6. nuisance ...
reduce/eliminate activities that contribute little to the final assurance of a product; clarify CEM terminology to reduce misunderstanding; restructure and refocus the evaluation activities to those ...
In his eagerness for a sociological case, he reveals his misunderstanding of behavioral psychology as a technology only for changing the individual’s response to the situation and not seeking to ...
Kaufrnan’s critique of our article was based on a misunderstanding of our purpose and procedures. This reply clarifies those points and suggests further areas of concern regarding the McCarthy ...
语言:English (United States) 这是印地文中带有令人惊叹的图像的最佳误解行情的集合。 在这里的朋友,我们正在共享印地语链接中最新误解性报价的链接。 在此网站中,我们收集了最好的印地语误解语录和生活在印地语...
at the start of 在……开始的时候 at the end of 在……结束的时候 go to college 上大学 be divided into 被(划)分成…… take part in 参加 amusing /ə'mju:ziŋ/ adj. 有趣的;可笑的 ...
Kaufrnan’s critique of our article was based on a misunderstanding of our purpose and procedures. This reply clarifies those points and suggests further areas of concern regarding the McCarthy ...
误解Android教科书中的应用
2.2.5 Expanding the Bit Representation of a Number . . . . . . . . . . . . . . . . . . . . 49 2.2.6 TruncatingNumbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.2.7 ...
i feel like I am still looking at a blank canvas blank pieces of paper;sadness 问题定义 :thinking_face: 给定用户的sentence ,我们必须预测与其相关的emotion 。 您的顾客评论的感想是什么? 今天的特色...
1. INFOR SCE 11.0.X - 2017V; 2. Chn vs Eng in manual; 3. Ref to Word Specification if any misunderstanding met; 4. Just for reference only.
1. INFOR SCE 11.0.X - 2017V; 2. Chn vs Eng in manual; 3. Ref to Word Specification if any misunderstanding met; 4. Just for reference only.
1. INFOR SCE 11.0.X - 2017V; 2. Chn vs Eng in manual; 3. Ref to Word Specification if any misunderstanding met; 4. Just for reference only.