Xref: utzoo comp.lang.eiffel:1377 comp.object:2514 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sun-barr!olivea!uunet!bellcore!att!pacbell.com!ucsd!hub.ucsb.edu!eiffel!bertrand From: bertrand@eiffel.UUCP (Bertrand Meyer) Newsgroups: comp.lang.eiffel,comp.object Subject: Re: Inheritance and Information Hiding Message-ID: <494@eiffel.UUCP> Date: 6 Feb 91 17:47:06 GMT References: <488@eiffel.UUCP> <27A86309.281A@tct.uucp> <1991Feb5.130359.9735@bellcore.bellcore.com> Organization: Interactive Software Engineering, Santa Barbara CA Lines: 108 From <1991Feb5.130359.9735@bellcore.bellcore.com> by caseau@maya.bellcore.com: > 1. This is a well-known and common problem. In the type theory and object-oriented > database community, the example is a class point with an instance variable x, > and the subclass is the set of point for which x is between 0 and 10. So if we > change the instance variable, the "class" to which the object belongs must > change, or an error must be detected. > It may be a well-known and common problem but I don't remember reading anything really enlightening in the literature about the fields mentioned by Yves Caseau. Here is a way to model Mr. Caseau's example. There is a class A class A export x, set_x, ... feature x: INTEGER; set_x (new_value: INTEGER) is do x := new_value end invariant -- Nothing relevant end and an heir B: class B export x, ... feature ... invariant 0 <= x; x <= 10 end B is fine since its invariant is stronger than the invariant of the parent class A. There is a a problem with procedure set_x, however: the basic rule of class correctness states (in particular) that every exported routine should preserve the invariant. (See chapter 7 of OOSC and the article ``Programming as Contracting''.) Since set_x does not necessarily preserve the invariant, ergo - it cannot be exported in B. So we are back to the situation discussed in previous postings, and the need to allow a class to hide features exported by its parents. It is not possible to redefine set_x in B to be more demanding, as in class B export x, ... feature ... set_x (new_value: INTEGER) is require 0 <= new_value; new_value <= 10 do x := new_value end invariant 0 <= x; x <= 10 end This does not work, since the new precondition would be stronger (again, see above references). It would make it impossible for a client to guarantee that a call will be correct through a scheme of the form [X] b1: B; ... if some_consistency_check (my_value) then b1.set_x (my_value) else ... Other action ... end There is a conceptual trick, however, that often works. Make the precondition abstract by introducing an exported function acceptable_value in A: acceptable_value (n: INTEGER): BOOLEAN is do Result := true end and redefine it in B (keeping it exported): acceptable_value (n: INTEGER): BOOLEAN is do Results := (0 <= x) and (x <= 10) end Then you can indeed export set_x in B because clients can use scheme [X] safely under the following variant: [Y] if acceptable_value (my_value) then b1.set_x (my_value) else ... Other action ... end The precondition of set_x does not change in B. (Of course the concrete precondition changes since B redefines function acceptable_value; and it may even appear to have been strengthened, violating the contracting rules as before. But this is in fact not true. What counts is the abstract precondition, as seen from the oustside, by clients; and that one has remained the same, as expressed by the property lambda n . acceptable_value (n).) This technique is used quite frequently in the Basic Eiffel Library. -- Bertrand Meyer bertrand@eiffel.com