Xref: utzoo comp.lang.eiffel:1346 comp.object:2469 Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!sdd.hp.com!think.com!barmar From: barmar@think.com (Barry Margolin) Newsgroups: comp.lang.eiffel,comp.object Subject: Re: Inheritance and Information Hiding Message-ID: <1991Jan31.185534.24530@Think.COM> Date: 31 Jan 91 18:55:34 GMT References: <10612@pasteur.Berkeley.EDU> <485@eiffel.UUCP> <488@eiffel.UUCP> Sender: news@Think.COM Organization: Thinking Machines Corporation, Cambridge MA, USA Lines: 68 In article <488@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes: >4. Accept that RECTANGLE inherits from POLYGON and does not export >``add_vertex''. Permit polymorphic assignments of the above form; >but have the type checker reject as invalid any system which would >contain both such an assignment and a call p.add_vertex (...), since >it may lead to a run-time inconsistency. Do not introduce >any distinction between ``implementation inheritance'' and any other >kind. > >Based on not inconsiderable experience with the design of O-O systems >and class hierarchies, I believe that solution 4 is the only reasonable >one. I would be interested to see any technical counter-argument, >and alternative solutions to the problem mentioned above. If static type checking is a requirement, I believe you are right. However, in OO systems with dynamic type checking and method dispatch, there is another solution: 5. Require that add_vertex (or, generally, all methods) be a virtual function (in languages that require such a distinction to be made). Accept that RECTANGLE inherits from POLYGON. Override add_vertex in the RECTANGLE class with a function that reports an error. Permit polymorphic assignments of the form p=r, and accept the fact that it is possible for p.add_vertex(...) to signal an error at runtime. This is not a realistic solution in a language without a decent exception handling mechanism, but that's an excuse for adding exception handling, not for crippling programs. Furthermore, some languages (e.g. CLOS) even allow the class of an object to change dynamically, so there's another solution: 6. As in 5, use virtual function inheritance of add_vertex. RECTANGLE::add_vertex would change the class of self to POLYGON and then call self.add_vertex (which will invoke the POLYGON::add_method, which might further change the class to PENTAGON). In fact, schemes like this may be the only sensible way to implement a system of this type. Consider a context in which the above classes would be used: a graphical drawing program. The user clicks on an object and then selects an operation from a menu (or vice versa). Solution 6 would be useful if you want to allow users to use the rectangle tool to create an object, but later permit them to edit it in such a way that it is no longer a rectangle. Solution 5 is useful if the UI design specifies that objects created using the rectangle tool must always stay rectangular; when the user tries to add a vertex to the object, an error message would be put up telling them that the operation is not appropriate for that object. The logic necessary to put up this warning must exist in the program somewhere, so why not embed it directly in the methods that need it? With solution 4, the code to select an object could not use polymorphic assignment, because eventually the object will be passed to the add_vertex function; it would probably have to use a discriminated union, and all future uses of that union would have to dispatch on the discriminant (isn't this what OO programming was supposed to relieve us from?). If the menu system supports "greying out" inappropriate menu items, this could be done by having a virtual list_allowed_operations or is_operation_allowed method. Either way, I think it would be very difficult to structure the program in such a way that a static type checker could verify that add_vertex is only called on POLYGONs that aren't specialized to a particular number of vertices, unless is_operation_allowed were made a primitive of the language (actually, it's a good idea to have such a primitive, because it would be easy for user-defined functions like this to get out of sync with the actual code). -- Barry Margolin, Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar