Another newbie question

Mike Meyer mwm at mired.org
Sat Dec 10 01:28:52 EST 2005


Steven D'Aprano <steve at REMOVETHIScyber.com.au> writes:
> On Thu, 08 Dec 2005 20:46:33 -0500, Mike Meyer wrote:
>> Steven D'Aprano <steve at REMOVEMEcyber.com.au> writes:
>>> Paul Rubin wrote:
>>>> Steven D'Aprano <steve at REMOVETHIScyber.com.au> writes:
>>>>>> Yes. Reaching through objects to do things is usually a bad idea.
>>>>>I don't necessarily disagree, but I don't understand why you say this. Why
>>>>>it is bad?
>>>> The traditional OOP spirit is to encapsulate the object's entire
>>>> behavior in the class definition.
>>> Uh huh. Say I have:
>>>
>>> class Coordinate:
>>>      def __init__(self, x=0.0, y=0.0):
>>>          self.x = x
>>>          self.y = y
>>>
>>> pt = Coordinate(1.0, 2.5)
>>> Presumably then I also need add_y_ord, sub_y_ord, rsub_y_ord,
>>> div_y_ord, and so on for every method that floats understand, plus
>>> *another* set of methods that do the same thing for the x
>>> ordinate. And in every case, these Coordinate methods are trivial
>>> one-liners.
>>> Do people really do this?
>> Yes, but usually with better API design. For instance, your Coordinate
>> class might have scale, translate, rotate and move methods.
> Which I obviously left as exercises for the reader. Did I really need to
> specify the entire API for an example like this?

Given that we're talking about API design, then yes, you do. With only
partial examples, you'll only get partial conclusions. 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).

>>> Yes, I could encapsulate the lot with a factory function that applied
>>> a specified operator to a specified attribute, and populate the class
>>> at runtime. But why would I want to?
>> You don't. You're thinking about things at the wrong level. You don't
>> want to think about "things you do to a Coordinate's attribute". You
>> want to think about "things you do to a Coordinate".
> I've done my thinking about coordinates. That is why I wrote a Coordinate
> class. But sometimes you don't want to do an operation on a coordinate, you
> want to do something to the x or y ordinate alone. It is utter nonsense to
> suggest that you should abstract coordinates to the point that you no
> longer know -- or pretend that you don't -- that a coordinate is a pair of
> ordinates.

Which demonstrates that *you've* missed the point. It's not utter
nonsense to abstract coordinates - or any other class - to the point
that you don't know what the underlying implementation is. That's what
abstraction is all about. Yup, a cordinate has a pair of ordinates. So
you have attributes to get them.

>>> Now, as I see it, the whole point of encapsulation is that you *don't*
>>> need to fill your class definition with meaningless helper functions.
>> Correct. That's where you went wrong - your methods were essentially
>> meaningless. They just manipulated the attributes, not the Coordinate.
>> Your methods should be meaningful for the object, not just the
>> attributes.
> *Exactly* my point -- and demonstrating that you've missed that point.
> Writing special purpose methods to manipulate object attributes when you
> can just as easily manipulate the object attributes is a bad idea. Methods
> should be meaningful for the object.

And you've once again missed the point. The reason you don't
manipulate the attributes directly is because it violates
encapsulation, and tightens the coupling between your class and the
classes it uses. It means you see the implementation details of the
classes you are using, meaning that if that changes, your class has to
be changed to match.

> The bad side of the Guideline of Demeter is that following it requires
> you to fill your class with trivial, unnecessary getter and setter
> methods, plus methods for arithmetic operations, and so on.

No, it requires you to actually *think* about your API, instead of
just allowing every class to poke around inside your implementation.

> As a guideline, to make you think about what your doing ("am I doing too
> much work? should my class implement a helper function for this common
> task?") it is perfectly fine. But when you find yourself writing trivial
> methods that do nothing but call methods on a attribute, you are another
> victim of a bad law.

More likely, you're just committing a bad design.

> If I *can't* keep the two views in sync, then I have some choices to make.
> For instance, I might have a Cartesian class and a Polar class, with
> methods to c
onvert from one to the other, and give up on the desire for
> one object to encapsulate both.

Those are all valid choices. They have their own tradeoffs. Following
the LoD lowers the coupling between classes, making it easier to
change/extend/etc. them; on the down side, you have to take more care
in your API design, and some operations take a bit more typing. You
may not like that trade off. That's fine; I've already mentioned some
cases where it makes sense to do otherwise. Don't do things that way -
but do know what the tradeoffs are.

       <mike
-- 
Mike Meyer <mwm at mired.org>			http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.



More information about the Python-list mailing list