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: fac41,c52c30d32b866eae X-Google-Attributes: gidfac41,public X-Google-Thread: 1108a1,c52c30d32b866eae X-Google-Attributes: gid1108a1,public X-Google-Thread: 103376,2ea02452876a15e1 X-Google-Attributes: gid103376,public From: ncohen@watson.ibm.com (Norman H. Cohen) Subject: Re: Real OO Date: 1996/03/18 Message-ID: <4ik3j7$cl6@watnews1.watson.ibm.com> X-Deja-AN: 143043176 distribution: world references: <4id031$cf9@dayuc.dayton.saic.com> organization: IBM T.J. Watson Research Center reply-to: ncohen@watson.ibm.com newsgroups: comp.lang.ada,comp.lang.eiffel,comp.object Date: 1996-03-18T00:00:00+00:00 List-Id: In article <4id031$cf9@dayuc.dayton.saic.com>, John G. Volan writes (concerning the division of a tagged type's interfaces into multiple subinterfaces for different clients): |> Here's an alternative approach that is subtly -- but significantly -- |> different: Make the subprograms in the child packages _classwide_ |> operations, and have them _dispatch_ to the private primitives: |> |> package Abstraction_1 is |> type T is tagged null record; |> private |> procedure Op_1 (X: in out T); -- for Specialty_1 |> procedure Op_2 (X: in out T); -- for Specialty_2 |> end Abstraction_1; |> |> |> package Abstraction_1.Specialty_1 is |> procedure Op_1 (X: in out T'Class); -- classwide operation |> pragma Inline (Op_1); |> end Abstraction_1.Specialty_1; |> |> package body Abstraction_1.Specialty_1 is |> procedure Op_1 (X: in out T'Class) is |> begin |> Abstraction_1.Op_1 (X); -- dispatching call |> end Op_1; |> end Abstraction_1.Specialty_1; ... |> package Abstraction_1.Specialty_2 is |> procedure Op_2 (X: in out T'Class); -- classwide operation |> pragma Inline (Op_2); |> end Abstraction_1.Specialty_2; |> |> package body Abstraction_1.Specialty_2 is |> procedure Op_2 (X: in out T'Class) is |> begin |> Abstraction_1.Op_2 (X); -- dispatching call |> end Op_2; |> end Abstraction_1.Specialty_2; Yes, this is an excellent paradigm! Extending T means that there is more than one kind of T, but that the two subinterfaces are designed to perform particular functions with any kind of T that comes along. The fact that there is more than one kind of T is irrelevant to the users of these packages, as long as Op_1 or Op_2 does the appropriate thing for any kind of T that is encountered. Defining "the right thing" is the responsibility of the programmer extending T. ... |> (In fact, I'd say in general that any operation ought to be classwide if |> it isn't explicitly a primitive. IMHO, only under very rare |> circumstances should a non-primitive operation restrict a parameter to |> accept only a specific root type but not any of its derived types.) By instinct, I'm wary of such sweeping generalizations. However, it would appear that John's approach is the appropriate one in many circumstances. ... |> (Well, this whole discussion might be a lot clearer if we had a more |> concrete example that exhibited this kind of pattern. Ideas anyone?) My canonical example of an abstraction that has different interfaces for different clients is a device driver, or more precisely, an abstract device state. There are certain operations meant to be invoked from interrupt handlers as the result of events in a device; there are other operations meant to be invoked from application programs using the device. One can define an abstract tagged type for device states, together with dispatching operations, in a parent package. Separate child packages, one providing an interface for interrupt handlers and one providing an interface for applications, can define classwide versions of those operations appropriate for each interface. These classwide versions simply invoke the corresponding dispatching operation of the abstract tagged type. For each kind of phyiscal device, one derives from the abstract type, overriding all its operations. Given an object corresponding to a particular kind of physical device, a client (either an interrupt handler or an application) continues to simply invoke the corresponding classwide operation, which dutifully makes a dispatching call that dispatches to the overriding subprogram defined for the appropriate derived type. This isolates clients from the details of physical devices and makes it unnecessary to modify clients when new physical devices arise. (Of course a new physical device may make a new kind of abstract operation possible. This will be defined as a primitive operation of the corresponding derived type. Children of the packages providing interrupt-handler and client interfaces can provide corresponding classwide operations for use by clients.* Thus application clients CAN be modified to take advantage of the special characteristics of a new physical device, as a result of a deliberate decision to upgrade the application. However, this is not NECESSARY: A client that was working before the new physical device was added will continue working, so the client can be upgraded at our leisure or not at all.) *-To avoid the need for clients to become aware of whether a given abstract device supports the new operation, the classwide operation should accept a parameter of the classwide type of the original root abstract type and check whether the object belongs to a class for which dispatching is possible. If not, the classwide type should provide an appropriate response--perhaps an expensive emulation of the new operation using the old operations common to all devices. -- Norman H. Cohen ncohen@watson.ibm.com