The subpattern mechanism combined with the possibility of redefining/extending virtual procedures is widely recognized as a major benefit of object-oriented languages. This mechanism is often called inheritance since a subpattern is said to inherit properties (code) from its superpattern. Inheritance makes it easy to define new patterns from other patterns. In practice this has implied that subpatterns are often used for sheer inheritance of code without any concern for the relation between a pattern and its subpatterns in terms of generalization/specialization. The use of multiple inheritance is in most cases justified in inheritance of code and may lead to complicated inheritance structures.
In BETA subpatterns are intended for representing classification and inheritance of code is a (useful) side effect. In BETA it is not possible to define a pattern with multiple superpatterns corresponding to multiple inheritance. There are indeed cases where it is useful to represent classification hierarchies that are not tree structured. However, a technical solution that justifies the extra complexity has not yet been found.
BETA does support multiple inheritance, but in the form of inheritance from part-objects. A compound object inherits from its parts as well as its superpattern. The reason that this has not been more widely explored/accepted is that in most languages inheritance from part-objects lacks the possibility of redefining/extending virtual procedures in the same way as for inheritance from superpatterns. Block-structure and singular objects make this possible in BETA.
Assume that we have a set of patterns for handling addresses. An address has properties such as street name, street number, city, etc., and a virtual procedure for printing the address. In addition we have a pattern defining an address register.
address: (# streetName: @text; streetNo: @integer; city: @text; print:< (# do inner; streetName->puttext; streetNo->putint; (*etc.*) #); #); addressRegister: register(# element::< address #)
We may use the address pattern for defining part-objects of employee/company objects:
employee: (# name: @text; {the name of the employee*) adr: @address(# print:: (# do name->puttext #)#) #); company: (# name: @text; (*the name of the company*) adr: @address(# print:: (# do name->puttext #) #) #);
The object adr of employee is defined as a singular address object where the virtual print pattern is defined to print the name of the employee. As can be seen it is possible to define a part-object and define its virtual procedures to have an effect on the whole object. The company pattern is defined in a similar way.
It is possible to handle the address aspect of employees and companies. An example is an address register:
AReg: @addressRegister; employee1.adr[]->AReg.insert; employee2.adr[]->AReg.insert; company1.adr[]->AReg.insert; company2.adr[]->AReg.insert; AReg.scan(# do current.print #)
The AReg register will contain address objects which are part of either employee objects or company objects. For the purpose of the register this does not matter. When the print procedure of one of these address objects is invoked it will call the print procedure associated with either employee or company. The scanning of the AReg register is an example of invoking the print pattern.
The example shows that in BETA inheritance from part-objects may be used as an alternative to inheritance from superpatterns. The choice in a given situation depends of course on the actual concepts and phenomena to be modeled. In the above example it seems reasonable to model the address as a part instead of defining employee and company as specializations of address.
In general it is possible to specify multiple inheritance from part-objects since it is possible to have several part-objects like the address object above. This form of multiple inheritance provides most of the functionality of multiple inheritance from C++ and Eiffel. It is simpler since the programmer must be explicit about the combination of virtual operations. It does, however, not handle so-called overlapping superclasses. The programmer must also explicitly redefine the attributes of the component classes. This may be tedious if there is a large number of attributes. However, a renaming mechanism for making this easier has been proposed for BETA, but it is not yet implemented in the Mjølner System. Multiple inheritance from part-objects should be used when there is a part-of relationship between the components and the compound. This also covers situations where implementations are inherited. It should not be used as a replacement for multiple specialization hierarchies.
A common example of using multiple inheritance is modeling windows with titles and borders. This may be modeled using block-structure. Since a window may have a title, a border or both, the following class hierarchy using multiple inheritance is often used:
In BETA this can be described using nested patterns:
window: (# title: (# #); border: (# #); #); aWindow: @window(# T: @title; B: @border #)
The descriptions for title and border are made using nested patterns. For a given window, like aWindow, a title object and a border object may be instantiated. If e.g. two titles are needed, two instances of title are made. This example illustrates another situation where multiple inheritance may be avoided.
BETA Language Introduction | © 1994-2004 Mjølner Informatics |
[Modified: Saturday October 21st 2000 at 18:34]
|