From: Firkraag on
Hello
When doing an excersise project I've come up with the following (part
of) UML diagram:
approach 1: http://img337.imageshack.us/my.php?image=approach1lt0.png

In that approach the concrete classes depend on the interface class,
which is as far as I understand "OK" or "not bad". But I've read in
"Head First Design Patterns", that it is best to create dependencies
between "high level abstractions" i.e. for example between interfaces
(pure abstract classes in C++). So I've come up with the following
approach:
approach 2: http://img230.imageshack.us/my.php?image=approach2fg8.png

But is this 2nd approach really better? I mean now (in the 2nd case),
in the (interface) class IShape I have to store some kind of reference
to the (interface) class IScreen (in C++ for example a pointer).
Somehow I am not happy with the idea. What was a nice pure abstract
class now has some kind of physical weight. And in Java I wouldn't
even be able to do that, since you can't store any data in that
"purest form of interface", right? Moreover why should I be forced to
store any kind of reference to IScreen, what if I wanted some of my
shapes to use ISoundSystem, anyway this has become less universal than
the first approach I think.

PS. don't mint borderColor variable in IShape, pretend it is not
there :)
From: Daniel T. on
Firkraag <firkraag07(a)tlen.pl> wrote:

> When doing an excersise project I've come up with the following (part
> of) UML diagram:
> approach 1: http://img337.imageshack.us/my.php?image=approach1lt0.png
>
> In that approach the concrete classes depend on the interface class,
> which is as far as I understand "OK" or "not bad".

If you are going to go with approach 1, I say remove the IShape class.
It adds absolutely nothing to the solution. You have an implementation
inheritance case, but no implementation to inherit.

> But I've read in
> "Head First Design Patterns", that it is best to create dependencies
> between "high level abstractions" i.e. for example between interfaces
> (pure abstract classes in C++). So I've come up with the following
> approach:
> approach 2: http://img230.imageshack.us/my.php?image=approach2fg8.png

Here too, IShape is inherited simply for implementation (no one ever
sends a message to an IShape apparently.) At least IShape has a purpose
in this diagram, a small one to be sure, its only job seems to be to
hold a reference to an IScreen, but that's something...

> But is this 2nd approach really better? I mean now (in the 2nd case),
> in the (interface) class IShape I have to store some kind of reference
> to the (interface) class IScreen (in C++ for example a pointer).
> Somehow I am not happy with the idea. What was a nice pure abstract
> class now has some kind of physical weight. And in Java I wouldn't
> even be able to do that, since you can't store any data in that
> "purest form of interface", right? Moreover why should I be forced to
> store any kind of reference to IScreen, what if I wanted some of my
> shapes to use ISoundSystem, anyway this has become less universal than
> the first approach I think.

If all IShapes send messages to IScreens, then they need to have some
way to access the IScreen they can send messages to, and even if they
also send messages to ISoundSystems, they still (by definition) send
messages to IScreens.

To reiterate, if you have an interface that has no uses or aggregation
arrows pointing toward it (only inheritance arrows,) then the interface
can safely be done away with. If you have a class (i.e., it contains
implementation details) that only has inheritance arrows pointing to it
(i.e., implementation inheritance only,) then you can (and should IMO)
turn those arrows into aggregations rather than inheritance.
From: H. S. Lahman on
Responding to Firkraag...



> Hello
> When doing an excersise project I've come up with the following (part
> of) UML diagram:
> approach 1: http://img337.imageshack.us/my.php?image=approach1lt0.png

Basically I agree with Daniel T. As Daniel T. points out, without the
border stuff there is no reason to have IShape at all. What IShape
provides is a means for a screen to collaborate with a shape without
knowing which shape it is collaborating with.

