Previous Table of Contents Next


9.5.2. Classes

The notion of class is central to the Eiffel approach. A class is the description of a type of runtime data structures (objects), characterized by common operations (features) and properties. Examples of classes include

  In a banking system, a class ACCOUNT may have features such as deposit, adding a certain amount to an account, all_deposits, yielding the list of deposits since the account’s opening, and balance, yielding the current balance, with properties stating that deposit must add an element to the all_deposits list and update balance by adding the sum deposited and that the current value of balance must be consistent with the lists of deposits and withdrawals.
  A class COMMAND in an interactive system of any kind may have features such as execute and undo, as well as a feature undoable, which indicates whether a command can be undone, with the property that undo is only applicable if undoable yields the value true.
  A class LINKED_LIST may have features such as put, which adds an element to a list, and count yielding the number of elements in the list with properties stating that put increases count by one and that count is always non-negative.

We may characterize the first of these examples as an analysis class, directly modeling objects from the application domain; the second one as a design class, describing a high-level solution; and the third as an implementation class, reused whenever possible from a library such as EiffelBase. In Eiffel, however, there is no strict distinction between these categories; it is part of the approach’s seamlessness that the same notion of class, and the associated concepts, may be used at all levels of the software development process.

9.5.3. Class Relations

Two relations can exist between classes:

  You can define a class C as a client of a class A to enable the features of C to rely on objects of type A.
  You may define a class B as an heir of a class A to provide B with all the features and properties of A, letting B add its own features and properties and modify some of the inherited features if appropriate.

If C is a client of A, A is a supplier of C. If B is an heir of A, A is a parent of B. A descendant of A is either A itself or, recursively, a descendant of an heir of A; in more informal terms, a descendant is a direct or indirect heir, or the class itself. To exclude A itself, we talk of proper descendant. In the reverse direction, the terms are ancestor and proper ancestor.

The client relation can be cyclic; an example involving a cycle would be classes PERSON and HOUSE, modeling the corresponding informal everyday object types and expressing the properties that every person has a home and every home has an architect. The inheritance (heir) relation may not include any cycle.

In modeling terms, client roughly represents the relation “has,” and heir roughly represents “is.” For example, we may use Eiffel classes to model a certain system and express that every child has a birth date (client relation) and is a person (inheritance).

Distinctive of Eiffel is the rule that classes can only be connected through these two relations. This excludes the behind-the-scenes dependencies often found in other approaches, such as the use of global variables, which jeopardize the modularity of a system. Only through a strict policy of limited and explicit interclass relations can we achieve the goals of reusability and extendibility.

9.5.4. The Global Inheritance Structure

If you write an Eiffel class, it does not come into a vacuum but fits in a preordained structure, shown in Figure 9.3 and involving a few library classes: GENERAL, ANY, and NONE.


FIGURE 9.3.  The global inheritance structure.

Any class that does not explicitly inherit from another is considered to inherit from ANY, which itself inherits from GENERAL. This makes GENERAL an ancestor of all developer-written classes (with an exception explained later). GENERAL introduces a number of general-purpose features useful everywhere; examples include copying, cloning, and equality testing operations (section 9.6.10), and default input/output mechanisms.

ANY, as delivered, is empty except for the clause stating that it inherits from GENERAL. Individual companies, groups, or projects can add features to it to provide general mechanisms available to all classes developed in a certain environment. This also gives the ability (not frequently useful) to produce classes that are not descendants of GENERAL: Just redefine ANY so that it ceases to be an heir of GENERAL.

NONE is a fictitious class, which is considered to be an heir of any class that has no explicit heir. Because inheritance has no cycles, NONE cannot have proper descendants. This makes it useful, as you will see, to specify non-exported features and to denote the type of void values.

9.5.5. Clusters

Classes are the only form of module in Eiffel. As is explained in more detail later, they also provide the basis for the only form of type. This module-type identification is at the heart of object technology and yields the fundamental simplicity of the Eiffel method.

Efforts to introduce a higher-level notion of module above classes are misguided because they introduce a whole new set of issues (namespace, name visibility, information hiding, separate compilation, and module inclusion) to which the solutions would clash with the class-level techniques. This would also hamper reusability (by making it harder to reuse a class by itself) and extendibility.

There is a need, however, for an organizational concept: cluster. A cluster is a group of related classes. The cluster is a property of the method, enabling managers to organize the development into teams, but it does not require a specific Eiffel language construct. As we have already seen (section 9.3.1), it also plays a central role in the lifecycle model.


Previous Table of Contents Next