[Tutor] What's the correct way to define/access methods of a member variable in a class pointing to an object?
Steven D'Aprano
steve at pearwood.info
Sat Sep 3 13:17:36 EDT 2016
On Sat, Sep 03, 2016 at 02:51:22PM +0100, Alan Gauld via Tutor wrote:
[...]
> The first thing to do is point out that what you
> are asking about is the Law of Demeter on OOP.
> See Wikipedia for a full description. In essence
> it says that the user of an object should not
> directly access that object's internal attributes/state.
> So ideally you only access foo via a call to a method of
> Bar.
That's a very important answer too, thanks Alan for reminding me of it,
but remember that the Law of Demeter is more of a guideline, and we
should understand it, not just blindly quote it.
One classic example of the Law Of Demeter is:
"If you want your dog to come to you, don't talk to your dog's legs,
talk to the dog."
class Dog:
...
fido = Dog()
# I want the dog to come here.
fido.left_front_leg.lift()
fido.left_front_leg.forward()
fido.left_front_leg.down()
fido.right_back_leg.lift()
...
Arggh, no, that's terrible! That violates the Law Of Demeter, and you
shouldn't expect your users to do that. The caller shouldn't have to
care about *how the dog walks*.
fido.command("come here boy!")
Or perhaps:
fido.heel()
whatever is most appropriate. The details of how the dog walks is
encapsulated inside the Dog class.
Another classic example is of a transaction where the paper boy gives
you a newspaper and you pay him a dollar:
newspaper = paperboy.get_paper()
paperboy.balance += 1.0
customer.trousers.wallet.balance -= 1.0
No! Would you really let the paperboy reach into your trousers, grab
your wallet, open it up, and take money out? Again, another Demeter
violation. Better to re-write your Customer class:
class Customer:
def pay(self, amount):
if amount > self.wallet.balance:
raise ValueError
self.wallet.balance -= amount
return amount
paperboy.receive(customer.pay(1.0))
But sometimes the Law Of Demeter should not apply. Sometimes you are
expected to tinker with the internals of an object.
car = Car()
# replace the engine with a bigger one
car.engine = Engine("800 horsepower of throbbing nitro-fueled POWER")
car.engine.inject(nitro=True)
but as I suggested in an earlier email, that mostly applies when the
attribute is a fairly simple object like a list, a dict, or similar.
--
Steve
More information about the Tutor
mailing list