Another newbie question

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sat Dec 10 23:23:19 EST 2005


On Sat, 10 Dec 2005 13:33:25 -0500, Mike Meyer wrote:

> Steven D'Aprano <steve at REMOVETHIScyber.com.au> writes:
>>> In particular,
>>> you can get most of your meaningless methods out of a properly
>>> designed Coordinate API. For example, add/sub_x/y_ord can all be
>>> handled with move(delta_x = 0, delta_y = 0).
>>
>> Here is my example again:
>>
>> [quote]
>> Then, somewhere in my application, I need twice the 
>> value of the y ordinate. I would simply say:
>>
>> value = 2*pt.y
>> [end quote]
>>
>> I didn't say I wanted a coordinate pair where the y ordinate was double
>> that of the original coordinate pair. I wanted twice the y ordinate, which
>> is a single real number, not a coordinate pair.
>   
> Here you're not manipulating the attribute to change the class -
> you're just using the value of the attribute. That's what they're
> there for.

[bites tongue to avoid saying a rude word]

That's what I've been saying all along!

But according to the "Law" of Demeter, if you take it seriously, I
mustn't/shouldn't do that, because I'm assuming pt.y will always have a
__mul__ method, which is "too much coupling". My Coordinate class
must/should create wrapper functions like this:

class Coordinate:
    def __init__(self, x, y):
        self.x = x; self.y = x
    def mult_y(self, other):
        return self.y * other

so I am free to change the implementation (perhaps I stop using named
attributes, and use a tuple of two items instead).

I asked whether people really do this, and was told by you that they not
only do but that they should ("only with a better API design").

So we understand each other, I recognise that abstraction is a valuable
tool, and can be important. What I object to is taking a reasonable
guideline "try to keep coupling to the minimum amount practical" into an
overblown so-called law "you should always write wrapper functions to hide
layers more than one level deep, no matter how much extra boilerplate code
you end up writing".



>> The wise programmer
>> will recognise which classes have implementations likely to change, and
>> code defensively by using sufficient abstraction and encapsulation to
>> avoid later problems.
> 
> Except only the omniscennt programmer can do that perfectly.

I'm not interested in perfection, because it is unattainable. I'm
interested in balancing the needs of many different conflicting
requirements. The ability to change the implementation of my class after
I've released it is only one factor out of many. Others include initial
development time and cost, bugs, ease of maintenance, ease of
documentation, how complex an API do I expect my users to learn,
convenience of use, and others.

[snip]

>> Do you lie awake at nights worrying that in Python 2.6 sys.stdout will
>> be renamed to sys.standard_output, and that it will no longer have a
>> write() method? According to the "law" of Demeter, you should, and the
>> writers of the sys module should have abstracted the fact that stdout
>> is a file away by providing a sys.write_to_stdout() function. That is
>> precisely the sort of behaviour which I maintain is unnecessary.
> 
> And that's not the kind of behavior I'm talking about here, nor is it
> the kind of behavior that the LoD is designed to help you with (those
> are two different things).

How are they different? Because one is a class and the other is a module?
That's a meaningless distinction: you are still coupled to a particular
behaviour of something two levels away. If the so-called Law of Demeter
makes sense for classes, it makes sense for modules too.

[snip]

>> "In addition to the full set of methods which operate on the coordinate
>> as a whole, you can operate on the individual ordinates via instance.x
>> and instance.y which are floats."
> 
> That's an API which makes changing the object more difficult. It may be
> the best API for the case at hand, but you should be aware of the
> downsides.

Of course. We agree there. But it is a trade-off that can (not must, not
always) be a good trade-off, for many (not all) classes. One of the
advantages is that it takes responsibility for specifying every last thing
about ordinates within a coordinate pair out of my hands. They are floats,
that's all I need to say.

If you think I'm arguing that abstraction is always bad, I'm not. But it
is just as possible to have too much abstraction as it is to have too
little.


[snip]

> Again, this is *your* API, not mine. You're forcing an ugly, obvious API
> instead of assuming the designer has some smidgen of ability.

But isn't that the whole question? Should programmers follow slavishly the
so-called Law of Demeter to the extremes it implies, even at the cost of
writing ugly, unnecessary, silly code, or should they treat it as a
guideline, to be obeyed or not as appropriate?

Doesn't Python encourage the LoD to be treated as a guideline, by allowing
class designers to use public attributes instead of forcing them to write
tons of boilerplate code like some other languages?



> I've
> already pointed out one trivial way to deal with this, and there are
> others.

Mike, the only "trivial way to deal with this" that you have pointed out
was this:

"For example, add/sub_x/y_ord can all be handled with move(delta_x = 0,
delta_y = 0)."

That's a wonderful answer *for the wrong question*. I thought I explained
that already.



-- 
Steven.




More information about the Python-list mailing list