According to Demeter's law, is a class allowed to return one of its members?
Yes it most certainly is.
Let's look at the main points:
- Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
- Each unit should only talk to its friends; don't talk to strangers.
- Only talk to your immediate friends.
All three of which leave you asking one question: Who is a friend?
When deciding what to return, The Law of Demeter or principle of least knowledge (LoD) does not dictate that you defend against coders that insist on violating it. It dictates that you do not force coders to violate it.
Getting these confused is exactly why so many think a setter must always return void. No. You must allow a way to make queries (getters) that do not change the state of the system. That's basic command query separation.
Does this mean you're free to delve into a code base chaining together whatever you want? No. Chain together only what was meant to be chained together. Otherwise the chain might change and suddenly your stuff is broken. This is what is meant by friends.
Long chains can be designed for. Fluent interfaces, iDSL, much of Java8, and good old StringBuilder are all meant to let you build long chains. They don't violate LoD because everything in the chain is meant to work together and promised to keep working together. You violate demeter when you chain together stuff that never heard of each other. Friends are those that promised to keep your chain working. Friends of Friends did not.
Apart from classes which were specifically appointed to return objects - such as factory and builder classes - is it okay for a method to return an object, e.g. an object held by one of the class's properties or would that violate the law of demeter (1)?
This only creates an opportunity to violate demeter. This is not a violation. This is not even necessarily bad.
And if it violates the law of demeter, would it matter if the object returned is an immutable object that represents a piece of data and contains nothing but getters for this data (2)?
Immutable is good but irrelevant here. Getting at stuff through a longer chain doesn't make it better. What makes it better is separating the getting from the using. If you're using, ask for what you need as a parameter. Don't go hunting for it by delving into the getters of strangers.
In pseudo code:
I suspect that Demeter's law forbids a pattern such as the above. What can I do to ensure that doSomethingElse() can be called while not violating the law (3)?
Before I talk about x.doSomethingElse(a)
understand that you've basicly written
b.getA().doSomething()
Now, LoD is not a dot counting exercise. But when you create a chain you're saying that you know how to get an A
(by using B
) and you know how to use an A
. Well now A
and B
had better be close friends because you just coupled them together.
If you had just asked for something to hand you an A
like thing you could use A
and wouldn't have cared where it came from and B
could live a life happy and free of your obsession with getting A
from B
.
As for x.doSomethingElse(a)
without details of where x
came from, LoD has nothing at all to say about it.
LoD can encourage separating use from construction. But I will point out if you religiously treat every object as not friendly you'll be stuck writing code in static methods. You can build a wonderfully complex object graph in main this way but eventually you have to call a method on it to start the thing working. You simply have to decide who your friends are. There's no getting away from that.
So yes, a class is allowed to return one of its members under LoD. When you do you should make clear if that member is friendly with your class because if it's not, some client may try coupling you to that class by using you to get it before using it. That's important because now what you return must always support that use.
There are many cases where this is simply not a concern. Collections can ignore this simply because they are meant to be friends with everything that uses them. Similarly value objects are friendly with everyone. But if you wrote an address validating utility that demanded an employee object that it extracted an address from, rather then just ask for the address, well you'd better hope that the employee and address both came from the same library.
x
getting set?x
is just a different property whose value is just some object that is able to invokedoSomethingElse
.user.currentSession.isLoggedIn()
. because it couples clients ofuser
to not onlyuser
but also its collaborator,session
. Instead you want to be able to writeuser.isLoggedIn()
. this is usually achieved by adding anisLoggedIn
method touser
whose implementation delegates tocurrentSession
.