Re: A Design Problem



Responding to Mousam...

Hi All,
First of all forgive me for the length of this post.

I am developing one library for some text editor. In this
library
I want to provide a set of C++ classes which will enable client (of
the library) to add tool bars to the text editor. The requirements
are:

-> Client can add as many tool bars as he wants to add.
-> In each toolbar there will be some tools. Tools can be of button
type or list box type like in MS word a tool bar can contain either
buttons or list box (For simplicity consider these two types of tool
only).

I took following approach to design the solution of above problem.

-> There will be a class say ToolBarMgr which will manage the
collection of all tool bars.

Anything with 'manager' on the end makes me nervous because the vast majority of the time is it just a high level node in a procedural functional decomposition tree or it is some kind of god object with it fingers in everything.

-> There will be a class say ToolBar which will manage collection of
all tools of this toolbar. This class will also contain information
particular to a toolbar.

Now I am bit confused about the design of Tool class because there
will be several functionalities which will be valid for button type of
tool but does not make sense for list box type of tool and vice
versa. Say e.g button tool can have one API to change the image of
button but this API does not make any sense to have in list box tool.

I am not sure I see the problem. So far you have described:

[Window]
A
| R1
+---------+--------+---...
| |
[MainWindow] [Dialog]
| 1
|
| R2
|
| contains
| 0..*
[Toolbar]
| 1
|
| R3
|
| contains
| * 1 represents R5 1
[Tool] -------------------------- [Icon]
+ iconRelativePosition + modify()
+ isGrayedOut
...
A
| R6
+--------------------+---....
| |
[ListBox] [Button]
+ elementCount + command()


There are two approaches which I can think of to design the Tool
class.

1st Approach:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-> There will be a class called Tool which will contain information
common to all type of tools. Say e,g it will contain toolbar ID to
which this tool is associated.

OK.

-> There will be two more classes say ButtonTool and ListBoxTool which
will inherit from Tool class and contain information specific to their
type.

So far, so good.

-> Tool class will have all the methods which can be called on at
least one type of tool and have the default implementation which will
just throw some exception.

Not so good. The [Tool] is a superclass that identifies properties that /all/ tools share by definition. So every subclass /must/ provide a valid implementation of those properties and any client invoking the [Tool] property should be indifferent to which implementation is actually provided. It should not be possible to invoke a responsibility a Tool does not have so the exception is unnecessary.

-> ButtonTool and ListBoxTool class will override their specific
methods.

Implementation inheritance was one of those ideas that seemed like a good idea at the time but did not turn out well. It just opens up another dimension of foot-shooting with respect to LSP violations. Modern practice is to avoid implementation inheritance by making all superclass methods virtual whenever possible.

-> In this approach if client calls a method which is invalid for this
tool object, he will get an exception.

If the client needs to invoke a method that only exists in a subclass, then the client needs to navigate a relationship that is directly to the subclass, not the superclass. Then the client can be sure that whoever is on the other end of the relationship has the needed properties. It is the responsibility of whoever instantiates that relationship to ensure that it is only instantiated to Tools having the right properties.

2nd Approach:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

-> There will be a class say Tool which itself will not be templatized
but will contain member templates similar to the class boost::any.
This class will contain information which will be common for all type
of tools. Also this class will contain the object of ButtonTool class
or ListBoxTool class or anything.
-> Tool class will also contain one method say GetObject() which will
also be templatized and will return the object it contains. Before
returning object the GetObject() method will make sure that it is
returning correct type of object. If it can not return correct type of
object it will throw some exception. So its implementation will be
something like this

template <typename ValueType>
ValueType* Tool::GetObject()
{
If(the ValueType
typeid is same as that
of type id of the
object Tool class contains)
{
return address
of the object
}
else
{
throw some
exception
}
}

-> So now if client is having object of the Tool class and if he knows
the exact type of tool; Tool class contains, he will call the
GetObject() method of the Tool class to get the exact type of tool
and then can call methods on that tool directly. But if the client
does not know the exact type of tool; Tool class contains, still he
can call the common methods (methods which are applicable to all type
of tools).

I really don't like this at all. Essentially this is just a complicated way of doing a downcast in a generalization relationship.

In both your examples you seem to be seeking ways for a client to decide which properties can be accessed by determining the type of the receiver. That is a major no-no in OO development because it creates implementation dependencies.

Ideally objects collaborate by sending each other messages. However, there should be no expectation in the sender implementation about what will happen in response to the message. IOW, OO messages are supposed to be announcements of something the sender did. What happens as a result or even who cares what the sender did should not be known in the sender implementation.

In fact, in OOA/D one addresses messages to the right object (i.e., one that cares about what the sender did in the overall solution context) at a different level of abstraction (e.g., a UML Interaction Diagram) from where the specific object implementations were defined. [The OOPLs don't make this distinction because they are 3GLs and the type systems map messages to method signatures. Since we name methods by what they do, this blurs the separation of message and method badly. But so long as the OOA/D is done with the right mindset, it is not a problem.]

One uses relationship instantiation to limit who can participate in collaborations. That's because the business rules and policies that govern WHO participates in a collaboration are often quite different than the rules and policies about WHEN objects should collaborate or WHAT they do when they collaborate. The use of relationships in this way facilitates separating those concerns so that they can be isolated and encapsulated.

Bottom line: the sender should not know who is on the other end of a relationship path or what it will do when the sender sends a message. Ensuring the independence of the sender implementation from external context is the main way that the OO paradigm ensures more maintainable applications.

I think you need to sit down and determine which clients will need which [Tool] services and then make sure that relationships are properly instantiated so that they always talk to the right objects.


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



.