Client/Supplier Adaptation 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.]

Although it is the most object-oriented way to adapt existing classes, the inheritance mechanism sometimes fails to fulfill its job when dealing with kernel classes. This is typically the case for basic types such as `INTEGER` or `CHARACTER`, or optimized classes such as `ARRAY` or `STRING`. To get around this problem, another technique using client/supplier relationship can be adopted.

## Description

This technique, which admittedly doesn't look nice in a pure object-oriented environment, consists in writing features which need to be adapted in a client class instead of in a descendant class. For example in ELKS '95 there is no way to get a `CHARACTER` knowing its ASCII code. What is needed is an extra routine in class `INTEGER`:

>     to_character: CHARACTER
>             -- Character whose ASCII code is Current
>         require
>             large_enough: Current >= Minimum_character_code
>             small_enough: Current <= Maximum_character_code
>         ensure
>             valid_character_code: Result.code = Current

Some Eiffel compilers already support this routine in class `INTEGER`. The other compilers provide other means to get the same result. But class `INTEGER` is half built-in in most compilers, so adapting this class through inheritance would result in run-time misbehaviors. The solution adopted here is to introduce a facility class `KL_INTEGER_ROUTINES` containing the following routine:

>     to_character (an_integer: INTEGER): CHARACTER
>             -- Character whose ASCII code is `an_integer'
>         require
>             an_integer_large_enough: an_integer >= Minimum_character_code
>             an_integer_small_enough: an_integer <= Maximum_character_code
>         ensure
>             valid_character_code: Result.code = an_integer

and other such adapted routines to be applied to `INTEGER`s. As with the inheritance technique, there will be a different cluster for each supported Eiffel compiler, and each such cluster will have the same set of classes. These classes are facility classes, such as `KL_INTEGER_ROUTINES`, associated with a given kernel class. They contain a set of routines whose first argument is the object that routine should be applied to.

There are two typical ways to use the classes described above. They can be used as mixin classes. This means that a given class will inherit from one of these classes to have access to its features, as shown below:

>     class FOO

>     inherit

>         KL_INTEGER_ROUTINES
>             export
>                 {NONE} all
>             end

>     feature -- Basic operations

>         do_something is
>                 -- Do something with a character obtained from
>                 -- its ASCII code provided as input by the user.
>             local
>                 c: CHARACTER
>             do
>                 io.read_integer
>                 c := to_character (io.last_integer)
>                 ...
>             end

>     end

(Note that all features of `KL_INTEGER_ROUTINES` have been made secret since this class is inherited for implementation only.) The disadvantage of this method is that it rapidly pollutes the name space in the application classes. A possible way to handle this problem would be to declare facility classes, such as `KL_INTEGER_ROUTINES`, as expanded and use them in local variables:

>     expanded class KL_INTEGER_ROUTINES

>     feature

>                 ...

>     end

>     class FOO

>     feature -- Basic operations

>         do_something
>                 -- Do something with a character obtained from
>                 -- its ASCII code provided as input by the user.
>             local
>                 integer_routines: KL_INTEGER_ROUTINES
>                 c: CHARACTER
>             do
>                 io.read_integer
>                 c := integer_routines.to_character (io.last_integer)
>                 ...
>             end

>     end

Some Eiffel compilers will even optimized the code above by inlining the call to `to_character` and hence avoiding the (implicit) creation of the expanded object. Unfortunately, user-defined expanded types are not properly supported by all Eiffel compilers, making it impossible to use this mechanism in portable code. To work around this problem and to avoid having to create an instance of these `_ROUTINES` classes "by hand" each time they are used, they can be accessed through once functions. For example, for class `KL_INTEGER_ROUTINES`, the following class will be provided:

>     class KL_IMPORTED_INTEGER_ROUTINES

>     feature -- Access

>         INTEGER_: KL_INTEGER_ROUTINES
>                 -- Routines that ought to be in class INTEGER
>             once
>                 create Result
>             ensure
>                 integer_routines_not_void: Result /= Void
>             end

>     end

and class `FOO` will become:

>     class FOO

>     inherit

>         KL_IMPORTED_INTEGER_ROUTINES

>     feature -- Basic operations

>         do_something
>                 -- Do something with a character obtained from
>                 -- its ASCII code provided as input by the user.
>             local
>                 c: CHARACTER
>             do
>                 io.read_integer
>                 c := INTEGER_.to_character (io.last_integer)
>                 ...
>             end

>     end

The class name convention `_IMPORTED_` has been adopted after reading an article from Richie Bielak which appeared in Eiffel Outlook in May 1994 (volume 3, number 5, page 6). The use of `INTEGER_`, all characters in uppercase (instead of the typical style guideline which states that once function names should have their first letter capitalized and all remaining in lowercase), has been chosen to make it clear that these routines really ought to be implemented in class `INTEGER` itself. The underscore at the end is to avoid compilation problems with some Eiffel compilers which could consider, with no good reasons in my opinion, `INTEGER` as one of Eiffel's reserved words.

## Caveats

The technique described above is not what we can call a masterpiece of object-oriented programming, but it has the advantage of getting around the drawbacks of the other technique using adaptation by inheritance. However the two techniques presented so far only take care of class and feature incompatibilities, but for the sake of interoperability, they will fail to deal with language differences (also known as dialects) due to bugs or extensions provided by some Eiffel compilers. The use of a simple preprocessor will help in such situations.