I'm trying to learn about binary trees and have created a generic Node class where T is the type of the value. My challenge is to implement a way for a node to identify if it's a leaf without cluttering my code with null checks everywhere. I initially thought about adding an isLeaf flag, but I prefer a solution that doesn't allow certain methods to be called on leaf nodes (like getValue or setLeft) without explicitly managing checks in each method. I experimented with making Leaf a subclass of Node, but that feels awkward and leads to unnecessary casting.
I'm looking for a clean solution that avoids: 1) extra handling code for each new method, 2) excessive casting, 3) using raw types, 4) unsafe code, and if possible, I'd like to just have one static Leaf object to reuse. I know this might sound a bit demanding, but I'm eager to learn proper practices in this area! Any suggestions would be greatly appreciated!
2 Answers
You could consider using the Visitor pattern for your tree structure. It can help with separating operations from the node structure, making your code more organized. But it does mean all methods would have to pass through the visitor interface, so I'm not sure if that's what you’re looking for. If you adopt this approach, you'd need to write specific handling for Leaf nodes in your visitor, which seems to contradict your goal of avoiding excessive handling code. So it might not be the best fit after all.
You might want to look up the "Sentinel Value" and "Null Object Pattern". From what you described, it sounds like you're dealing with EmptyNodes rather than traditional leaf nodes since they don't hold any values. It's common to have something like an EmptyNode that inherits from Node, and though you may need to accept type parameters without using them, this shouldn't be too cumbersome.
Some methods can throw exceptions when called on an EmptyNode while others can have smart default behaviours, like an empty list for `getChildren`. You may also want to consider reusing the same EmptyNode object across your tree or use factory patterns for that. Also, a simple approach would be to use Optionals for the left/right pointers, which keeps it straightforward and avoids unnecessary checks.
Absolutely! Optionals sound like the tool you need. Just a quick follow-up—in Java, since you're keeping data only in the inner nodes, would it be feasible to implement something like a final static empty node? That way, you'd sidestep creating new instances. If your class is generic, though, it complicates it since static fields can't use type parameters directly. You might want to explore whether an 'Option'-like approach would help your scenario too.
That's an interesting point! Optional does require checks for presence, yet it's similar to null handling. Just keep in mind that it might add an extra pointer dereference for performance-critical applications. But hey, it's definitely a solid solution for a learning exercise!

Thank you for the insight! I'm still not sure if I fully get how a visitor would apply here without added complexity. It might be great for keeping things tidy, but multiple custom cases would still require maintenance, right?