Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

LoD questions

0 views
Skip to first unread message

Paul Winkler

unread,
Sep 18, 2001, 2:32:20 PM9/18/01
to

I've been reading about the Law of Demeter which says, in a nutshell,
that methods should only talk to their "close friends", a concept
which seems to be vague by intent.

My question boils down to this: How does one find an appropriate
balance between minimizing the number of dots and managing the number
of methods in the parent object?

If I have an object, foo, that contains lots of information, the LoD
says that however I organize it, I don't want to directly dig deep
inside like so:

x = foo.bar.bat.baz.baf()

But it may not be practically possible to put methods for every
possible thing directly in foo, or else I'll have hundreds of methods
that look like

x = foo.get_bar_bat_baz("baf")

So what's a reasonable compromise? Is it generally considered OK under
the LoD to reach directly in two layers? e.g.

x = foo.bar.get_bat_baz("baf")

So we only need to know about foo that it contains an object bar with
a method a get_bat_baz("baf") and don't need to worry how that method
works.

Comments?

--Paul Winkler

Ype Kingma

unread,
Sep 18, 2001, 4:42:46 PM9/18/01
to
Paul,

It depends :).
When you find yourself writing foo.bar.bat a lot of times in the
methods of the class of foo, it
is reasonable to consider foo.bar.bat a "close friend" of foo,
so you introduce:
foo.bat = foo.bar.bat
at an appropriate place, possibly in the __init__() method.
This is more concise, and more efficient.
It also reduces the need for get_bar_bat_baz() methods considerably.

This is related to some OO design issues:
- method normalisation, ie. introduce methods only for independent
functionalities, except when performance requires otherwise
(foo.bar.get_bat_baz() might be foo.bar.getbat().getbaz()), and
- when there are many different objects involved in a series of steps,
which object should call which?. There are two extremes: one object
calls all the other ones, and: each object only calls one other object.

Given these choices, you choose the ones for which you expect the future
maintenance to be confined to as few classes as possible:
When you expect the order of steps to change, choose a central object.
When some of the steps are not related at all to a central object,
move them into a method of an object/class to which they do relate.

If that sounds rather abstract, try thinking in responsibilities:
which object/class should be responsible for which (kinds of) steps?

Have fun,
Ype

--
email at xs4all.nl

Joshua Macy

unread,
Sep 18, 2001, 5:56:39 PM9/18/01
to
Try http://c2.com/cgi/wiki?LawOfDemeter for an interesting discussion of
this. Basically, I agree with the crowd that says there's no automatic
benefit to making spam.eggs.ham.rat() into spam.doEggsHamRat(), but if
you have a lot of that going on in your code there's probably something
wrong in your design. It's a signal that you should be asking yourself
why it is that spam needs to know the internal structure of so many
other classes? Are the class responsibilities really divided
appropriately? Is there a Visitor pattern or something in there trying
to work its way out? Are you not taking sufficient advantage of
polymorphism? Etc.

Joshua

Paul Winkler

unread,
Sep 18, 2001, 11:23:12 PM9/18/01
to
On Tue, 18 Sep 2001 21:42:46 +0100, Ype Kingma <yki...@accessforall.nl> wrote:
>When you find yourself writing foo.bar.bat a lot of times in the
>methods of the class of foo, it
>is reasonable to consider foo.bar.bat a "close friend" of foo,
>so you introduce:
> foo.bat = foo.bar.bat
>at an appropriate place, possibly in the __init__() method.
>This is more concise, and more efficient.
>It also reduces the need for get_bar_bat_baz() methods considerably.

Hmm, yes, I've been overlooking that. Looks like it might be a useful
technique in some parts of my current project. Thanks!

--PW

Paul Winkler

unread,
Sep 19, 2001, 12:16:12 AM9/19/01
to
On Tue, 18 Sep 2001 21:56:39 GMT, Joshua Macy wrote:
>Try http://c2.com/cgi/wiki?LawOfDemeter for an interesting discussion of
>this.

