This site contains older material on Eiffel. For the main Eiffel page, see http://www.eiffel.com.

25.9 FACILITY INHERITANCE

With facility inheritance we are even less coy than with implementation inheritance about why we want the marriage: pure, greedy self-interest. We see a class with advantageous features and we want to use them. But there is nothing to be ashamed of: the class has no other raison d'être.

Using character codes

The Base Library includes a class ASCII:

 indexing   description:
    "The ASCII character set. % %This class may be used as ancestor by classes
    needing its facilities."; class ASCII feature -- Access
  Character_set_size: INTEGER is 128; Last_ascii: INTEGER
is 127;   First_printable: INTEGER is 32; Last_printable:
INTEGER is 126;   Letter_layout: INTEGER is 70; 
Case_diff: INTEGER is 32;
    -- Lower_a -- Upper_a
  ...
  Ctrl_a: INTEGER is 1; Soh: INTEGER is 1;   Ctrl_b:
INTEGER is 2; Stx: INTEGER is 2;   ...
  Blank: INTEGER is 32; Sp: INTEGER is 32;   Exclamation:
INTEGER is 33; Doublequote: INTEGER is 34;   ...
  Exclamation: INTEGER is 33; Doublequote: INTEGER is 34;
  ...
  Upper_a: INTEGER is 65; Upper_b: INTEGER is 66;   ...
  Lower_a: INTEGER is 97; Lower_b: INTEGER is 98; 
... etc. ...  end -- class ASCII 

This class is a repertoire of constant attributes (142 features in all) describing properties of the ASCII character set. As the description entry states, it is meant to be inherited by classes needing access to such properties.

Consider for example a lexical analyzer --- the part of a language analysis system that is responsible for identifying the basic elements, or tokens, of an input text; these tokens may be (assuming the input is a text in some programming language) integer constants, identifiers, symbols and so on. One of the classes of the system, say TOKENIZER, will need access to the character codes, so as to classify the input characters into digits, letters etc. Such a class will inherit these codes from ASCII:

class TOKENIZER inherit ASCII feature   ...
Routines here may use such features as Blank, Case_diff etc. ...  end

Classes such as ASCII have been known to raise a few eyebrows; before going into the methodological discussion of whether they are a proper application of inheritance, we will look at another example of facility inheritance.

Iterators

The second example will show a case in which the inherited features are not just constant attributes (as with ASCII) but routines of the most general kind.

Assume that we want to provide a general mechanism to iterate over data structures of a certain kind, for example linear structures such as lists. "Iterating" means performing a certain procedure, say action, on elements of such a structure, taken in their sequential order. A number of iteration mechanisms must be provided, including: applying action to all the elements; applying it to all the elements that satisfy a certain criterion given by a boolean-valued function test; applying it to all the elements up to the first one that satisfies test, or the first one that does not satisfy this condition; and so on. A system that uses the mechanism must be able to apply it to any action and test of its choice.

At first it might seem that the iterating features should belong to the data structure classes themselves, such as LIST or SEQUENCE; but as an exercise invites you to determine for yourself this is not the right solution. It is preferable to introduce a separate hierarchy for iterators:

Class LINEAR_ITERATOR, the one of interest for this discussion, looks like this:

 indexing
  description:
    "Objects that are able to iterate over linear structures"; status: "See
  notice at end of class"; names: iterators, iteration, linear_iterators,
  linear_iteration; date: "$Date: 2007-03-30 19:10:11 +0000 (Fri, 30 Mar 2007) $"; revision: "$Revision:
  1.7 $" deferred class LINEAR_ITERATOR [G] inherit 
ITERATOR [G]
    redefine target end; feature -- Cursor
movement   target: LINEAR [G];       -- The structure to which iteration
features will apply.    test: BOOLEAN is       -- The boolean
condition used to select applicable elements     deferred
    end;

  action is       -- The action to be applied to selected
elements     deferred     end;   do_if is
      -- Apply action in sequence to every item of target
that satisfies test.      do       from
start invariant invariant_value until exhausted loop
        if test then           action
	end;         forth
      end     ensure then       exhausted
  end;   ... And so on: do_all, do_while, do_until etc. ...
end -- class LINEAR_ITERATOR 

Now assume a class that needs to perform a certain operation on selected elements of a list of some specific type; for example a command class in a text processing system may need to justify all paragraphs in a document, excepted for preformated paragraphs (such as program texts and other display paragraphs). Then:

 class
JUSTIFIER inherit   LINEAR_ITERATOR [PARAGRAPH]
    rename       action as justify,       test
as justifiable,       do_all as justify_all 
end feature   justify is 
do ... end;   test is       -- Is
paragraph subject to justification?      do       Result :=
not preformated     end;   ...  end --
class JUSTIFIER

The renaming was not indispensable but helps for clarity. Note that there is no need to declare or redeclare the procedure justify_all (the former do_all): as inherited, it does the expected job based on the effected versions of action and test.

Procedure justify, instead of being described in the class, could be inherited from another parent. In this case multiple inheritance would perform a "join" operation that effects the deferred action, inherited from one parent under the name justify (here the renaming is essential), with the effective justify inherited from the other parent. A form of marriage of convenience, in fact.

Forms of facility inheritance

The two examples, ASCII and LINEAR_ITERATOR, are typical of the two main variants of facility inheritance:

  • Constant inheritance, in which the parent principally yields constant attributes and shared objects.

  • Operation inheritance, in which it yields routines.

As noted earlier, it is possible to combine both of these variants in a single inheritance link. That is why facility inheritance is one of our categories, not two.

Understanding facility inheritance

To some people facility inheritance appears to be an abuse of the mechanism --- a form of hacking. It is not. Examination of our examples explains why.

The main question to consider in these examples is not about inheritance but about the classes that have been defined, ASCII and LINEAR_ITERATOR. As always when looking at a class design, we must ask ourselves: "Does this indeed describe a meaningful data abstraction?" --- a set of objects characterized by their abstract properties.

With the examples the answer is less obvious than with a class RECTANGLE, BANK_ACCOUNT or LINKED_LIST, but it exists all the same:

  • Class ASCII represents the abstraction: "any object that has access to the properties of the ASCII character set".

  • Class LINEAR_ITERATOR represents the abstraction: "any object that has the ability to perform sequential iterations on a linear structure". Such objects, by the way, tend to be of the "machine" kind described in earlier chapters.

Once these abstractions have been accepted, the inheritance links do not raise any problem: an instance of TOKENIZER does need "access to the properties of the ASCII character set", and an instance of JUSTIFIER does need "the ability to perform sequential iterations on a linear structure". In fact, we could classify such inheritance links examples under the subtype kind. What distinguishes facility inheritance is the nature of the parent.

That the classes themselves are the issue, not the use of inheritance, is reinforced by the observation that an application class could rely on these classes as a client rather than heir. This would make things heavier, especially for ASCII: with

 charset: ASCII;
...
create charset 

every use of a character code would have to be written charsetlLower_a and the like. The object attached with ASCII does not play any useful role. With LINEAR_ITERATOR the same comments apply as long as a given class needs only one kind of iteration. If several are required, it becomes interesting to create iterator objects, each with its own version of action and test; then you can have as many iteration schemes as you need.

PREVIOUS SECTION ---- NEXT SECTION