0

I know its considered bad:

$this->laptop->getKeyboard()->getTouchpad()->getLbutton();

or

$this->laptop->getKeyboard()->getCapslock()->isLedOn();

its explained as "we dont want tons of -> -> -> ->" - but I wanted to make this construction physical, I mean it cannot be decoupled. I understand that if the architect changes, it must be done at everywhere, but its not going to happen. Then again, how to workaround this?

John Smith
  • 6,129
  • 12
  • 68
  • 123
  • 1
    I think the LoD refers to accessing member variables of objects, not calling methods. You shouldn't have lots of `a->b->c->d`, but `a->b()->c()->d()` is OK. – Barmar Jan 19 '15 at 11:00
  • 1
    Could you not just have a function that returns the getLbutton? eg. $this-?laptop->getLButton(); - returns NULL if not set?? – developer__c Jan 19 '15 at 11:17
  • 1
    @Barmar `a->b()->c()->d()` is also bad. See my answer. – woru Jan 19 '15 at 11:29
  • 1
    Like mentioned - it's not about `->` (`.`) number, it's about the conversation of your objects. Consider the example in my answer for more. – Jarek Tkaczyk Jan 19 '15 at 12:53

2 Answers2

7

Law of Demeter is about dependencies not -> (or dots in other languages).

For example if you have a fluent interface (where methods return this) this law does not apply.

You want your class to depend on minimal number of other classes.

If your class depends on 10 other classes, a change in any of them can break it. It's harder to understand what your class does. It's also hard to test such a class.

It's doesn't matter if you have $field->getDependency1()->getDependency2() or $field->dependency1->dependency2 - your code stills depends two other classes and knows internal structure of dependency1 (that it has dependency2 inside).

You can solve your problem with tell don't ask principle. In OOD you don't want to use structures that just hold data. You want to have objects that know what to do with their fields.

For example you can refactor:

$oldBalance = $bank->getAccount()->getBalance();
$bank->getAccount()->setBalance($oldBalance - $amount);

To:

$bank->withdraw($amount);

In bank class:

function withdraw($amount) {
    $this->account->withdraw($amount);
}

In Account class:

function withdraw($amount) {
    $this->balance = $this->balance - $amount;
}

You have more methods but now the code that uses $bank, knows nothing about Account and its balance. You can easily tests that class mocking only $bank field. You have also more reusable code.

woru
  • 1,420
  • 9
  • 17
6

Imagine this conversation:

You: Hey Laptop, get me the I/O devices
Laptop: There you go
You: Hey IODevices, get me the keyboard
IODevices: There you go
You: Hey Keyboard, get me the touchpad
Keyboard: There you go
You: Hey Touchpad, I want to press your left button
Touchpad: Consider it done Boss!

Man, you're freaking electronics fanboy, or you do it for living - you know every little piece of your laptop! ;)

vs. this:

You: Hey Laptop, I want to press left button on the touchpad
// Laptop gets the job done
Laptop: Done Boss!

In the 2nd conversation, under the hood,

  1. Laptop calls its dependency IODevices (You don't need to know anything about it),
  2. IODevices calls its dependency Keyboard (Latop doesn't need to know about it),
  3. Keyboard gets its dependency Touchpad (IODevices doesn't need to know about it - well it's a bit bad example in fact, but... you get the idea) and then calls pressLeftButton() on it.

That said, you don't care about the details, all you want to do is pressLeftButton. You don't need to know how Keyboard and Touchpad are related, what is returned from each method etc. You have public interfaces that must be implemented.

And this makes everyone happy, because nothing can break when you replace KeyboardXyz with KeyboardAbc, as long as the latter implements the same interface.

Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157