Very interesting indeed, thanks! Clearly there's no easy
one-size-fits-all answer to my question. But many interesting
perspectives to consider. And I just found the discussion of "Tell,
Don't Ask" which was a definite light-bulb-over-the-head moment.

--PW

Roeland Rengelink

unread,
Sep 19, 2001, 4:58:27 AM9/19/01
to

Paul Winkler wrote:
>
> I've been reading about the Law of Demeter which says, in a nutshell,
> that methods should only talk to their "close friends", a concept
> which seems to be vague by intent.
>
> My question boils down to this: How does one find an appropriate
> balance between minimizing the number of dots and managing the number
> of methods in the parent object?
>
> If I have an object, foo, that contains lots of information, the LoD
> says that however I organize it, I don't want to directly dig deep
> inside like so:
>
> x = foo.bar.bat.baz.baf()
>
> But it may not be practically possible to put methods for every
> possible thing directly in foo, or else I'll have hundreds of methods
> that look like
>
> x = foo.get_bar_bat_baz("baf")
>

Note that you've really only replaced dots with underscores here. The
structural knowledge is still there. If you end up with hundreds of
methods like this in foo, you know something is wrong.

I think that LoD is saying that if x needs a baf and if foo is the only
reasonable place to get it, then foo should have a method get_baf()

x = foo.get_baf()

foo can of course implement this as:

def get_baf(self):
return self.bar.get_baf()

and bar can do

def get_baf(self):
return self.bat.get_baf()

etc. etc.

However, if foo ends up with:

def get_bars_baf(self):
return self.bar.get_baf()

def get_oomps_baf(self)
return self.oomp.get_baf()

You may already be in trouble. x just wants a baf. If it has to tell foo
where to get it, foo is not the place to get it from. (or baf may not be
the proper thing to ask from foo, or x is in the wrong place, or baf is
in the wrong place, or oomps baf is not really a baf, or...)

> So what's a reasonable compromise? Is it generally considered OK under
> the LoD to reach directly in two layers? e.g.
>
> x = foo.bar.get_bat_baz("baf")
>

I think you shouldn't think of LoD as a rule that tells you how many
dots (or underscores!) you can use in indirection, but as a hint that
tells you that if you need many dots then there may be something wrong
with your code. In general you're in bigger trouble if you need three
than if you need two.

It is difficult to say what may be wrong without knowing more about your
code.

Just one hint, beware of the FORTRAN common block. If foo's only
function is to pass
bar and oops around, then just pass bar and oops around, and get rid of
foo.

Hope this helps,

Roeland
--
r.b.ri...@chello.nl

"Half of what I say is nonsense. Unfortunately I don't know which half"

Paul Winkler

unread,
Sep 19, 2001, 11:01:25 AM9/19/01
to
On Wed, 19 Sep 2001 08:58:27 GMT, Roeland Rengelink
<r.b.ri...@chello.nl> wrote:

>Paul Winkler wrote:
(snip)

>> But it may not be practically possible to put methods for every
>> possible thing directly in foo, or else I'll have hundreds of methods
>> that look like
>>
>> x = foo.get_bar_bat_baz("baf")
>>
>
>Note that you've really only replaced dots with underscores here. The
>structural knowledge is still there.

I noticed that. :) I was pretty sure, for that reason, that that
*wasn't* the intention of the LoD.

>You may already be in trouble. x just wants a baf. If it has to tell foo
>where to get it, foo is not the place to get it from. (or baf may not be
>the proper thing to ask from foo, or x is in the wrong place, or baf is
>in the wrong place, or oomps baf is not really a baf, or...)

OK. So it's really more a sign of trouble than a prescription for a
cure.

>Just one hint, beware of the FORTRAN common block. If foo's only
>function is to pass
>bar and oops around, then just pass bar and oops around, and get rid of
>foo.

No, in this case foo has a very well defined job, and so does bar. I'm
now going through my code looking for places where I need to know that
foo has a bar, and considering other ways of organizing it.

Thanks for the comments, this is very helpful!

--PW

0 new messages