From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,9eccdbc9cfa1c9b9,start X-Google-Attributes: gid103376,public From: ncohen@watson.ibm.com (Norman H. Cohen) Subject: Re: OO Idiom: Interfaces for derived classes Date: 1996/03/21 Message-ID: <4is6mk$1dn6@watnews1.watson.ibm.com> X-Deja-AN: 143605668 distribution: world references: organization: IBM T.J. Watson Research Center reply-to: ncohen@watson.ibm.com newsgroups: comp.lang.ada Date: 1996-03-21T00:00:00+00:00 List-Id: In article , mmann@ibm.net (Gerd Moellmann) writes: |> After studying the ARM and "Ada as a second language" (Normal ^ This is an abnorman way to spell my name, but that's okay. I've been called worse things than "normal". ;-) |> H. Cohen, 1996), I came to the conclusion that Ada as a language does |> not directly address/ has no apparently obvious approach wrt |> |> whether it is desirable to separate interfaces for clients of |> classes vs. derived classes, and if so, how to implement the |> separation. ... |> These were the reasons I got to the following coding idiom: |> |> o For every tagged type "base" in a package "base_package", the |> public interface of "base" is declared in "base_package" as |> normally for ADTs. |> |> o Functions and procedures needed for derived class implementation |> are declared in the private section of "base_class" when they must |> be primitive. |> |> o A child package "base_class.derived" contains, in its public ^^^^^^^^^^ (Presumably you mean base_package.) |> interface, functions/procedures that together form the interface of |> "base" for derived classes. These functions may access private |> functions/ data of "base" because "base_package.derived" is |> considered to be part of class "base". |> |> o A derived class "derived" is implemented in a package |> "derived_package" that is not a child package of "base_package" to |> prevent "derived" from relying on the private implementation of |> "base". |> |> o "derived" uses the public interface of "base" together with the |> services in "base_package.derived" for its implementation. |> |> What are your thoughts about it? Do you think it is as important as I |> do? Are there perhaps other ways of doing it? Note that your solution still requires voluntary compliance with a convention--that only units implementing derived classes should contain with clauses for base_package.derived. Rather than trusting the whole rest of the program, I prefer the "traditional" solution of declaring descendents of base_class in child packages of base_package, which is more straightforward and only requires trusting the writers of those child packages to use declarations in the private part of base_package appropriately. (If you are not the trusting sort, you can use an access-management tool to ensure that no "unauthorized" children of base_package are written. After all, you must trust programmers not to make unauthorized changes to the text of base_package itself, or else enforce this with an access-management tool. Page 471 of _Ada_as_a_Second_Language_ states, "In a sense, since a child unit is logically nested inside its parent, the author of a child package creates a modified version of the parent package. Only a programmer who would be trusted to modify the text of the parent itself should be trusted to write a child unit of a package with a private part.") The situation gets more complicated when we turn our attention from a snapshot, or release, of a program to a program as an living entity that evolves over time, or to the distribution as a reusable component of a package providing some OO class. Then it becomes important for the writer of the parent package to provide documentation distinguishing those aspects of the parent package that represent supported parts of its interface from those parts of the parent package that represent implementation choices that may change in future releases of the parent package. At one extreme, the writer of the parent might guarantee the proper functioning of the package only as long as no children are written for it. At the opposite extreme, the writer of the parent may commit to preserving all the declarations in the private part of the package in all future releases. But the child should ask not (just) what the parent can do for it; the child should ask too what it can do for (or to) its parent: If the writer of the parent does not rule out child packages entirely, he must also document those invariants that all children of the package must preserve if the package is to continue to function correctly. Back to the idiom you propose: The usual reason for a derived class referring directly to members of a base class is efficiency. If you are going to force all manipulations of the base-class implementation to be through subprograms exported by Base_Package.Derived anyway, you lose the motivation for giving implementations of derived classes special privileges. Why distinguish then between operations that are acceptable only for derived classes to perform and those that are acceptable for all clients to perform? If you have truly hidden the implementation of the base class from derived classes, then it would seem that any operation deemed safe for the implementation of the derived class to perform will also be safe enough for the world at large to perform. If you have a specific example that suggests otherwise, it would be interesting to see it. Having said all that, if you really want to, there IS a way in Ada 95 to mimic the C++ distinction between protected and private members, at the cost of a level of indirection when accessing private members: package P is type T is tagged private; ... private type Private_Components_Type; type Private_Components_Pointer_Type is access Private_Components_Type; type T is tagged record ... -- "protected" components declared here Private_Components : Private_Components_Pointer_Type; end record; end P; package body P is type Private_Components_Type is record ... -- "private" components declared here end record; ... end P; The "protected" components are visible in the private parts and bodies of children of P, but the "private" components are not. -- Norman H. Cohen ncohen@watson.ibm.com