day@grand.UUCP (Dave Yost) (12/22/88)
From: Jos Warmer <jos@cs.vu.nl> Date: Mon, 21 Nov 88 10:24:59 MET Having read and enjoyed the book 'Oject Oriented Software Construction' of Bertrand Meyer, I have some questions/remarks about (multiple) inheritance and safety. At page 241 of Bertand Meyer's book an alternate definition is given for STACK2 (page 118): the class FIXED_STACK. It is declared an heir of class ARRAY, instead of a client. As far as I can see this definition of FIXED_STACK has a serious safety-leak, as opposed to the definition of STACK2 at page 118. Consider the following piece of (pseudo) eiffel code: a : ARRAY[INTEGER]; -- declare an entity of type ARRAY f : FIXED_STACK[INTEGER]; -- declare an entity of type FIXED_STACK f.Create; -- created a fixed stack f.push(12); -- top of stack is now 12 a := f; -- allowed, because FIXED_STACK is descendant of ARRAY a.enter(1, 20); -- enter is allowed on ARRAY's value := f.top: -- this will deliver value 20, instead of the previously -- entered 12. This example shows that any client of FIXED_STACK can manipulate its implementation. This is clearly not what is intended for the class FIXED_STACK. It doesn't behave like a stack anymore. The FIXED_STACK class has no way to enforce its own semantics. This is dangerous. In this case the rule at page 333 should be applied: 'inheritance means "is", client means "has".' A FIXED_STACK has an array as it's implementation. It ** is not ** an ARRAY. In the discussion at page 333 the safety-aspect of the solution for FIXED_STACK is not considered. The disadvantage clearly outperforms two of the advantages mentioned: The advantage of having a simpler way of accessing the array features is superficial. The reason that inheritance is more efficient is not to the point. Whether inheritance or buying should be used must only depend on design-criteria, not on efficiency. This attitude is correctly expressed at page 344: "An object-oriented environment in which designers would have to think twice before adding an inheritance level because it would mean degraded performance would be rather unpleasant." It is not clear whether the flexibility obtained by inheritance is an advantage or not. Also, the book advocates safe programming by the use of pre- and post- conditions and assertions. When you think this way, the FIXED_STACK solution should be considered bad. Maybe another rule can be formulated, in choosing between inheritance or buying: "If a class B needs class A, then B should not be an heir of A if any operation on A corrupts the intended semantics of B. In these cases B should be a client." This rule can be seen as an extension of the "is-a" versus "has" rule. Any comments, or different opinions? Jos Warmer jos@cs.vu.nl (via mcvax)
day@grand.UUCP (Dave Yost) (12/22/88)
From: Jan Prins <prins@cs.unc.edu> Date: Mon, 21 Nov 88 17:01:47 est In-Reply-To: Jos Warmer's message of Mon, 21 Nov 88 09:18:11 PST <8811211718.AA26128@grand.COM> Like Jos, I too have recently become acquainted with Eiffel, but I had not thought of the subtle ADT implementation-visibility defect he described. Although I agree with Jos that FIXED_STACK might, in this case, be better thought of as a "client" of ARRAY rather than a "descendant", the general problem of unauthorized access to ancestral features of a class remains. Jos proposes a rule that would serve to prevent inheritance in cases where the exposed parent-class features could be abused: "If a class B needs class A, then B should not be an heir of A if any operation on A corrupts the intended semantics of B. In these cases B should be a client." I would imagine that this rule would effectively prevent most inheritance in ADT's. Apart from deferred classes that impart some common structure on descendants, most inheritance brings something into the class to be used in the implementation of the class -- something subject to manipulation via Jos' scheme. My opinion is that this problem should be solved with declarations and the type system. Why make rules to recognize an apparent defect (don't inherit here -- it's not safe!) rather than fixing it? For example, one might require that a direct ancestor of a class must be explicitly exported from a class, if it is desirable that others be able to access the ancestor. Or that the "inherit" clause be partitioned into a "private" and "public" portion. Thus class FIXED_STACK would make ancestor STACK (a deferred class that defines a few functions common to all representations of stacks) visible, but not ancestor class ARRAY. With such a scheme and declarations a : ARRAY[INTEGER]; -- declare an entity of type ARRAY f : FIXED_STACK[INTEGER]; -- declare an entity of type FIXED_STACK the type system refuses the assignment a := f; since the class of "f" is not a publicly-acknowledged descendant of the class of "a". Consequently manipulation of "f"'s representation via Jos' device is thwarted. I suppose that inheritance-structure visibility declarations would be transitive. If class C publically inherits B and B publically inherits A, then, as far as the type system is concerned, features of class A are polymorphic with respect to instances of classes A, B and C. But before I go any further astray, it would be interesting to hear from others whether Jos' observation is really perceived to be a problem. Jan Prins Dept. of Computer Science UNC - Chapel Hill prins@cs.unc.edu
day@grand.UUCP (Dave Yost) (12/22/88)
From: Dave Robbins <drobbins@gte.com> Date: Tue, 22 Nov 88 08:58:31 EST I think that the "defect" described by Jos is something that illustrates the difference between inheritance of "specification" and inheritance of "implementation." The two are distinctly different concepts. 1) Inheritance of specification is inheritance of behavior, or protocol. A class inherits behavior from another class when it intends to behave the same way, and in such cases it is clearly expected that the subclass will respond appropriately to all the same operations to which the superclass responds. 2) Inheritance of implementation is no more and no less than a way to implement a class by sharing its implementation with that of another class. A class that inherits for implementation purposes (such as FIXED_STACK in Jos' message) has absolutely no intention of behaving in all respects like the class from which it inherits. It is easy to get the two concepts confused, especially when dealing with languages that make no distinction between the two concepts of inheritance. Off the top of my head, I don't recall whether or not Meyer in his book draws such a distinction. I do think that Eiffel is somewhat schizoid about this: some of its rules (e.g., those for pre- and post-conditions on redefined routines) are rather clearly intended to preserve behavior in subclasses, thus supporting a specification-inheritance view, while other rules (e.g., permitting a subclass to not export a feature exported in the superclass) seem more in line with an implementation-inheritance view. The problem Jos points out is that FIXED_STACK intends to inherit the implementation *but not the specification* of ARRAY. In the class of object-oriented languages exemplified by Smalltalk, there is no formal notion of specification at all -- inheritance is always implementation inheritance. In the class of object-oriented languages exemplified by Eiffel, inheritance leans toward that of specification, but retains implementation inheritance as well. Trellis/Owl is the one object-oriented language that I am familiar with that treats inheritance strictly as inheritance of specification. It is my opinion that the distinction between the two forms of inheritance is very important, and that languages such as Eiffel must come to grips with that distinction. Eiffel has not, at this point, dealt effectively with that distinction. Both forms of inheritance are desirable, but the two must not be inextricably intertwined. A language must permit inheritance of specification when that is desired, and inheritance of implementation *but not specification* when that is desired. Jan's suggestion of "private" inheritance may have some merit. I recall reading at least one or two papers in OOPSLA '87 that address the two concepts of inheritance. Enough for now. Dave Robbins GTE Laboratories Incorporated drobbins@gte.com 40 Sylvan Rd. ...!harvard!bunny!drobbins Waltham, MA 02254