Re: A Design Problem



Responding to Keighley...

-> 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.


ah. that frightened me as well. I was thinking a do-nothing method
instead was a good idea, but apparently not.



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.


I suppose the question is why is his design trying to apply an
inapplicable method to an object. Why try and change the image
for a list box?

That is, indeed, the key question. Doing that should be a clue that something is wrong and one should look to the way the problem space was abstracted to figure out the root cause. IOW, providing a no-op method is just a kludge to get around a more basic problem.

The OO paradigm depends upon peer-to-peer collaboration (among other things) to avoid hierarchical implementation dependencies. The corollary is that objects should only be talking to objects that they need to talk to for the solution collaboration to take place. That is, they should only be talking to objects that care about what they did to trigger the collaboration.

The OO paradigm also uses relationships to limit participation in collaborations. In effect one captures the business rules of participation in static structure. That makes the application more maintainable and reliable (by reducing opportunities for defect insertion in executable statements).

At the same time the paradigm provides a rather elegant facility for dealing with generalizations and polymorphic dispatch. However, that facility is quite disciplined. One only talks to a superclass when one is indifferent to the implementation that /all/ subclasses are obligated to provide. If a subclass does does not inherently have an intrinsic property that is present in other subclasses, that property should not be identified as a common property in the superclass when abstracting the problem space. IOW, if members of the subclass don't have the property in the problem space, they shouldn't have it in the OOA/D model.

IOW, there are a flock of reasons why one should be worried about the overall design and should look for another way to allocate responsibilities and/or implement relationships when managing collaborations.

Would it be better for the GUI to ask the tool what it can do.
Eg. right click the tool to get a list of applicable operations
then you are never offered the option of doing something
the tool can't do. Yeah I know I'm confusing GUI design
with a discussion about basic OO principals

Let's assume the GUI is properly encapsulated in a subsystem or layer. The user clicks a button or selects a listbox entry. The GUI subsystem announces that by sending back a particular message. That message will probably have a different message identifier for each situation. It is up to the receiving subsystem interface to re-dispatch that message to the right flavor of object to handle it based in the message identifier. [Perhaps more important, the receiver can interpret that message identifier in its own context rather than the GUIs. IOW, the receiving subsystem doesn't even know Button and ListBox objects exist in the GUI.]

-> 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.


I think I've heard this before. Are there any good books that discuss
modern OOD?

None that I know of specifically. But as a translationist I deal with a smallish subset of UML where it isn't directly supported and I never have to touch OOPL code so I am unlikely to be reading the books that might deal with this. In addition, this only started showing up in the '90s as a coding standard. So I've usually only seen it mentioned on forums like this. [My speculation is that Java's explicit support of interface inheritance was provided to ensure an alternative to implementation inheritance. But I haven't seen any explicit evidence that was what they had in mind.]

-> 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.


is what I said above a way of doing this?

Yes, but I think there is a more basic way. Let's say there is a Client that needs to collaborate with the specialized properties of a Button. Rather than:

1 interacts with R1 *
[Control] ----------------------- [Client]
A
| R2
+---------+-------+---...
| |
[ListBox] [Button]

we should have:

[Control]
A
| R2
+---------+-------+---...
| |
[ListBox] [Button]
| 1
| interacts with
|
| R1
|
| *
[Client]

If we implement R1 with a pointer in Client, then when the relationship is instantiated by setting the pointer reference. Whoever does that should enforce the rule that Client can only talk to a Button. In an OOPL that would be enforced in the type system by making the pointer type be specifically a Button type rather than a Control type.

The more interesting question is: How do we know Client only needs to collaborate with a Button? That is, what if Client needs to access the correct properties of either a ListBox or a Button? The short answer is that it shouldn't and that is the root of the design problem rather than the mechanics of navigating relationships.

One set of reasons are the ones I provided above. Essentially they all conspire to say that [Client] should not know who it is talking to. But to make that decision the sender method must understand its context in the overall solution. That creates an implementation dependency in the [Client] method on context. But methods are supposed to implement /intrinsic/ behavior responsibilities of the underlying problem space entity. Those reasons play together to ensure that is true.

That is rooted in the fact that to collaborate objects send messages to one another and those messages decouple the objects. Unlike the procedural paradigm (Do This), OO messages should be announcements (I'm Done) of something the sender did. The developer then can map those announcements to objects that care about what was done. (In UML one can do that in an Interaction Diagram, which is at a higher level of abstraction than individual object implementations.) Obviously a no-op receiver doesn't care, so there is something wrong with the Big Picture.

One of the advantages of messages as announcements is that one can apply rigorous DbC to ensure correct flow of control in the overall solution. For any method to execute, some precondition must be satisfied in the overall solution. That precondition can only prevail if some other method did something to change the state of the overall solution. The postcondition of that other method will match the precondition of the method in hand for execution. So theoretically one could design all the object methods and after that one could apply DbC to back fill the necessary messages for the overall flow of control by mathcing preconditions to postconditions. (That's a tad tedious in practice so experienced developers only do that sort of DbC analysis when the flow of control is tricky.)

However, for this to work all the methods must be self-contained, encapsulated, logically indivisible, and independent of context. So when one gets into a situation where this sort of decision needs to be made in a method, it is symptomatic that the method itself is not well conceived. That will usually require two adjustments. The decision about who to talk to needs to be moved out of the method and put elsewhere (i.e., some other object instantiates the right relationship based on some context).

The second adjustment is redefining the Client method so that its responsibility fits the specific context of collaborating with a Button. That will usually mean providing two distinct methods with somewhat different responsibilities. IOW, one will likely end up with two responsibilities: one to deal with Buttons and one to deal with ListBoxes. (More precisely, one to deal with a context where the Button semantics is relevant and one to deal with a context where a ListBox semantics is relevant.)

[Caveat. Don't get hung up here on Button/ListBox from the original example. Those pure GUI artifacts and the problem solution won't even know they exist. As I indicated above, the problem solution will get a unique announcement message for each one and that will be used to dispatch to an appropriate method in the problem solution (as opposed to somebody talking to a Control and deciding what to do with it). IOW, the problem solution will interpret the message identifier in terms of its own solution context. That decision to do something with a control is fundamentally a procedural view and only seems plausible if one has not separated the GUI and solution paradigms.]


*************
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



.