Adaptation by Inheritance Technique |

[Note that the examples used below to illustrate this adaptation technique may be out of date and not relevant anymore for recent versions of the supported Eiffel compilers.]

NICE (Nonprofit International Consortium for Eiffel) has provided us with a kernel library standard that we can rely on for interoperability between Eiffel compilers. This standard is made up of a set of classes, such as `ANY`, `INTEGER` or `STRING`, and a set of features that all Eiffel compilers should implement. However, some compilers fail to support some these classes or features, making it difficult to write portable Eiffel code. Moreover, some useful features are also missing in the standard. Some compilers may provide implementation for these features, but some don't.

## Description

One way to get around this problem without having to directly edit the library classes is to put a layer of classes between the kernel library and the application classes. In that layer, there will be a different cluster for each supported Eiffel compiler. Each such cluster will contain the same set of classes, each of these classes adapting a corresponding kernel class through inheritance. For example, ELKS '95 states that class `PLATFORM` among other things should have the two following features:

>     Minimum_character_code: INTEGER
>             -- Smallest supported code for CHARACTER values
>         ensure
>             meaningful: Result <= 0

>     Maximum_character_code: INTEGER
>             -- Largest supported code for CHARACTER values
>         ensure
>             meaningful: Result >= 127

However these two features were missing in class `PLATFORM` provided with ISE Eiffel 4.2 and Halstenbach 2.0. The solution to this problem is to use class `KL_PLATFORM` in the client code, where `KL_PLATFORM` had the following form in the clusters associated with ISE Eiffel and Halstenbach:

>     class KL_PLATFORM

>     inherit

>         PLATFORM

>     feature -- Access

>         Minimum_character_code: INTEGER = 0
>         Maximum_character_code: INTEGER = 255
>                 -- Smallest and largest supported codes for CHARACTER values

>     end

and the following form in all other clusters:

>     class KL_PLATFORM

>     inherit

>         PLATFORM

>     end

That way, a call to `Minimum_character_code` from a client of class `KL_PLATFORM` will always work, regardless of the Eiffel compiler used. This wouldn't be true for clients of `PLATFORM`.

This technique can also be used to supply missing classes. For example the class `EXCEPTIONS` was not provided in SmallEiffel -0.81. However this class is useful when a system has to be terminated using feature `die`. This routine could easily be implemented using SmallEiffel's built-in `die_with_code` feature. SmallEiffel kernel adaptation cluster hence ended up with the following class:

>     class KL_EXCEPTIONS

>     feature -- Basic operations

>         die (a_code: INTEGER)
>                 -- Terminate execution with exit status a_code
>                 -- without triggering an exception.
>             do
>                 die_with_code (a_code)
>             end

>     end

whereas clusters for other compilers already supporting this facility contained this simple class:

>     class KL_EXCEPTIONS

>     inherit

>         EXCEPTIONS

>     end

Sometimes, the required functionality will be provided but under another feature name or with an inefficient implementation. Once again one can in these situations inherit from the corresponding classes and rename or redefine the proper features. This is for example the case in class `EXCEPTIONS` from Halstenbach 2.0 where feature `die` was obsolete and feature `new_die` had to be used instead:

>     class KL_EXCEPTIONS

>     inherit

>         EXCEPTIONS
>             rename
>                 die as old_die,
>                 new_die as die
>             end

>     end

Finally, some useful features are missing from the standard. For example class `ARRAY` is not equipped with a feature `clear_all` which would reset all items in the array to their default values. Some compilers already provide an efficient implementation of this routine, but some don't. When it is not already provided, it is easy to add this routine to the corresponding class `KL_ARRAY`.

## Caveats

Although the technique described above seems attractive, it still has some drawbacks. One of these disadvantages is when using manifest constants. For the adaptation by inheritance solution to work properly, the application code should now always use the `KL_` classes instead of the kernel classes they inherit from. But the following code:

>     my_string: KL_STRING
>     my_array: KL_ARRAY [INTEGER]
>     ...
>     my_string := "Hello World!"
>     my_array := <<1, 2, 3>>

will not compile because `"hello World!"` is a `STRING` which does not conform to `KL_STRING`, and `<<1,2,3>>` is an `ARRAY` which does not conform to `KL_ARRAY` (although `KL_STRING` conforms to `STRING` and `KL_ARRAY` conforms to `ARRAY`). To solve this problem, one would then have to write:

>     create my_string.make_from_string ("Hello World!")
>     create my_array.make_from_array (<<1, 2, 3>>)

which is cumbersome and inefficient with some compilers.

Another problem encountered with this technique is that most Eiffel compilers do not support some of their kernel classes to be adapted using inheritance. This is for example the case for basic types such as `INTEGER` or `CHARACTER`, and also for some other classes such `ARRAY` or `STRING` where some built-in features hard-coded for optimization purposes cannot even be renamed in descendant classes without breaking the run-time system (try to rename feature `count` from class `ARRAY` in a descendant class with SmallEiffel to convince yourself).

Finally, the kernel library standard specifies that compatible implementations may provide more features than those required. This implies that using these classes by inheritance, as the technique described here does, the extra features will pollute the name space and might cause name clashes dependent on the Eiffel compiler used.

Another adaptation technique, using the client/supplier relationship, gets around these problems.