Xref: utzoo comp.lang.eiffel:1360 comp.object:2483 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sun-barr!ames!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: <492@eiffel.UUCP> Date: 1 Feb 91 18:26:49 GMT References: <10612@pasteur.Berkeley.EDU> <485@eiffel.UUCP> <488@eiffel.UUCP> <27A86309.281A@tct.uucp> Organization: Interactive Software Engineering, Santa Barbara CA Lines: 75 From <27A86309.281A@tct.uucp> by chip@tct.uucp (Chip Salzenberg): > >A class POLYGON exists and has a procedure > >``add_vertex'', which it exports. You want > >to add a class RECTANGLE. What do you do? > > In C++, the add_vertex() member function would almost certainly be > virtual; that is, it would act correctly in cases where a |POLYGON*| > actually points to an object of type |RECTANGLE|. (If it were not > virtual already, it could be made so by the insertion of the word > "virtual" at one place in the code.) [As an aside to clarify any misunderstanding: in Eiffel all routines are by default dynamically bound (``virtual''). Applying static binding when appropriate is the compiler's job, not the programmer's.] > Given this language feature, add_vertex becomes a non-problem. The > implementor of RECTANGLE simply provides an appropriate definition of > the RECTANGLE::add_vertex(): a do-nothing function body that returns a > failure indication (if appropriate). A non-problem? For a routine which is supposed to add a vertex, to do nothing at all is a non-problem? The postcondition for add_vertex says ensure number_of_vertices = old number_of_vertices + 1 This is the contract that any implementation must satisfy - or else (more on the ``or else'' below). Doing nothing is a gross violation of this contract. Of course Mr. Salzenberg's solution is not just to ``do nothing'' but to return a ``failure indication''. Returning a failure indication is not that trivial (especially if we are talking about a function that already has a result), but it can be done (if only by adding a global variable), so let's accept it. But then we have changed the contract! The postcondition is now ensure failure_indication or else (number_of_vertices = old number_of_vertices + 1) which means that the specification of the ORIGINAL ``add_vertex'' routine has now changed! The postcondition has become weaker, which is the reverse of what is required if existing clients are to be kept correct. As a result, every piece of code ever written that included a call to the old ``add_vertex'' must be updated - just because someone added the notion or rectangle somewhere. Is that object-oriented design? Is that the best one can do in C++? To be sure, a more acceptable of Mr. Salzenberg's scheme is possible. Instead of a ``do-nothing function body that returns a failure indication'', one may implement ``add_vertex'' in RECTANGLE so that it raises an exception - if the language supports exceptions, of course. This can readily be done in Eiffel; in fact, the theoretical and practical role of an exception is precisely to signal that a routine is unable to fulfil its contract. This could have been solution number 5 in the original list. With this approach, there is no need to change existing client software. But the effect is essentially to renounce static type checking for run-time checks, an unpleasant perspective. All this confirms that, as Rick Jones recently commented here, one cannot pursue this discussion properly without some reference to the specification of software elements. Assertions serve that purpose. Without assertions, you lose any perspective of the reason why a certain piece of code does what it does, and for problems such as the one discussed here there is the ever present risk of falling into mere hacking. -- Bertrand Meyer bertrand@eiffel.com