|  | Design Question |  | |
| | | Pradeep VN |  |
| Posted: Mon May 26, 2008 12:19 pm Post subject: Design Question |  |
Hi all, I have the following doubt in designing a tree class. My tree has children, which may be either a childgroup or itemfields. I am maintaining an object list class to keep these children. Now, I want to get the childgroups and itemfields separately. To accomplish this, I have two options 1) Add two methods (one will retreive childgroups and the other, itemfields) to the tree class. This is because the tree "has" child groups and item fields 2) In another viewpoint, its better to add the methods to the Children object list class. This is because, the children are "of types child group and itemfield".
Both seems to be correct functionally. Please suggest me the best solution. Thanks in advance, Pradeep |
| |
| | | Stefan Ram |  |
| Posted: Mon May 26, 2008 12:19 pm Post subject: Re: Design Question |  |
| |  | |
Pradeep VN <mailmefrequent@gmail.com> writes:
| Quote: | Now, I want to get the (...) itemfields separately.
|
Do you want a call that retrieves all item fields at once or a call that retrieves a specific itemfield?
In the first case: What should be the type of the result of this call?
In the second case: How is the item field to be retrieved indicated to the call?
Anyway, »Tell, don't ask« suggest not to ask objects for data, but to tell them to apply operations to certain other objects. For example, you'd pass in an operation to be applied to all item fields, you'd pass in an operation to indicate which item field to be removed an so on.
When you ask for components of an object, you want to retrieve data instead of asking for behavior.
If you want to retrieve all items fields at once, the result needs to be some container, containing these fields. But the (sub)tree allready is such a container containing all its direct item fields. So not much would be gained that way. |
| |
| | | Daniel T. |  |
| Posted: Mon May 26, 2008 12:19 pm Post subject: Re: Design Question |  |
Pradeep VN <mailmefrequent@gmail.com> wrote:
| Quote: | I have the following doubt in designing a tree class. My tree has children, which may be either a childgroup or itemfields. I am maintaining an object list class to keep these children. Now, I want to get the childgroups and itemfields separately.
|
When you find yourself wanting to dig into an object to get at the bits, ask yourself, "What is it that I'm trying to do with the data and why doesn't the class perform that operation for me?" |
| |
| | | H. S. Lahman |  |
| Posted: Mon May 26, 2008 3:11 pm Post subject: Re: Design Question |  |
| |  | |
Responding to VN...
| Quote: | I have the following doubt in designing a tree class. My tree has children, which may be either a childgroup or itemfields. I am maintaining an object list class to keep these children. Now, I want to get the childgroups and itemfields separately. To accomplish this, I have two options 1) Add two methods (one will retreive childgroups and the other, itemfields) to the tree class. This is because the tree "has" child groups and item fields 2) In another viewpoint, its better to add the methods to the Children object list class. This is because, the children are "of types child group and itemfield".
|
First, let's look at the basic structure. I assume a 'ChildGroup' can have descendants that are either other ChildGroups or ItemFields or both. If so, then this sounds very much like a Composite design pattern:
[Tree] | 0..1 | base of | | R1 | | 1 * contains [TreeElement] ----------------------------------+ + elementType // {AGGREGATE, SINGLE} | A | | R2 R3 | ...--+-----+----------+----------+ | | | | 1 | [ItemField2] [ItemField1] [ChildGroup] -------------+
Presumably [Tree] has an algorithm for "walking" the tree (R1 -> R3) to collect all the ItemFields or ChildGroups. To properly handle that navigation all it needs to know is whether the TreeElement in hand is an aggregate or not.
Thus the client needing elements from the tree would talk to [Tree]. The interface might be something like:
[Tree] + reset() // start new search + getNextItemField() + getNextChildGroup()
but there are lots of variations depending on the actual problem in hand. The key issue is that the tree structure is captured in relationships and [Tree] has the intelligence to "walk" the relationships. Thus the tree could be quite simple or something exotic like a red/black tree and the client needing elements wouldn't know anything about the implementation. Similarly, [Tree] would understand how to do things like backtracking up limbs if necessary (e.g., breadth-first or depth-first searching).
[Note that [Tree] and the structure really don't care what the semantics of what an ItemField is. That is the client's issue when it collaborates with the specific TreeElement. All [Tree] does is navigate a structure. Also note that elementType is abstracted in terms of the tree structure rather than the specific type of a TreeElement.]
Alas, from a purist viewpoint there is a potential Catch-22 here. If Client invokes Tree::getNextItemField, how does it know what type of field it actually is (ItemField1, ItemField2, etc.)? The one thing one doesn't not want to do is have Client invoke some sort of kludge like dynamic_cast to figure what subclass it is. There are some ways around it, depending on circumstances...
(1) If the only thing that the different flavors provide are different implementations, then Client doesn't need to know what flavor it is because it can access then polymorphically through a common interface. This would be the preferred approach so it would be worthwhile spending some time to come up with [ItemFieldN] abstractions that could be accessed this way.
(2) Provide more specific interfaces for [Tree] like getNextItemField1(). In effect the interface is defining a direct relationship to the subclass temporarily.
(3) Have getNextItemField() return a tuple of {ItemField address, type}. Ordinarily this would be a no-no but here one can argue that [Tree] is basically a heterogeneous collection class, one of whose responsibilities is to keep track of what types it is collecting. It is still ugly because Client needs a switch statement internally (similar to using dynamic_cast) to dispatch to the right interface based on the 'type' value returned.
[This is better when some ItemFields can be accessed polymorhically while others cannot. The 'type' has semantics that are not directly associated with individual subclasses. It also works well when there is problem space semantics for 'type' that just happens to map into the way subclasses are defined for ItemField (e.g., a partName like "6-32 Pan Head Screw" that would apply to all and only members of the subclass).]
(4) Rather than returning an ItemFieldN, have getNextItemField() instantiate a direct relationship between [Client] and the returned element:
[Client] | 0..1 | current item for | | R4 | | 0..1 [ItemFieldN]
That relationship would have the right subclass type. But this gets messy quickly on the client side because the client needs to find the relationship that is actually instantiated. (The client also needs to be careful to de-instantiate the relationship when if is done with the ItemField.)
-- There is nothing wrong with me that could not be cured by a capful of Drano.
H. S. Lahman hsl@pathfindermda.com Pathfinder Solutions LINK blog: LINK "Model-Based Translation: The Next Step in Agile Development". Email info@pathfindermda.com for your copy. Pathfinder is hiring: LINK (888)OOA-PATH |
| |
|
|