At 05:04 PM 4/13/98 -0700, C. Daniel Cooper wrote:
>Robert Eachus wrote,
>> I've actually had to convince projects to accept guidelines...
>> (More than six withs on a package spec, or fifteen on a package body
>> indicates that there is restructuring that should be considered. There are
>> good arguments for having more withs on the main program, but a with on a
>> subunit is almost always an indication of a design problem.)
>The above numbers intrigued me because some time ago we had noticed
>similar thresholds. In particular, we noticed that when interfacing to a
>binding for some subsystem written in C (such as for X-Windows), a more
>generous allotment needed to be permitted for bodies (about 20 or so).
I guess it depends on how high-level your X11 interface code is. My
preference is to concentrate ALL X interface stuff in the bodies of a few
packages, and these bodies basically end up with the same number of withs,
determined by the X (and Motif) library organization.
>However, your observation regarding subunits is unclear to me: are you
>suggesting that subunits should ideally have *no* WITHs? Moving such
>WITHs up to the parent body would meet the criterion, even though it
>would still be the same design.
First, one exception that I like to treat as such... Withs for
Unchecked_Conversion should be moved as low as possible. Other than that,
if you have say 10 subunits in one package, and five of them with the same
unit, why not put the with on the package body. However, if each subunit
has a disjoint set of withs, it is time to review your design. Why are
these things in the same package? Incidently something I find myself doing
in Ada 95 is putting all actual I/O for a type/class in a private child
package. That way changing the I/O paradigm (such as from X/Motif to (MS)
Windows) is localized. It takes getting used to the fact that inlining in
Ada works and allows you to keep your abstactions pure. With Ada 95 it is
even easier with renaming as body allowed.
>Here is some other data: In a recent review of several independent Ada
>projects, we noticed the following pattern of typical numbers:
># WITHable specs avg WITHs/spec avg WITHs/body max WITHs/body
> 164 2 or 3 2 20
> 340 2 or 3 4 28
> 1450 2 or 3 13 118
> 7006 2 or 3 20 983
>(Yes, that's 983 WITHs for a single body unit: it was a memory resident
>database that correlated data of all TYPEs across the whole system.)
There are techniques in Ada 95 that fix these problems. In particular,
with Ada 95 tagged types, you can use run time dispatching to call
suprograms defined in packages you do not with. I bet a good type
hierarchy for that last case would result in less than a dozen withs,
rather than almost a thousand.
>These numbers count subunits as bodies, and include unused WITHs, if
>any; bear in mind that a spec's WITHs need not be repeated in a
>body/subunit. It's an interesting pattern that invites speculation:
>The architecture (ie, inter-spec dependencies) is fairly constant
>regardless of program size, whereas the implementation inexorably
>grows in complexity as the system size grows; and it's nonlinear.
>Is this an intrinsic property or just weak design? A human limitation
>or state-of-the-art? Has anyone else observed such a pattern?
Yes, exactly. The "average" pattern you observe comes from doing
functional decomposition right in Ada. Using OO techniques--even in Ada
83--takes more design effort--and design time, but pays off in the long
run. I think that the growth you note at the extremes is just due to
invalid abstractions being left in the design, but it could be also be
related to functional decomposition.
The record for projects I have worked is 687 withs on one body. In
that case, and in other like cases I have seen, the problem was a single
easily (less than 2 weeks) correctable design error. I had been called in
on a "Red Team" on that project to see what we could do to help fix the
code. Fixing the easily fixable design errors did not completely eliminate
the performance problems, but it did alleviate them, and dropped the number
of withs per body (and recompiles) significantly. Incidently, that was an
Ada 83 program, and the change was to replace painful case statements with
hand coded run-time dispatching. It would have been much easier to do
right in Ada 95.
Robert I. Eachus
function Message (Text: in Clever_Ideas) return Better_Ideas is...