Thread View: comp.lang.eiffel
1 messages
1 total messages
Started by bertrand@hub.ucs
Thu, 12 Jan 1989 06:33
Re: multiple inheritance example
Author: bertrand@hub.ucs
Date: Thu, 12 Jan 1989 06:33
Date: Thu, 12 Jan 1989 06:33
205 lines
9129 bytes
9129 bytes
This is a copy of my answer to <2791@hplabsz.HPL.HP.COM> from snyder@hplabsz.HPL.HP.COM (Alan Snyder). Both that article and my answer were mail messages initially, but they should be of interest to readers of this newsgroup. > > I have a question about the example you used in your column on multiple > inheritance in the Nov/Dec issue of JOOP. To refresh your memory, you gave > an example of walking menus, which included a class SUBMENU that inherits > from both MENU and ENTRY. > > It is clear to me why a SUBMENU must be an ENTRY: it must be an ENTRY > because it is an entry in a menu. However, it is not clear to me why a > SUBMENU must also be a MENU. As an alternative, I would consider having a > SUBMENU contain a MENU as an attribute. > > [...] > > What is your reasoning in favor of the multiple inheritance > solution over this alternative? Do you see one alternative as strongly > superior to the other? On what basis do you choose between these > alternatives? The arguments below are not as precise and formal as I would like them to be. We have developed a certain style of using multiple inheritance, that we and other Eiffel users have applied successfully to the development of many lines of code, and I feel quite strongly about it because it works so nicely in practice. Things simply seem to fit well this way. On the other hand I can't really say I have a full-fledged theory to support it. Answering your comments might help improve the theory. > the MENU operations are not needed on the object that lives in > the enclosing menu (the menu entry) and [you] claim that the > two behaviors need not be represented by a single object. It seems to me that a submenu should have the behavior of a menu, or at least part of it. For example it itself contains further subentries. It should have a procedure to add such an entry, remove it so on. Class MENU has procedures ``display'', ``erase'' etc. all of which seem meaningful for submenus. (By the way, some of these were not exported in the submenu class of the current Eiffel Graphics Library, called MENU_ENTRY. I was quite embarrassed when I realized that. However I take this to be simply a mistake which will be corrected in the forthcoming version.) > As an alternative, [you] would consider having a > SUBMENU contain a MENU as an attribute; the execute action > would have the effect of bringing up that contained MENU. > (...) What was [my] reasoning in favor of the multiple > inheritance solution over this alternative? > Do you see one alternative as strongly > superior to the other? On what basis do you choose > between these alternatives? I wish I had perfect answers. My general guideline is clear: I see inheritance as the ``is'' relation and client as ``has''. These are natural language terms and not perfectly rigorous. I am aware in particular that our form of inheritance (where a descendant can hide a feature exported by an ancestor or conversely) may appear to cast some doubts as to what ``is'' really means. However the following points are worth considering. 1. Cases in which inheritance is definitely NOT appropriate are usually pretty obvious. The example from the Trellis-Owl people that I quoted in my column is typical: having PLANE inherit from COCKPIT (or was it HOUSE from DOOR). In general, when ``has'' applies but not ``is'' (and hence inheritance is improper), any doubt is easily removed. 2. The more difficult case is when ``is'' apparently applies but you are not quite sure (as in the submenu case). However it seems to me that in a sense you can always interpret ``is'' as ``has'' and thus do away with inheritance altogether. (Again I am aware of the limitations of using such natural-language-based criteria, but we are in the domain of information modeling here and I don't see how to avoid human interpretation.) If ``every A may also be viewed as a B'' (the basis for applying ``is'', or inheritance), then you can always reinterpret this by saying ``every A has a part of it which is a B''. In other words, A could be rewritten as a client of B. It seems to me that if you take your favorite example of unquestionable inheritance (say polygons and rectangles, or anything else) you can rewrite it in such a way that it uses the client relation instead (i.e. each rectangle will have an attribute that refers to an object describing its polygon features). [If I understand figure 1 in your paper in the Shriver & Wegner volume, page 170, then that's what it means.] Of course you may then lose subtyping: polymorphism, dynamic binding and so on (see point 4). 3. If a submenu is not a menu, then you cannot (in the SUBMENU class) write something like ``entry (i)'' to refer to the i-th entry, but you must write ``associated_menu.entry (i)''. This will be the same for all menu features of submenus. This is only a matter of convenience, but it reinforces the previous point. In a RECTANGLE class, being able to write ``rotate (30)'' rather than ``associated_polygon.rotate (30)'' could also be dismissed as a matter of convenience. 4. This leaves the need to define more precisely what ``is'' means. ``Every operation applicable to instances of A must be applicable to instances of B'' seems to provide the appropriate definition. This means that subtyping and inheritance are merged. This is basically the approach taken in Eiffel and I believe it applies to menus and submenus. 5. However if taken literally the previous definition excludes possibility of a descendant hiding (in its own interface specification) some of the features that were exported by ancestors. There are some arguments in favor of this approach. However it was not retained in Eiffel for various reasons, theoretical and practical. Among the practical ones is that even simple examples seem to require hiding features that ancestors exported. For instance a full POLYGON class will probably have a procedure ``add_vertex''; this should not be exported by RECTANGLE. You could always avoid the problem by complicating the inheritance hierarchies (having a class POLYGON which does not include ``add_vertex'', with EXTENDIBLE_POLYGON and FIXED_POLYGON both being heirs of this class, the former introducing ``add_vertex'', and RECTANGLE inheriting from FIXED_POLYGON). However this seems much too restrictive in practice. This ability not to export previously exported routines means that exceptions are accepted to the general rule that inheritance includes subtyping. What we are trying to do, however, is to avoid being needlessly restrictive here. For example in the above case I would like to avoid rejecting offhand an assignment such as p := r just because one feature exported by POLYGON is not exported by RECTANGLE. The rejection should only occur if the system contains an actual call to such a feature, e.g. p.add_vertex. (Flow analysis should not be taken into consideration.) Although our current compiler does not handle such checks (this is of course very similar to the problems discussed by William Cook in his paper), we are confident that we can fill this hole soon. This leaves a definition of inheritance that is not as clear-cut as the one above (see 4.). ``Every'' may have to be replaced by ``most''. 6. An associated criterion is the following: an instance of B is acceptable in any list (tree, queue, stack etc.) of instances of A. In the example at hand, if you have a list of all menus in your application, it should include submenus as well. 7. I suspect giving submenus an attribute of type MENU would be even more cumbersome than suggested above. In particular we will need to record the information that such menus are special in some way; at the least, we want to associate with them a reference to the entry from which they come in the parent menu, and this parent menu itself. So we need attributes of the form seed_entry: ENTRY parent_menu: MENU Since these do not make sense for general menus, we will need a special class SUBMENU anyway, inheriting from MENU. Now entries which correspond to submenus are also special and (this is the part that you considered as self-evident) should be described by a class, say MENU_ENTRY, which inherits from ENTRY and has the attribute associated_menu: SUBMENU Furthermore some routines of MENU will need to be redefined for SUBMENU. For example you may want to display submenus in a special way (shifted both vertically and horizontally from the parent menu), so procedure ``display'' (as well as ``erase'' etc.) will be redefined in SUBMENU. But there is a close correspondence between instances of SUBMENU and MENU_ENTRY; apparently a one-to-one correspondence. So we need invariants of the form in class MENU_ENTRY not associated_menu.Void and then associated_menu.seed_entry = Current in class SUBMENU not seed_entry.Void and then seed_entry.associated_menu = Current This brings in a considerable amount of complication which appears totally unwarranted. All this simply vanishes if you accept that a ``menu entry'', or submenu, is both a menu and an entry. -- Bertrand Meyer bertrand@eiffel.com
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads