-2

Consider we have something like this:

A person, which has a physical body with arms, each arm has a hand, each hand fingers, and each finger a fingernail.

Another example: we have a car and a car-wheel in it.

So now I want to tune the fingernail a little bit, or the car-wheel, in both cases I want to change the color.

I go to a Person-Tuning-Garage, in which case I have to dive through the object-graph to get to the object ("fingernail") i'm interested in:

person.getBody().getArm("left").getHand().getFinger(4).getFingernail()

I don't want to let the person itself decide which fingernail I am going to tune.

In the car scene, I would allow the user to rotate the car-wheel, so the user has to ask the car object for the car-wheel to rotate it; here we have another problem.

If the user wants to rotate the wheel, before letting the user rotate it, the wheel must ask the car if there are no stones at the care-tire and only then the car wheel is allowed to rotate. Bumps in the street also forbid rotating the wheel from the car-side. So we have a cyclic dependency.

My questions are:

  • Is it good design to ask deep into the object-graph to get to the destiny object to work on?

  • Is the cyclic dependency in the car-wheel scene acceptable?

Doc Brown
  • 218,378

1 Answers1

1

In short

This design hides a tight coupling between the classes in the hierarchy, which will make it difficult to evolve.

It's difficult to analyse if benefits could outweight this major inconvenience in your special case. But in general, I'd advise another more flexible approach.

Some more details

If the context (e.g. a client class) uses a path such as :

person.getBody().getArm("left").getHand().getFinger(4).getFingernail()

it is required to know not only about Person (probably an immediate neighbour or a parameter to method) and Fingernail (which could be defended if it needs to operate on fingernails), but it also needs to know everything in-between.

As a consequence, if tomorrow you decide to change anything to the way a Person is composed, you'd be in trouble. For example:

  • The tiniest change in the Body API, for example to use an enum leftMember, rightMember instead of a string, would require to change a lot of classes, even if they are not at all interested in the exact structure.
  • Making the body model more accurate, adding a forearm and a couple of knuckles in between the body and the nail, would again need to change a lot of the existing code, even if these changes are irrelevant details.
  • If you want to make your nail processing more generic, e.g. for managing the equivalent of "fingernails" for cats, octopuses and bots, you'll be stuck, because all of the sudden, you'd need to cope with a wide array of different possible body structures.

In fact, all this relates to the principle of least knowledge.

There are a couple of possible solutions:

  • If the fingernail was just an example to explain that you're interested in terminal nodes, you may consider modelling the body using the composite pattern. It allows to process the body parts uniformely, without haveing to know the exact details.
  • You could also think of using the visitor pattern to iterate over your structure. The visitor would still be very coupled to the design, but at least you'd isolate the mechanism to traverse the body.
  • Ideally, you should tell the body what it should do instead of asking for its part and doing yourself. Normally I'd start with there, but you seemed to eliminate this possibility. Maybe something to consider as well.
  • Last but not the least, deep hierarchies of components tend to make software very inflexible, since new combinations require new code. A lighter alternative is then to flatten the structure, using a dynamic "entity-component-system" pattern.
Christophe
  • 81,699