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=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,9eccdbc9cfa1c9b9 X-Google-Attributes: gid103376,public From: mmann@ibm.net (Gerd Moellmann) Subject: Re: OO Idiom: Interfaces for derived classes Date: 1996/03/22 Message-ID: X-Deja-AN: 143680589 distribution: world sender: mmann@ibm.net references: <4is6mk$1dn6@watnews1.watson.ibm.com> organization: None x-nntpdaemon: changi 0.9 for OS/2 newsgroups: comp.lang.ada Date: 1996-03-22T00:00:00+00:00 List-Id: ncohen@watson.ibm.com (Norman H. Cohen) 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". ;-) :-)) > 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. Yes, the proposal requires voluntary compliance (which can be easily checked if necessary) and it requires programmers to take an explicit action---with'ing base_package.derived---to gain access to base class internals. > 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 we talk about "trusting" (I am not concerned about programmers cluttering up something intentionally; I am primarily aiming at getting a less coupled implementation with my proposal): Don't class libraries, frameworks/patterns often require users to derive classes from library classes? Can it always be foreseen from which classes it might be desirable to derive new classes? The "trusting scheme" would have to trust quite a lot of people, IMO. > (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.") I agree that this is true when derived classes are implemented in child packages. But I must add that it sounds like placing a burden on programmers to me. I think of an application programmer using, say, one class library for the user interface, another for database access, and yet another one for communications. If I had the choice to either protect her against inadvertantly relying on non-public features of one of maybe several hundred classes or trust her, I would prefer the first alternative. > 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. Agreed. This is true no matter how you implement derived classes. > Back to the idiom you propose: The usual reason for a derived class > referring directly to members of a base class is efficiency. You can gain efficiency in my scheme by inlining subprograms. > 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. To cite [Rumbaugh, Object Oriented Modeling & Design, 1991 (p.322)], (hope I got the name right:-), and this the intention behind my proposal: "Encapsulation can be violated through careless use of inheritance. A subclass inherits the attributes of its superclass, but if the methods of the subclass directly access superclass attributes, the encapsulation of the superclass is defeated. CLOS, Owl, Eiffel and C++ permit a class to restrict the visibility from its subclasses. There are arguments in favor of allowing a class to access its superclass, but the cost is tight binding between the classes. Often it is useful to write some "private" operations that are for internal use only by other methods of the same class. It is desirable to restrict the visibility of these operations so that other classes cannot use them. It is often necessary to allow subclasses to access these internal operations, so a simple distinction between public and private is not possible..." This meets exactly my experience in early C++ projects. Classes end up tightly coupled. Modifications/extensions become harder and harder over time. I believe this lesson was learned in C++---I didn't see many C++ class libraries (maybe none?) in the last years. > 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. > Yes, this letter-envelope pattern is usefull in its own right. As you said, its costs can be high because the base class itself can only access the private part with an additional indirection. Thank you for your answer, Norman. Although it didn't convince me to drop my proposal it was interesting to hear your opinions. May I add a congratulation for your really fine book "Ada as a 2nd language"? -- Gerd Moellmann, Altenbergstr. 6, D-40235 Duesseldorf, Germany Software Development & Consulting Internet: mmann@ibm.net / CIS: 100025,3303 / Phone: +49 211 666 881