I have a question regarding the Law of Demeter in relation to Lists contained within other objects in Java. I have the following class.
public class Conversation
{
Person person;
List<Message> conversationList;
public List<Message> getConversationList()
{
return conversationList;
}
}
To add a new Message object to conversationList in this class I would typically do something like the following.
Conversationc = new Conversation();
c.getConversationList().add(new Message());
After a bit of reading this would appear to violate the Law of Demeter and adding a method to Converstaion like the following, would be "better" way to approach this.
public List<Message> addMessageToList(Message msg)
{
conversationList.add(msg);
}
However this seems like complete overkill to me. What is the best practice approach in this this situation?
However this seems like complete overkill to me.
Ahh I remember asking myself this question repeatedly when I was first starting out - when the word encapsulation was something I had learned for a test but didn't really understand the "necessity" of.
I say that because encapsulation is what we're talking about here - a one-word answer to your question:
Accessing conversationList
through a getter - which is what the second pattern you've posted here is called - encapsulates your list of conversations in your conversation class. It's hard to see why this is important when the programs you're writing have three or four classes tops and have requirements that are both simple and fixed: When you're still wrapping your head around how these small programs work, this whole notion of "adding a getter" rather than accessing fields directly seems like just one more thing to do, one more you thing you don't really understand why you have to do, and one more place you can muck something up.
That feeling, of course, will go away as you get more confident, and something else that will happen is you'll start thinking about and working on applications that are much larger and more complex, and whose requirements will change (both in reality and in the perception of them) over time, and this will often mean your programs need to change - and that's when encapsulation will start to make an awful lot of sense:
For now: Pretend that your teacher changes this assignment he's given you tomorrow, adding an unforeseen requirement to this program you've been working on (which, incidentally, I think would be a smart thing for teachers to do). Imagine that the only way you can meet that requirement is to keep your list of conversations in a Map
instead of a List
, so you can map each of them to some other value.
If you've been using "your first approach" - calling conversationsList.add()
to access that field directly in many different classes, you'll then have to go through and change every one of those classes to call conversations.put()
instead of .add()
.
But if you've been using the second approach - accessing your collection through a getter - then you can change that collection however significantly you need to and you'll only have to make one change - to that getter:
public void addMessageToList(Message msg)
{
this.myMessagesManager.put(msg, new ArrayList<SomeValue>);
//or .put(msg, null) or whatever
}
Your other classes will never know anything changed, which is great because it means you write less code, and particularly great because it keeps you from having to do a lot of the worst kind of coding - with all the pulling-of-hair and gnashing-of-teeth that's often involved in catching bugs that arise from little mistakes you make when you're making one of these little changes to one of those classes.
(also, see my answer to this question for a better illustration)