In terms of good OOP design, what is the best way to structure code that is just containers of list of objects that contains other lists of objects that also are just other containers?
Example: A Cookbook owns a list of Chapters that owns a list of Recipes that owns a list of Directions. Each object is a different layer within a whole. For clarity, I use the word "list" to refer to any data structure used to store multiple objects.
I've come across this Russian doll problem of nested objects inside of nested objects many times. I've tried passing the bottom level objects through the nest hierarchy and also tried writing methods that pass information down to the bottom nested object. My structure always feels messy because the intermediate classes are filled with methods that do nothing more than passing either objects or information.
An addition due to confusion expressed in the comments: I'm looking for direction in things to study for handling a general type of problem. I'm not asking for a solution to a specific coding problem that I have in front of me. I wasn't expecting a "one answer to rule them all", but more of "use A design pattern for X situations but use B design pattern for Y situations and beware of Z." The example is for providing a general context, not a specific one.
2 Answers 2
One way to make it easier is that all classes implement the same interface so you can treat them indistinctly. Theres a known pattern for this:
The good news is that as this creates a tree. You can then write, in an external managing class, recursive methods to look up items.
For example you could write a method findAndAdd(String tagToFind, Component componentToAdd);
To the base interfaces documented in the pattern you should add a tagMatches(String tag)
as well as all common getters and setters so you can, again, treat items indistincltly of their class.
I would add hasChild()
and nextChild()
methods to implement iterations.
-
Is
i
just an index to the(i-1)th
item (for zero-based arrays)?Robert Harvey– Robert Harvey09/06/2016 19:10:58Commented Sep 6, 2016 at 19:10 -
7-1, abusing the composite pattern for a fixed hierarchy of different levels of non recursive structured objects is nothing but an example of overengineering.Doc Brown– Doc Brown09/06/2016 20:03:36Commented Sep 6, 2016 at 20:03
-
1@WP0987: see my answer below. You are IMHO approaching this from the wrong side, your data structure is probably ok, instead you may consider to implement the code which accesses it differently.Doc Brown– Doc Brown09/06/2016 20:31:22Commented Sep 6, 2016 at 20:31
-
3And if all the objects in the hierarchy have different capabilities (ie different methods) ? It would mean you either have to declare all of them in the interface, leading to unimplemented methods in some classes (code smell) or declare them in their respective classes but since you unified the whole hierarchy under one interface they are not directly visible. So you'll have to crazy-cast everywhere to reach these methods (again, bigger code smell). That's how a design pattern becomes an anti-pattern...Spotted– Spotted09/08/2016 06:17:37Commented Sep 8, 2016 at 6:17
-
1@TulainsCórdova Yep, that's why I said "if" because it's not sure at the moment that my comment above is relevant (at least as long as the OP doesn't provide more details).Spotted– Spotted09/08/2016 06:58:07Commented Sep 8, 2016 at 6:58
Do yourself a favor, and keep it simple and stupid!
In your example, you wanted to model Cookbooks
, having Chapters
, containing Recipes
, owning Directions
". In terms of good OO design, you model exactly this: a class Cookbook
with an aggregate list of Chapters
, having an aggregate list of Recipes
, each one with a list of Directions
. This is a straightforward mapping from "real world objects" to "OOP objects", which is one of the core ideas of object oriented modeling. It is easy to understand and maintain for you and anyone else who has to deal with this data.
At this point, your code is not yet messy. It might become messy if you often have to write methods like
// inside a Cookbook method:
foreach chapter in listChapters
foreach recipe in chapter.listRecipes
foreach direction in recipe.listDirection
// do something with direction
You can avoid writing such code more than once if your programming language has good support for creating your own iterator function (like C# or Python). Then you can easily provide resuable helper functions once for getting, for example, all directions in a cookbook. Another approach it to utilize the visitor pattern, it will allow you to provide different operations "visiting" all objects in that hierarchy and do "something" during the visit.
Of course, when you have uniform containers, where on each level of the hierarchy a container can hold a list of containers of the same type, then the composite pattern suggested in @TulainsCórdova answer makes sense. Examples for this are a file/folder hierarchy, or a tree structure for abstract syntax trees, where expressions contain subexpressions which contain subexpressions, and so on. But in your example, each of the container object you described on each level is different from each object on another level, it has a different name, probably a different behaviour, and contains only sub containers of one specific type. So trying to shoehorn the described case into the composite pattern will probably make your code really messy.
-
Needs more details: could you please elaborate on how you think the helper functions might look? Mainly, does Cookbook know about ingredients, or only about recipes? How to navigate the layers?user949300– user94930009/07/2016 21:16:13Commented Sep 7, 2016 at 21:16
-
3@user949300: it would be helpful if the OP would give an understandable example where his code "becomes messy" (as he wrote), then I could probably tell him how to tailor an iterator for this. But so far the ony response I see that he accepted a clearly overengineered answer which does not really fit to his question in favor of mine. So I have to assume I might not have understood his intentions correctly.Doc Brown– Doc Brown09/08/2016 05:29:15Commented Sep 8, 2016 at 5:29
Explore related questions
See similar questions with these tags.
Menu
to own all theIngredient
s. Is there also aCookbook
in your hypothetical scenario?