2

Let's say I'd like to perform the following command:

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

To avoid a NullPointerException, I'd have to do the following if:

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

Is there a way or an already existing Utils class that does this more elegantly, let's say something like the following?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

Accepted Answer:

The best way would be to avoid the chain. If you aren't familiar with the Law of Demeter (LoD), in my opinion you should. You've given a perfect example of a message chain that is overly intimate with classes that it has no business knowing anything about.

Law of Demeter: http://en.wikipedia.org/wiki/Law_of_Demeter

From : https://stackoverflow.com/questions/3458451/check-chains-of-get-calls-for-null/3458520

  1. Why is chaining bad in this case?

Floor has public method to get wall of specified direction through, getWall, similar is the case of Wall, Door, Doorknob.

  1. Does, house having Floor and Floor having Wall counts as intimate knowledge of internal structure,
  2. if yes, then does it also apply to separate pieces of knowledge house has Floor and Floor has Door?
  3. If answer to both is yes, then please provide some examples where some knowledge about an Object doesn't constitute as intimate knowledge about its internal structure.
  4. Is there is some rule of thumb for the same?

  5. Also what would be solution to the original question that I quoted, if the chain is avoided?

8

Short answer: The problem is that the caller needs to traverse (and know the structure of) the entire multi-object data structure in order to access it's leaf node. I think you're confusing things a bit. You're arguing: if A knows about B and that's ok, and B knows about C and that's ok (etc.), then it must be ok for A to know about C. But it's not transitive like that, because the issue isn't that A knows B and B knows C, it's that the caller knows A, B, C, and D and how they all connect to each other. That's a lot that the caller has to know in order to get to D.

Long answer:

  1. Chaining in this case is bad because:
    1. It makes for brittle code. If you change the interface to floor, wall, or door, then the calling code can't access doorknob the way that it used to. So imagine you have a few parts of your code that access doorknobs, and a few that access doors, and a few that access walls, and then for some reason you have to change the floor class. Now you have to change all the code that accesses any of those objects.
    2. How will you test this? If you want to use object doubles, you will have to make getFloor(0) return an object double that responds to .getWall(WEST) and returns an object double that responds to .getDoor() and returns the double of the doorknob object that you want to use in your white box testing. Also point one applies to all your tests also.
    3. Change happens. (this point especially relates to the TLDR below.) What if my house wall doesn't have a door - it's a patio floor? Do all floors have walls, all walls have doors, and all doors have doorknobs? We're assuming a lot about how each of these works.
  2. I'm going to answer this with another question. In a long while when I am a grandfather, if I know where my son's daughter's friend keeps her bike, is that intimate knowledge? I'd have to know my granddaughter's friend pretty well... so yes, I would say it's relatively intimate knowledge. But for my granddaughter, that would be no big deal to know where her friend keeps her bike. Because I am further removed, having detailed information is more intimate than if I was not far removed. Also, see point 1.3 above.
  3. see (2)
  4. The issue here is that the details of the entire multi-class data structure have to be known in order to access anything in the data structure. Of course for one object to use another it needs to know all about it's public interface. It's when you scale that up and need to know that A returns a B which has function C that returns object D which has function E that returns object F that you are really describing an entire data structure every time you want to access one of its members).
  5. Two levels deep is about as far as I would be comfortable going, but I'm keen to hear if anyone has anything more substantial than a gut feel for this question. So I would be ok up to this point: house.getFloor(0).getWall(WEST)
  6. The goal is to make all of your house parts less tightly coupled. You want a way to get a reference to the doorknob, given a reference to a house, without traversing the entire house structure.
    1. One possible solution is to store the house structure in a format that allows querying - as a relational database or in xml. Then you can SELECT * FROM doorknob WHERE house = '45' AND floor = '1' or house//floor//doorknob (searching for any doorknob for a certain floor in the house, not caring that there are walls and doors between the floor and the doorknob). The point there is that you don't care about all the intermediates house parts.
    2. But I should really zoom out and say this: the point of this whole discussion is that if you are().tempted().to().chain().functions() as badly as that, then it's a code smell; an indicator of a more fundamental problem with your design. You need to redesign the data structure so that you can access parts without knowledge of the whole. Maybe you find yourself iterating over doorknobs a lot, and the house object needs a function that returns the list of doorknobs with some meta-data about their rough location.

Conclusion: One general rule of thumb for architecture presented in Design Patterns: Elements of Reusable Object-Oriented Software (Erich Gamma et. al., Chapter 2) is to "encapsulate the concept that varies". In the case you're presenting, with all those different objects, something (I don't know what) is bound to vary (it depends on the problem domain; we don't have the background info to know what could change here, but the owner of the code in that example would know. I've highlighted a few things that I thought could change in point 1.3 above). So if somethings going to change, then something should be encapsulated. The design challenge is to know what could change and encapsulate that... but as it stands, the house structure is totally un-encapsulted. It's intimate details are publicly known.

  • submission.getComments().getChildren().get(0).getComment().getAuthor() This is how I get author of first comment of a reddit post, using JRAW(Java reddit API wrapper). How can I avoid chaining in this case. I cannot avoid it. If I write helper function to access it, I will have to do the null check that I mentioned in my question. – q126y Jun 12 '16 at 18:10
  • 1
    @q126y if that's the only option the API gives you, then there's nothing you can do. It's up to the API authors to make another way for you to get a submissions child-comments' authors that doesn't involve chaining like you described. The answers here are written assuming you can change the API. – alexanderbird Aug 26 '17 at 2:09
6

Beyond AlexanderBird's answer, I would simply say the following - object orientation is about telling objects to do things for you, not just exposing attributes and getting you to do things with them.

So in the above, you're asking object A about it's object B, then asking object B about C, and so on. Just tell object A to do something, and let it delegate and/or determine applicability.

  • 1
    Something like house.getDoorknobs(0, WEST)? – alexanderbird Apr 25 '16 at 14:33
  • 2
    The real question should be "why do you need the doorknob anyway" and then the resulting code will probably become house.enterLivingRoom() (Which will probably require house to target and turn the specific doorknob, but we don't care about that. It can make us go in through the window for all we know, as long as we end up in the right place.) – Erik Apr 25 '16 at 14:46
  • submission.getComments().getChildren().get(0).getComment().getAuthor() This is how I get author of first comment of a reddit post, using JRAW(Java reddit API wrapper). How can I avoid chaining in this case. I cannot avoid it. If I write helper function to access it, I will have to do the null check that I mentioned in my question. – q126y Jun 12 '16 at 18:10

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.