comp.lang.ada
 help / color / mirror / Atom feed
From: matthew_heaney@acm.org (Matthew Heaney)
Subject: Re: Uncle
Date: 1998/05/24
Date: 1998-05-24T00:00:00+00:00	[thread overview]
Message-ID: <matthew_heaney-ya023680002405981526140001@news.ni.net> (raw)
In-Reply-To: 6jlk5s$mob@tomquartz.niestu.com


In article <6jlk5s$mob@tomquartz.niestu.com>, chipr@niestu.com (Chip
Richards) wrote:


>On to specific questions.  First, there's an Object type (a tagged record--an
>*abstract private* tagged record at the moment, because that seemed like the
>right thing to do), from which about half the various widget types are
>derived, and a Composite type (called "puInterface" in the original code)
>which is derived from Object, and from which the rest of the widgets are
>derived.  Composite adds a pointer to a list of child Object items, and each
>Object contains a pointer to its parent Composite.  Since the two types are
>interdependent in this way, they must be declared in the same package spec,
>right?  No other choice?  So my urge to make Composites a child package off
>Objects is misguided?

This idiom is straight out of the GofF book (see the Composite pattern).

As always, the answer is, "It depends."

What is a composite doing to its children?  It sounds like they need to go
in the same package, but, you could just declare the root of the object
hierarchy and just the primitive operations needed by composite, in package
visitible to the composite package.  For example,

package PUI Is

   type Root_Object is abstract tagged ...;
   type Object_Access is access all Root_Object'Class;

   <primitive operations needed by composite>

   type Object_List is ...;  <collection of Object_Access items>

   type Root_Composite is 
      abstract tagged record
         List : Object_List;
      end record;

end PUI;


>Right now, Object and Composite are defined in the topmost package, called
>"PUI", along with their primitive operations.  There are child packages
>PUI.Objects and PUI.Composites, which define various related procedures, but
>only "internal" ones--all the user-visible subprograms that operate directly
>on those two types are also in package PUI.  I already don't like that
>structure, but I've got more pressing problems at the moment.

Why don't you like that structure?  

I think you could just isolate the primitive operations just required by
composite in the scope visible to the composite type.  That's what I did
above.  You can still have child packages, as in

package PUI.Objects is

   type Object is new Root_Object with ...;

   <the rest of the primitive ops for Object>

end;

package PUI.Composites is

   -- 
   -- I don't know if you really need this package.
   --

   type Composite is new Root_Compositive with ...;

   <the rest of the primitive ops for Composite>

end;

What you could do is move the declaration of root object into the private
region of PUI; then those "special operations" needed only by composite
could be declared there, so only children (like Composites) would have
visibility.  The "real" object type is declared its own package, as above. 
Something like

package PUI is
...
private

   type Root_Object is abstract tagged ...;
   type Object_Access is access all Root_Object'Class;

   <primitive ops for Root_Object needed by Composite>

end PUI;

with PUI.Composites;
package PUI.Objects is

   type Object is tagged private;

   <the public ops of object>

private

   type Object is
      new Root_Object with record
         Parent : Composite_Access;
 ...
      end record;
        

end PUI.Objects;

package PUI.Composite is

   type Composite is ...;
   type Composite_Access is access all Composite'Class;

   <primitive ops of Composite>

private

   type Object_List is ...;

   type Composite is
      tagged record
         List : Object_List;
 ....
      end record;

end;

Now Composite has visibility to (dispatching) operations of Object, which
it can use to implement its own primitive operations.

>One suggestion I've received is to combine Object and Composite into a single
>type.  I could do that, but it seems as if it would lose some information--all
>Composites are Objects, but not all Objects are Composites.

No, I don't think this is required.  Because you're manipulating pointers
to things, I don't think you have any real problem with mutual spec
dependencies.  Composite and Object can be declared in separate packages.

Actually, you probably want to declare the Root_Object in the public part
of PUI, because you probably need to declare it in the public part of
Composite, as in

package PUI.Composite is

   type Composite is ...;

   procedure Add_Child
      (C : in out Composite;  Child : Root_Object'Class);
or maybe
   procedure Add_Child
      (C : in out Composite; Child : Object_Access);
...
end PUI.Composite;

 

>In fact, my overall tendency is to want to put each of the various types and
>their associated subprograms together in separate packages.  And because most
>of the "major" user-visible types are derived from Object (which is itself not
>used at the "user" level), I tend to think in terms of defining Object in a
>"parent" package, and making all the other packages children of it.  However,
>I'm starting to get the idea that this isn't a very good plan.  Every time I
>try to split the code into neat chunks, a powerful force seems to want me to
>glom them into one giant package.  Ugh.

I think you can put them in separate child packages, though it may require
a downcast in the body of composite (from Root_Object to just Object) or
maybe the other way around (downcase from Root_Composite to Composite in
the body of Objects).

In any case, I think you need one root type visible to the other.  Either this

package PUI is

   type Root_Composite is ...;

end;

package PUI.Objects is

   type Object is ...;

   function New_Object (Parent : Composite_Access) return Object'Class;
   -- (ops only depend on Root_Composite, not PUI.Composites.Composite)

end;

with PUI.Objects;
package PUI.Composites is

   type Composite is new Root_Composite ...;

   <ops that depend on Object>

end;


-or- 

package PUI is

   type Root_Object is ...

 end;

with PUI.Composites;
package PUI.Objects is

   type Object is new Root_Object ...;

   <ops that depend on Composite type>

end;

package PUI.Composites is

   type Composite is ...;

   procedure Add_Child (C : in out Composite; Child : in Object_Access);
   
   -- (ops only depend of Root_Object, not on PUI.Objects.Object)

end;


Without studying your application some more, I can't say which approach is
better.  But the general concept is that a root type (acting as a sort of
type hook, to grab on to) must be available to the other.   (Perhaps this
root type can be declared in the private part of PUI, but I don't think so,
because it's used as an argument to the other type's public, primitive
operations.)

>Many of my problems doubtless stem from my practice of patterning the Ada
>structure after the C++ structure.  I mean, the C++ is broken up into (fairly)
>tidy separate little source files, which seemed like a nice structure to use.
>This is starting to seem like more and more of a mistake, and my current
>temptation is to rip the code apart and bolt it back together some other way.

The language has a profound influence on the design of the application.  It
wouldn't surprise me one ounce that the C++ structure isn't appropriate for
Ada.  But don't give up yet, as  I think it's possible to more or less
follow the C++ structure.

>But, which way?  Should I create independent packages like, say, Objects (as
>opposed to the current PUI.Objects) and Boxes, and include *those* in a main
>"PUI" package?  The problem with that is, for user programs to see primitive
>ops of type Object, they would have to "with" package Objects.  Now, to my way
>of thinking, that's just silly--users don't use "Objects", they use "Buttons"
>and "Sliders" and so forth.  Including a seemingly useless package because the
>language makes you do it that way seems wrong to me.

I think it's best to have a package that acts as the root of the subsystem,
and you are correct in creating a PUI package.  All other packages should
be children of PUI.

>I'm pretty sure it's me and not the language, however, and I'm sure some of
>you out there can do this stuff in your sleep.  I'm open to any suggestions.
>In case it matters, I'm using GNAT 3.10p under Linux.  If you've gotten this
>far, thanks very much for your time!

Feel free to email any time.  You should have other Ada users to talk to,
and I am willing to help.

(I'm trying to boostrap GNAT 3.10p under LinuxPPC.  Maybe you can help me
too...)

Matt




      parent reply	other threads:[~1998-05-24  0:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-05-17  0:00 Uncle Chip Richards
1998-05-17  0:00 ` Uncle Brian Rogoff
1998-05-24  0:00   ` Uncle Matthew Heaney
1998-05-25  0:00     ` Uncle Brian Rogoff
1998-05-24  0:00 ` Matthew Heaney [this message]
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox