There is no simple answer, knowing when and how to add functionality - this is what you should learn over time.
Just adding to the base class seems like a simple solution, but it pollutes your base class. If this is a class that you can reasonably expect from using another program (or even parts of your program), does the functionality you add add to the context of your class responsibility? If not, this is probably a bad move. Are you adding dependencies that link your base class with your specific use? Because if you chose to reuse the code directly from the window.
Inheritance is the solution that many engineers seek, and this is a seductive route. But since I grew up as an engineer, I use it sparingly. Inheritance should only be used in true relationships - and you need to respect behavioral subtyping or are you going to regret it later. And since Java allows only one inheritance, this means that you get only one snapshot when subtyping.
Composition (especially with interfaces) is often the best idea. Often, what looks like an is-a relationship does have one-one. Or sometimes all you really need is a helper class that has many functions that take your original class as an argument.
However, there is one problem with the composition; you want to save these objects in your tree. The solution here is the interfaces. You do not want the tree to store nodes. You want objects that have an interface that node can give you.
public interface HasNode { public Node getNode(); }
Your node class is HasNode with getNode just returning this. Your NodeVisualizer class is also HasNode, and now you can also store NodeVisualizers in your tree. Of course, now you have one more problem: your tree may contain NodeVisualizers and nodes, and this will not be good. Plus, when you return HasNode from tree functions, you have to drop them in the right instance and it is ugly. You want to use templates for this, but this is a different answer.
source share