I'm designing a web site navigation hierarchy. It's a tree of nodes.
Most nodes are pages. Some nodes are links (think shortcuts in Windows).
Most pages hold HTML content. Some execute code.
I'd like to represent these as this collection of classes and abstract (MustInherit) classes…
This is the database table where I'm going to store all this…
database table http://img178.imageshack.us/img178/8573/nodetablefm8.gif
Here's where I'm stumped. PageNodes may or may not be roots.
How should I handle the root class?
I don't want to have to have all four of…
- HtmlPageNode
- CodePageNode
- HtmlRootPageNode
- CodeRootPageNode
I want the HtmlPageNode and CodePageNode classes to inherit either from PageNode or else from RootPageNode. Is that possible?
Clarification: There are multiple root nodes and roots may have parent nodes. Each is the root of only a sub-tree that has distinct styling. Think of different, color-coded departments. (Perhaps root is a poor name choice. Suggestions?)
Update: Regarding the "Root" name...
I've asked: Is there a specific name for the node that coresponds to a subtree?
Should the PageNode class simply have a property of type Root?
Is that counter to the idea that a PageNode is-a Root. Or, are they not "is-a Root" because only some of them are roots?
And does that imply that the property might traverse the tree looking for the root ancestor? Or is that just me?
Yeah it's possible. You need to have HtmlPageNode and codePageNode have an object that will be an Abstract class that PageNode will inherit and RootPageNode too. In the constructor of HtmlPageNode and codePageNode you accept your new Abstract class that will be in your case PageNode OR RootPageNode. This way you have 2 differents classes with same methods but two differents object. Hope that help you!
Root is a poor name choice because it's (somewhat ironically) accepted as explicitly the top level of a tree structure, because the tree starts where root comes out of the ground. Any node beyond that is a branch or leaf and not directly attached to the root.
A better name would be something like IsAuthoritativeStyleNode, IsCascadingStyleNode, IsStyleParentNode or instead qualify it: e.g. IsDepartmentRootNode. Giving things clear unambiguous names is one of things that drastically improves readability / easy understanding.
You can't really achieve what you want just via abstract base classes/inheritence. As per other suggestion(s), consider interfaces instead.
I'd also consider thinking about whether you're letting the database schema drive your client side class design too much. Not saying it needs changing in this case, but it should at least be thought about. Think about how you could factor out properties into separate tables referencing the common 'Node' table, and normalize them to minimize nulls and/or duplicated identical data.
Use the the Composite Pattern.
With regard to your root nodes, are there differences in functionality or is the difference it entirely appearance? If the difference is appearance only I suggest you have an association with a separate Style class from your PageNode.
If there are differences in functionality AND you have lots of types of page then think about using the Decorator Pattern.
Actually, as the "root" node is a special case of node, maybe you need RootHtmlPageNode : HtmlPageNode.
Another idea: as you do not specify what is the difference between a "root" and normal node, maybe just a flag in node specifying if it is root or not also will be a good design.
EDIT: Per your clarification, there is no functional difference between normal and root node, so a simple flag should be enough (or property IsRootNode). If "root" node only supplies a styling data (or any other data for itself and it's children), then you can place this styling data in a separate structure/class and fetch it recursively (based on IsRootNode):
This assumes, that each node has a reference to it's parent.
It become way beyond the question, as I do not know the exact design.
As noted, the Composite Pattern may be a good solution.
If that doesn't work for you, it may be simpler - if appropriate - to define Root as an Interface, and apply that as needed.
Of course, that doesnt let you provide any implementation for Root...
If Root must have implementation, you can use the Decorator Pattern.