Responding to Daniel T. and Whoever...
[color=blue]
> The following is a message I came across on comp.lang.c++, it sounded
> like a good topic of discussion for comp.object as well, so I'm
> forwarding it here...
>
> ========== Begin Forwarded Message ==========
> From: "Chocawok" <nospam@nospam.com>
> Subject: tightly coupled class design problem
> Date: Fri, Jan 27, 2006 6:11 PM
>
> Some of the classes in my app are graphical.
>
> To encapsulate the graphical side of things I had created a class called
> "sprite" which holds a bit map and knows how to draw itself etc.
>
>
>
> The classes that are graphical contain a sprite object.
>
>
>
> MyClass
>
> {
>
> private:
>
> Sprite this_objects_sprite;
>
> }[/color]
Given that C++ has reference pointers, I am afraid I not keen on this
and your first sentence. The sprite is a graphical representation /of/
whatever entity MyClass abstracts from the problem space. That
qualifies the sprite as a distinct conceptual entity that is a peer of
MyClass.
So it shouldn't be part of the implementation of MyClass and one needs a
reference based relationship implementation for it. If one thinks of it
as a peer via
1 corresponds to 1
[MyClass] --------------------- [Sprite]
then
Sprite* this_objects_sprite;
just implements the peer relationship. That segues to...
[color=blue]
> The sprite class requires a pointer to a "video surface". There is no
> way
> around this. The pointer is required for the construction and drawing of
> the
> sprite object.[/color]
Fine. All the more reason to make Sprite a peer object.
[color=blue]
> My problem is this:
>
>
>
> This design means that I have to pass the pointer to the video surface
> to
> myclass in its constructor, e.g.
>
>
>
> MyClass
>
> {
>
> private:
>
> Sprite this_objects_sprite;
>
>
>
> public:
>
> MyClass(VideoSurface* s)
>
> {
>
> this_objects_sprite = new Sprite(VideoSurface*
> s);
>
> ...
>
> ...
>
> }
>
> ...
>
> ...
>
> }
>
>
>
> Obviously this means all my graphical objects are tightly coupled to the
> graphics system I am using. Also obviously (I think) is that this is
> undesireable.[/color]
I don't see a problem here so long as this_objects_sprite is just
instantiating a relationship. As you indicate above, that relationship
is fixed in the problem space. Therefore it must be established somehow.
If the relationship is fixed throughout the like of MyClass, then
instantiating the reference via the MyClass constructor is fine. If the
context can change during MyClass' life cycle, then one would need a
set_sprite method to re-instantiate the relationship properly when the
context changes.
[Note that even if it is fixed for life now, one can add set_sprite
later if things change with minimal hassle because it /is/ just a
relationship implementation rather than part of the MyClass
implementation. This becomes even more important if one later decides
that [Sprite] needs to be accessed by other objects than {MyClass].
Keep this in mind in the discussion of relationship path navigation below.]
However, the key issue is that relationship navigation is orthogonal to
class semantics. MyClass may or may not need to collaborate with the
Sprite. Assuming MyClass has some other role in solving the customer's
problem, Sprite will probably be accessed by some other object that
understands graphical issues like when the display needs to be rendered
or updated. So it is likely that Sprite will be accessed /through/
MyClass by that object and MyClass won't know anything about that, as in:
[Renderer]
| 1
| displayed by
|
| R1
|
| *
[MyClass]
| 1
| corresponds to
|
| R2
|
| 1 * constrains 1
[Sprite] ------------------------ [VideoSurface]
R3
To do the display Renderer needs the the information from [Sprite] so it
navigates R1 -> R2 to get it. In doing so MyClass is just a way point
in addressing the collaboration to the right [Sprite] instance. Since
Renderer's collaboration is peer-to-peer with Sprite, it doesn't need to
know and shouldn't know anything about the semantics of MyClass (other
than the implementation of the relationship path).
Similarly, if Renderer needs to collaborate with the relevant
VideoSurface, then it also needs to navigate R1 -> R2 -> R3. But in
that peer-to-peer collaboration Renderer needs to know nothing about
MyClass or Sprite. More important to your concern here is that MyClass
doesn't need to know anything about that collaboration or the semantics
of [VideoSurface]. It's only knowledge of [VideoSurface] is the Class
type for C++ to properly type the reference pointer that implements the
relationship.
[Automatic code generators for OOA models don't even need to know the
class. Typically they instantiate relationships with Object*
references. The only place the class type /needs/ to be defined is in
Renderer so it can access properties. The code generator can cast that
safely because it knows from the UML model exactly what the class /must/
be when it write the [Renderer] code.]
So if all one is doing is instantiating a relationship, then there is no
heavy duty coupling between [VideoSurface] and any class along the path
except [Renderer]. (Renderer is necessarily coupled because it is doing
peer-to-peer collaboration with [VideoSurface].) In fact, I believe
that the main role of relationships in the OO paradigm is to provide a
context-independent mechanism to /ensure/ decoupling except among
collaborating peers. [Almost as important is the management of access
to state variables (attributes).]
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindermda.com
blog:
http://pathfinderpeople.blogs.com/hslahman
(888)OOA-PATH