Re: Design Problem Aggregation

From: John Carson (donaldquixote_at_datafast.net.au)
Date: 05/11/04


Date: Tue, 11 May 2004 13:51:02 +1000


"Merlin" <merlin2769@hotmail.com> wrote in message
news:12235816.0405101041.3f8b8e79@posting.google.com
> Hi John
>
> Thanks for your suggestion. I like your first suggestion but making
> the members public makes me a bit uneasy. Your second suggestion works
> well if no class sits between BASE and A or BASE and B.
>
> In order to make things clear, discard the original classes and
> consider the following scenario.
>
> Imagine we have the following classes
>
> BASE, A, B, C, D, E, F, G, H, I, and J (class diagram will help)
>
> Classes A, D, F, G, J inherit from BASE.
>
> Class B inherits from A
>
> Class C inherits from B
>
> Class E inherits from D
>
> Classes H and I inherit from G
>
> Class H has a member (aggregation by value) of type C.
> Class I has a member (aggregation by value) of type E.
>
>
> Class J has a member that is container that can accept objects of type
> G. As G is the base class of H and I we can add to the container
> objects of type H or I. I have made the container type safe in this
> way.

Please note that a container that can store objects of type G CANNOT store
objects of type H or I. If you attempt to do this, then you get "slicing" in
which H and I objects have everything sliced off them except for their G
base component. The compiler will let you do this, but it isn't what you
want (as you will discover when you attempt to use the objects you have
stored).

What is true is that a container that can store POINTERS to objects of type
G can store POINTERS to objects of type H or I.

> I wanted J to be a collection of objects of type C or E but never F or
> even BASE so I introduced an abstract class G and made the container
> of that type so it would only accept objects of base type G. However,
> although this looks ok, I am not happy with the extra work it has
> created.
>
> As I need to access the interface to C and E, I need to repeat all
> that interface in H and I. C and E have many member functions and I
> dont want to rewrite all that interface in H and I and delegate the
> calls to the aggregate.
>
> How can I change my design to make it better and flexible?
>
> If I do what you suggested I will have multiple inheritance and cyclic
> dependencies. What else can I do to get a type safe container? Should
> I put member functions in G to return the objects C and E? Doesnt that
> break encapsulation?

As I have already indicated, if the sole purpose of G, H and I is to
effectively give C and E a common base class, then there is no meaningful
encapsulation to protect in G, H and I. C and E should incorporate their own
encapsulation and that is what you should be relying on.

However, on further reflection, this strategy has problems of its own. The
whole point of using a container with more than one type is presumably to
implement polymorphism. So how are you going to return the member of H and
I? You presumably want a virtual function in G, GetObject, that is
overridden in H and I. The return type of this GetObject virtual function in
G must be a reference or pointer to a common base of C and E, which means in
this case that it must be a reference or pointer to BASE (note that H and I
are just containers so they have no inheritance relationship with C and E
and hence neither does G).

Here is the problem: the overrides of GetObject in H and I can have a return
type of a reference/pointer to C and E respectively, but it remains the case
that if GetObject is called using a pointer of type pointer to G (rather
than a pointer of type pointer to H or I), then the return type of GetObject
is determined by the function's declaration in class G. Thus the return type
is a pointer/reference to BASE. This means that you can only use the return
value of GetObject to call functions that are declared in BASE. Thus the
entire interface to C and E needs to be declared in BASE.

It is difficult to know what design would be ideal without knowing what each
class does. Possibilities:

1. Scrap G, H and I and introduce a base class Z incorporating the required
interface for C and E. Classes C and E would then inherit from Z and one
other class (B in the case of C and D in the case of E). This will only work
if you make C and E override all pure virtual functions in Z --- they can't
just inherit the functions from BASE.

2. As above, but make Z inherit from BASE, so that Z has BASE's interface
without the need for any typing. C and E would then potentially have two
BASE components --- one via Z and one via B or D. This may or may not be a
problem. If it is, you can avoid it by using virtual inheritance (this,
however, has performance penalties).

3. If your type safety requirements are accurately stated as "type C or E
but never F or even BASE", then you could achieve that by having X inherit
from BASE and having A and D inherit from X. Your container could then store
X pointers. This, of course, would not rule out A or D or B.

4. Perhaps the entire class heirarchy could do with a re-think.

-- 
John Carson
1. To reply to email address, remove donald
2. Don't reply to email address (post here instead)


Relevant Pages

  • Re: Design Problem Aggregation
    ... Something else also has to inherit from A or else B is an identity set ... > Class H has a member of type C. ... As G is the base class of H and I we can add to the container ... > that interface in H and I. C and E have many member functions and I ...
    (comp.object)
  • Re: Design Problem Aggregation
    ... Classes A, D, F, G, J inherit from BASE. ... Class H has a member of type C. ... Class J has a member that is container that can accept objects of type ... As I need to access the interface to C and E, ...
    (comp.object)
  • Re: Design Problem Aggregation
    ... > A, B, C, D, G inherit from BASE. ... > Class E has a member of type A. ... > Class G has a member that is container that can accept objects of type ... > that interface in E and F. A and B have many member functions and I ...
    (comp.object)
  • Re: Design Problem Aggregation
    ... > A, B, C, D, G inherit from BASE. ... > Class E has a member (aggregation by value) of type A. ... > Class G has a member that is container that can accept objects of type ...
    (comp.lang.cpp)
  • Re: strange crash after assertion with std::map::iterator
    ... in that you can never compare to it. ... found in the container but it's a member ... It may be implemented as a null pointer, ...
    (microsoft.public.vc.stl)