The real issue here is about the relationships to IScreen. The reason
that one employs relationships to subclasses is because the client
navigating the relationship cannot accept the implementations provided
by other subclasses. [Not quite true; there is an arcane situation where
the relationship enforces an problem space rule that is orthogonal to
the subclass implementation. But let's not go there.]

In your case, though, IScreen seems to be able to access either subclass
implementation. Therefore IScreen is indifferent to which subclass is
actually there. Therefore IScreen should collaborate with IShape. So...

>
> In that approach the concrete classes depend on the interface class,
> which is as far as I understand "OK" or "not bad". But I've read in
> "Head First Design Patterns", that it is best to create dependencies
> between "high level abstractions" i.e. for example between interfaces
> (pure abstract classes in C++). So I've come up with the following
> approach:
> approach 2: http://img230.imageshack.us/my.php?image=approach2fg8.png

This is better. Now a screen doesn't care which flavor or a shape it is
collaborating with.

>
> But is this 2nd approach really better? I mean now (in the 2nd case),
> in the (interface) class IShape I have to store some kind of reference
> to the (interface) class IScreen (in C++ for example a pointer).
> Somehow I am not happy with the idea. What was a nice pure abstract
> class now has some kind of physical weight. And in Java I wouldn't
> even be able to do that, since you can't store any data in that
> "purest form of interface", right? Moreover why should I be forced to
> store any kind of reference to IScreen, what if I wanted some of my
> shapes to use ISoundSystem, anyway this has become less universal than
> the first approach I think.

I'm afraid I don't see your concern. The relationships need to be
implemented in both versions. The directionality of navigation will
depend on how they collaborate:

case I: a shape invokes screen behaviors. Then you need a pointer in
IShape to reach IScreen.

case II: a screen invokes shape behaviors. Then you need a pointer in
IScreen to reach IShape. [This is a problem in the first version for two
reasons. One is that you need two pointers, one for each flavor of
shape. The second is that a screen needs to make a decision about which
pointer to navigate when it needs to collaborate with a shape. Avoiding
burdening IScreen with that decision manifests why your second version
is preferable.]

In either case the relationships need to be implemented in the same way
in both of your versions because the class semantics hasn't changed so
<presumeably> the collaborations haven't changed. At the OOPL level, it
is difficult to implement such pointers, especially in statically type
languages, without introducing a lot of physical coupling. One reason
why the second version is preferred is that by abstracting the interface
via superclasses one can reduce the amount of physical implementation
detail that is exposed to the compiler.

>
> PS. don't mint borderColor variable in IShape, pretend it is not
> there :)

I don't think they can be ignored because they are crucial to why IShape
exists in your first example. They introduce true implementation
inheritance, which is an entirely different issue than relationship
implementation and collaboration between a screen and shape.

Implementation inheritance is one of those early OOPL features that
seemed like a good idea at the time but turned out badly when tested
over time. As a general rule one should avoid implementation
inheritance. The problem is that it introduces a new dimension into how
behaviors are resolved. That is, the client can see a different behavior
depending on where an inherited implementation is introduced in the tree
and where it is overridden. That tends to be a maintenance nightmare
when somebody performs surgery on the generalization tree because it can
break existing <superclass> clients.

If one has common implementation actions, one can eliminate the
redundancy with private class methods that the public leaf subclass
behaviors invoke. Then the client does not need to worry about whether
the behavior will change based on where in the tree the collaboration
access is done. Since the behavior is always explicitly defined at the
leaf level all the client needs to check is that the relevant leaves
invoke acceptable private methods.


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

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



From: Firkraag on
Oh boy. The UML diagrams which I provided given were
"reverse_engineered" from my C++ code in a free UML tool. The thing is
I made a mistake and also when it comes to dependencies that tool
doesn't take forward declarations into consideration. Its only concern
are header file dependencies. Here is the whole diagram, hopefully
good now (I've manually added the association arrow from DrawLogic to
IShape. I am really sorry for creating confusion, although maybe a
good thing have come out of it :)

http://img525.imageshack.us/my.php?image=fig1sz2.png

The original question holds: Should I leave it as it is (approach 1),
or make it so IShape holds a pointer to IScreen (but what kind of "I"
it is then)?
From: Daniel T. on
Firkraag <firkraag07(a)tlen.pl> wrote:

> Oh boy. The UML diagrams which I provided given were
> "reverse_engineered" from my C++ code in a free UML tool. The thing is
> I made a mistake and also when it comes to dependencies that tool
> doesn't take forward declarations into consideration. Its only concern
> are header file dependencies. Here is the whole diagram, hopefully
> good now (I've manually added the association arrow from DrawLogic to
> IShape. I am really sorry for creating confusion, although maybe a
> good thing have come out of it :)
>
> http://img525.imageshack.us/my.php?image=fig1sz2.png
>
> The original question holds: Should I leave it as it is (approach 1),
> or make it so IShape holds a pointer to IScreen (but what kind of "I"
> it is then)?

The main thing that strikes me is that Rectangle and Triangles send
messages to IScreens, but I don't see any way of telling a Rectangle or
Triangle what screen to use.

I expect that, through some means not shown, DrawLogic tells IShapes
what IScreen they can use. If that is true, then IShape should have an
association line to IScreen.

BTW, just because the UML shows an association between IShape and
IScreen (rather than between each IShape sub-type and IScreen) you need
not implement it that way. You can still put an IScreen pointer in each
IShape sub-type.

What you will have once you put the association between IShape and
IScreen is the Bridge pattern from GoF.