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.7 required=5.0 tests=BAYES_00,INVALID_DATE, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,c83a22003c320b45 X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1994-11-14 16:33:59 PST Path: nntp.gmd.de!xlink.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 Subject: Re: Initialization Params for Controlled Types Date: 14 Nov 1994 21:19:30 GMT Organization: IBM T.J. Watson Research Center Distribution: world Message-ID: <3a8k92$10rk@watnews1.watson.ibm.com> References: <1994Nov4.134412.10010@unix.brighton.ac.uk> <39ebsa$129i@watnews1.watson.ibm.com> <1994Nov10.152352.27015@unix.brighton.ac.uk> Reply-To: ncohen@watson.ibm.com NNTP-Posting-Host: rios8.watson.ibm.com Date: 1994-11-14T21:19:30+00:00 List-Id: In article <1994Nov10.152352.27015@unix.brighton.ac.uk>, je@unix.brighton.ac.uk (John English) writes: |> Norman H. Cohen (ncohen@watson.ibm.com) wrote: ... |> : You can look at this as a problem with the operation Open or a problem |> : with the hierarchy. If you view it as a problem with the hierarchy, a |> : solution is to introduce a new abstract class, say Universal_Account, |> : that declares the operations that truly are meaningful for ALL bank |> : accounts, and derive BankAccount and CurrentAccount from |> : Universal_Account. Open is not one of these operations. Two distinct |> : versions of Open would be declared for the two derived types. |> |> Changing the hierarchy only postpones the problem: Open will end up as |> a primitive of CurrentAccount, and any derivation from CurrentAccount |> will therefore inherit it. Open is indeed not one of those universally |> sensible operations, so it needs to be provided as a non-primitive, and |> it needs to be provided anew for every derivation. And each new version |> will have to call its parent's version of Open to do the parent-part |> specific initialisation. This is where it gets messy and error-prone. This is a more general problem that arises with OOP in any language: Often the need arises to define a new class B that has MOST, BUT NOT ALL of the features of some other class A. The problem is that the author of A, not having been omnisicient, failed to recognize it as a special case of a more general problem for which other special cases would arise in the future. If you don't have the authority to modify the definition of class A, you end up duplicating a lot of code. If you do have such authority, you can FACTOR the common features of A and B into a new class C, redefine A as a specialization of C, and define B as another specialization of C. In your example, CurrentAccount is A and the unanticipated derivation from CurrentAccount that you hypothesize is B. (Had the derivation been anticipated, an abstract class playing the role of C--having all the features of CurrentAccount meaningful even for specializations of current accounts, but not features such as Open--would have been written in the first place, and a concrete class would have been defined by deriving from this abstract class and adding an appropriate Open operation. |> And of course, there is no way in 9X to force Open to be called and you |> have to take extra care (e.g. an Initialised flag) to prevent it being |> called more than once. C++ constructors are a neat solution to these |> problems -- they are always called exactly once per object, they are |> not inherited, they automatically call the base class constructor, and |> they can have parameters. Ada 9X requires a lot more effort to avoid |> subtle errors IMHO. I'm confused now about whether you're envisioning Open as a function or a procedure. (It's been a while and I don't have the full example at hand.) As others have pointed out, a function with a classwide result subtype, function Open_Current_Account (...) return Account'Class; will not be inherited. However, calls on functions are not intrinsically associated with objects. Your remark about exactly one call on open per object sounds like an initialization PROCEDURE. This can be provided by controlled types. Like C++ constructors, the Initialize procedure of a controlled type is called once per object, and can be parameterized using discriminants. By default, a C++ constructor is not inherited, but a default constructor that implicitly calls the base-class constructor is implicitly created, which amounts to pretty much the same thing. In Ada 9X the parent's Initialize procedure is not implicitly invoked by the derived type's Initialize procedure, but it is straightforward to make an explicit call. -- Norman H. Cohen ncohen@watson.ibm.com