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.8 required=5.0 tests=BAYES_00,INVALID_DATE, REPLYTO_WITHOUT_TO_CC,T_FILL_THIS_FORM_SHORT autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 1108a1,66253344eaef63db X-Google-Attributes: gid1108a1,public X-Google-Thread: 109fba,66253344eaef63db X-Google-Attributes: gid109fba,public X-Google-Thread: 103376,66253344eaef63db X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1994-09-29 19:21:59 PST Path: bga.com!news.sprintlink.net!howland.reston.ans.net!swiss.ans.net!newsgate.watson.ibm.com!watnews.watson.ibm.com!ncohen From: ncohen@watson.ibm.com (Norman H. Cohen) Newsgroups: comp.lang.ada,comp.object,comp.lang.c++ Subject: Re: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG) Date: 29 Sep 1994 18:37:19 GMT Organization: IBM T.J. Watson Research Center Distribution: world Message-ID: <36f1gv$hln@watnews1.watson.ibm.com> References: <1994Sep27.165203.9192@swlvx2.msd.ray.com> <36bt0c$17oo@watnews1.watson.ibm.com> <1994Sep29.021209.20769@swlvx2.msd.ray.com> Reply-To: ncohen@watson.ibm.com NNTP-Posting-Host: rios8.watson.ibm.com Keywords: Ada 9X, C++, object-oriented Xref: bga.com comp.lang.ada:6319 comp.object:6901 comp.lang.c++:31022 Date: 1994-09-29T18:37:19+00:00 List-Id: In article <1994Sep29.021209.20769@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com (John Volan) writes: |> This solution suffers from the same problem as Mark Biggar's suggestion: Mark's solution and mine have a lot in common. (I should have declared Employee_Parent and Office_Parent to be abstract, as Mark did with the corresponding types in his solution, to indicate my intent that there be no objects of this type.) |> These access types only give clients an "opaque" view of the designated objects. |> The useful primitive operations for these objects won't be declared until we |> get to the concrete *subclasses* declared later. So a client would have to |> resort to using a non-typesafe Unchecked_Conversion to "downcast" one of these |> pointers into designating the corresponding concrete subclass. You are right. This problem can be solved by declaring all the operations of Employee and Office as abstract operations of Employee_Parent and Office_Parent. (So making the parent types abstract isn't just a good idea, it's the law.) The common operations can be handled by adding function Office_Occupied_By (The_Employee : in Employee_Parent'Class) return Office_Pointer; function Employee_Occupying (The_Office : in Office_Parent'Class) return Employee_Pointer; procedure Occupy (The_Office : in access Office_Parent'Class; The_Employee : in access Employee_Parent'Class); to Employees_And_Offices as concrete classwide operations. |> Another issue is whether the concrete subclasses declared below are going to |> be the *only* subclasses of these abstract classes. Certainly that's the |> original intent -- but will a maintainer pay any attention? ;-) I don't see any problem here. If the maintainer can come up with another useful extension to Office_Parent or Employee_Parent, more power to him. Indeed, type inheritance and child packages are useful in part as a "structured" way for a maintenance programmer to tailor the facilities in the original program in a way not anticipated by the original author, while leaving the original author's source intact. The correctness of an OO program should not depend on the absence of subclasses not mentioned in the original program! |> Another problem here is that this doesn't really solve the original puzzle I |> posed: How do you avoid breaking encapsulation *between* these two classes? |> These type declarations are private, but since the packages below are all |> children of this parent package, they have complete visibility to the private |> part of the parent. So an Employee subprogram still has license to ignore |> the public primitives of the Office class and "diddle" with the Office's |> private components directly. (And vice versa.) What you are asking for is impossible. As I noted yesterday, there is no way to define, in two separate packages, two types with primitive operations that operate on the other type, because the package specs are not allowed to be mutually dependent: - If Offices depends on Employees, for example, Employees can not depend on Offices, so there is no way to declare Office_Occupied_By (which must be declared in a place where its body can see the Its_Occupied_Office record component). - If Offices does not depend on Employees, there is no way to declare Employee_Occupying (which must be declared in a place where its body can see the Its_Occupying_Employee record component). But, as the song says, that's what friends are for. The implementations of mutually recursive types are often so tightly intertwined that it is not possible to say where one ends and the other begins; in the case of tightly coupled mutually recursive types there is no point in hiding the implementation of one from the implementation of the other. Tightly coupled mutually recursive types constitute a single abstraction consisting of two or more types, typically designed by one person and implemented by one person even if the abstraction is distributed among several child packages. When the mutually recursive types are tightly coupled, the ability of the subprogram bodies for each type single to reference the full recursive definition is a feature, not a bug. However, types Office and Employee are not so intimately intertwined. Each contains one pointer to the other and much more that has nothing to do with the other type. Here is an approach that exploits the loose coupling between the recursive types to hide only the "other stuff": package Employees_And_Offices is type Employee_Parent is abstract tagged private; type Employee_Pointer is access all Employee_Parent'Class; -- Abstract Employee_Parent subprograms involving that -- "other stuff" type Office_Parent is abstract tagged private; type Office_Pointer is access all Office_Parent'Class; -- Abstract Office_Parent subprograms involving that -- "other stuff" -- Concrete nondispatching subprograms involving only the pointers -- between the classes: function Office_Occupied_By (The_Employee: in Employee_Parent'Class) return Office_Pointer; function Employee_Occupying (The_Office: in Office_Parent'Class) return Employee_Pointer; procedure Occupy (The_Office: in Office_Pointer; The_Employee: in Employee_Pointer); private -- Only those components of Office and Employee that are of mutual -- interest: type Employee_Parent is record Its_Occupied_Office : Office_Pointer; end record; type Office_Parent is record Its_Occupying_Employee : Employee_Pointer; end record; end Employees_And_Offices; package body Employees_And_Offices is function Office_Occupied_By (The_Employee: in Employee_Parent'Class) return Office_Pointer is begin return The_Employee.Its_Occupied_Office; end Office_Occupied_By; function Employee_Occupying (The_Office: in Office_Parent'Class) return Employee_Pointer is begin return The_Office.Its_Occupying_Employee; end Employee_Occupying; procedure Occupy (The_Office: in Office_Pointer; The_Employee: in Employee_Pointer) is begin The_Employee.Its_Occupied_Office := The_Office; The_Office.Its_Occupying_Employee := The_Employee; end Occupy; end Employees_And_Offices; package Employees_And_Offices.Employees is type Employee is new Employee_Parent with private; -- Concrete Employee subprograms involving that -- "other stuff" private type Employee is new Employee_Parent with record -- various components involving that "other stuff" end record; end Employees_And_Offices.Employees; package Employees_And_Offices.Offices is type Office is new Office_Parent with private; -- Concrete Office subprograms involving that -- "other stuff" private type Office is new Office_Parent with record -- various components involving that "other stuff" end record; end Employees_And_Offices.Offices; -- Norman H. Cohen ncohen@watson.ibm.com