This is G o o g l e's cache of http://www.elj.com/eiffel/why-eiffel/.
G o o g l e's cache is the snapshot that we took of the page as we crawled the web.
The page may have changed since that time. Click here for the current page without highlighting.
To link to or bookmark this page, use the following url: http://www.google.com/search?q=cache:FSB-ImIX3isC:www.elj.com/eiffel/why-eiffel/+%22Why+Eiffel%22&hl=en&ie=UTF-8


Google is not affiliated with the authors of this page nor responsible for its content.
These search terms have been highlighted: why eiffel 

Why Eiffel - Eiffel Liberty
Eiffel Liberty -> Eiffel -> Why Eiffel by Todd Plessel

[2nd Edition of OOSC (Object-Oriented Software Construction) by Bertrand Meyer]
OOSC2 Reviews

Why Eiffel?

Todd Plessel

Lockheed Martin / US EPA

plessel@vislab.epa.gov

  1. Purpose
  2. What is Eiffel?
  3. Software Quality
    1. Software Product Quality
      1. Correctness
      2. Robustness
      3. Efficiency
      4. Ease of Use
      5. Functionality
      6. Timeliness
      7. Cost
      8. Extendibility
      9. Compatibility
      10. Portability
      11. Reusability
      12. Understandability
      13. Testability
    2. Modularity Criteria
      1. Decomposability
      2. Composability
      3. Understandability
      4. Continuity
      5. Protection
    3. Modularity Rules
      1. Direct Mapping
      2. Few Interfaces
      3. Small Interfaces
        1. Operand Principle
      4. Explicit Interfaces
      5. Information Hiding
        1. Law Of Demeter
    4. Modularity Principles
      1. Linguistic Modular Units Principle
      2. Self-Documentation Principle
      3. Uniform Access Principle
      4. Command-Query Separation Principle
      5. Open-Closed Principle
        1. Liskov Substitution Principle
      6. Single-Choice Principle
        1. Dependency Inversion Principle
  4. Example Code
    1. MY_DISPENSER
    2. MY_DEQUE
    3. MY_STACK
    4. MY_QUEUE
    5. Short-Flat Form Of MY_STACK
    6. Ace File
  5. Software Process Quality
    1. Reusability Culture Principle
    2. Seamlessness
    3. Reversible
    4. Quality First
  6. How The Alternatives Fall Short Compared To Eiffel
    1. Smalltalk
    2. C++
    3. Java
    4. CLOS
    5. Ada
    6. Sather
  7. Conclusions
  8. More On Eiffel
  9. References


1. Purpose

This document describes the Eiffel programming system and explains why it is superior for large-scale application software development by relating it to the software quality definitions in Meyer's comprehensive pioneering text Object-Oriented Software Construction [1].

2. What Is Eiffel?

Eiffel is a methodology, language and environment for developing high-quality reusable software components.

Methodology: A system of principles and rules based on theory and practice that guides the software construction process to achieve high-quality software.

Language: An elegant object-oriented programming language (OOPL) that strongly supports the methodology.

Environment: Integrated set of OO CASE tools that strongly support software development using the methodology and language [12]. Here's a screen snapshot.

How does it differ from other "integrated development environments"?

Eiffel is an elegant modern object-oriented approach to software development that emphasizes software product quality - especially correctness and reusability - crucial prerequisites to shifting current practice towards component-based development.

What does all that mean?

Elegance: "the achievement of a given functionality with a minimum of mechanism and a maximum of clarity" -- F.J. Corbato [8].

Some of Eiffel's goals can be realized in non-Eiffel approaches but only with considerable external/non-standard mechanisms. Eiffel is elegant in the way it seamlessly integrates many aspects of high-quality software development.

Modern

The award-winning definitive text on the object-oriented (OO) approach to software development was published in 1988 (and expanded in 1997) - Object-Oriented Software Construction by Bertrand Meyer [1] - and it introduced Eiffel.

What's wrong with the older approaches?

For reasons described in [1], older approaches, such as Structured Programming, simply don't scale-up to the ambitious goals (particularly reusability) and dynamic requirements for today's software applications.

Aren't there even newer approaches?

There are newer object-oriented methods and programming languages but they lack strong support for software quality compared to the Eiffel approach. In many ways, Eiffel is still ahead of its time as the software development industry has not yet adopted the mindset needed to mass-produce high-quality reusable components [6,13].

Object-Oriented

Ok, What's your definition of OO?

First of all, object-oriented is somewhat of a misnomer since the essence of OO is inheritance - which is a relationship between classes rather than objects. Nonetheless, the Eiffel definition of OO is:

"Built from classes, assertions, genericity, inheritance, polymorphism and dynamic binding." [1].

However, there exist languages (e.g., Ada-83) that support classes (abstract data types (ADTs) where module = type) and objects (instances of classes) but not inheritance. Such languages may be called object-based rather than object-oriented.

Genericity (parameterized typing) and assertions (covered later) are independent of object-orientation. Since there exist languages accepted by many to be OO that lack genericity and assertions (e.g., Java) and there exist languages accepted as non-OO that support (to varying degrees) genericity and assertions - e.g., Ada-83 and C, respectively.

Dynamic binding (selecting the appropriate operation on an object of a derived type) is a runtime consequence of polymorphism (declaring entities that denote objects of various derived types) which is made possible by inheritance.

So inheritance is the key, therefore I propose a simple litmus test:

If a language supports inheritance it is OO otherwise it is not OO.

This is not to diminish the importance of the other elements (assertions, genericity, etc.). They are all desirable - even crucial - and are supported by Eiffel.

Other relevant definitions:

Design: The art of recognizing, evaluating and selecting tradeoffs.

Discipline: A systematic rigorous approach to achieving a goal.

Engineering: The application of scientific principles to practical ends [9].

Software Engineering: The technological and managerial discipline concerned with systematic production and maintenance of software products of required functionality and quality on time and within cost estimates. [Fairley '85]

As will be shown, Eiffel combines many crucial concepts in a simple an powerful way that enables rigorous elegant designs and implementation and supports the engineering aspects of software development.

Abstract Data Type (ADT): A set of mathematical elements specified by listing the functions applicable to all these elements and the formal properties of these functions. [1]

As with any pure OO programming language, Eiffel strongly supports ADTs in the form of classes. However, Eiffel is the only major OO language to provide checkable assertions to enforce the ADT properties.

3. Software Quality

Software qualities can be associated with either the process or the product:

Software Process: The human activity associated with software development.

Software Product: The software and artifacts resulting from that process.

We will define product qualities and modularity principles and show how Eiffel strongly supports them.

3.1 Software Product Quality

Software product qualities can be external or internal [1]:

External Quality Factors: Quality properties observable from executing a program.

These include: correctness, robustness, efficiency, ease of use, functionality, timeliness, cost

3.1.1 Correctness: The ability of software to perform their exact tasks as defined by their specification.

This is the most important quality. After all, if the software doesn't do what it is supposed to do then nothing else about it really matters. For all practical purposes it is useless.

Strong Static Typing

Eiffel supports correctness through static typing. The Eiffel language requires all program entities to be declared of some class type thereby virtually eliminating runtime type errors that occur in programs developed in dynamically typed languages such as SmallTalk and CLOS.

    dilbert: HAPLESS_PROGRAMMER
    ...
    dilbert.fires (pointy_haired_boss)
        -- A sadly impossible operation that is caught at compile time

Automatic Memory Management

Eiffel includes a (multi-threaded) "garbage collector" that handles all deallocation of object memory thus eliminating a huge source of errors in manual dynamic memory languages such as C++.

Design By Contract

Design By Contract (DBC) is one of Eiffel's most valuable pioneering contributions to the field. It is covered in more detail in a separate document - Design By Contract: A Missing Link In The Quest For Quality Software. DBC is described in section 5. ("Design By Contact" is a trademark of Interactive Software Engineering, Inc. - the primary vendor for Eiffel.)

In short, Design By Contract is a simple and powerful discipline that applies the business contracting metaphor and runtime-checkable assertions to the specification and verification of software component interfaces.

3.1.2 Robustness: The ability of software systems to react appropriately to abnormal conditions.

Disciplined Exceptions

Eiffel supports robustness primarily through disciplined exceptions. Unlike the unstructured ad-hoc notoriously error-prone try-catch blocks of C++ [17] and Java [18], Eiffel employs a structured exception handling mechanism that is compatible with Design By Contract. A routine can have only one rescue clause to handle exceptions, restore the class invariant and possibly propagate the exception up to the caller:

    test is
            -- Test exceptions
        do
            try_something_that_may_fail
        rescue
            report_failure
            retry
        end

3.1.2 Efficiency: The ability of a software system to place as few demands as possible on hardware resources such as processor time, internal and external memories and communication bandwidth.

Eiffel supports efficiency in several ways:

Native Code Compiled

Eiffel can be compiled into ANSI-C which is then compiled into efficient native machine code (unlike most Smalltalk, CLOS and Java implementations).

Compiler Optimizations

Unlike C++, the Eiffel compiler rather than the application programmer, is responsible for optimizing code. This includes tunable inlining of short routines and automatic implementation of static dispatch when dynamic dispatch is not needed.

This is a consequence of the overall Eiffel philosophy that, as much as possible, computers rather than people, should perform all automateable mundane tasks like optimization, dependency analysis, etc. since the results will be more reliable and the programmer less distracted.

Expanded Types

Eiffel provides expanded classes whose instances are not allocated dynamically on the heap but stack-based instead. This is crucial for efficiently supporting large collections of small objects (like complex numbers or 3D vectors).

Optimized Basic Types

Basic types (BOOLEAN, CHARACTER, INTEGER, REAL, DOUBLE) are expanded for efficient processing but, unlike Java, can be used in the same ways as other objects - for example, stored in generic containers.

3.1.4 Ease of use: The ease with which people of various backgrounds and qualifications can learn to use software products for their intended role.

Eiffel provides a rich library of classes that enable the construction of applications featuring standard graphical user interfaces for WYSIWYG interaction style.

3.1.5 Functionality: The extent of possibilities provided by a system. The applicability of software to solve a range of problems.

Eiffel's rich class library provides a good deal of ready-to-use functionality for your own applications.

3.1.6 Timeliness: The ability of a software system to be made available when or before its users want it.

Eiffel addresses timeliness by improving programmer productivity in several ways:

Rich Class Libraries

Eiffel class libraries provide a good foundation to build upon.

Interpreter

The Eiffel Environment also includes compilation of byte-codes that are interpreted. This is known as ISE Melting Ice Technology and it enables incremental changes to be compiled in seconds so that the software can be run immediately - leading to a more rapid micro-process.

3.1.7 Cost: The financial cost of developing or acquiring and using the software.

Eiffel is reasonably priced (from multiple vendors) and does not require user-runtime licensing fees. There are even free compilers, class libraries and other Eiffel products available.

Internal Quality Factors: Quality properties perceptible by examining the source code of a program.

These include correctness, robustness, efficiency (already covered) and extendibility, compatibility, portability, reusability, understandability and testability,

3.1.8 Extendibility: The ease of adapting software to changes of specification.

Inheritance is the principle means of extending object-oriented software and is covered below under Reusability.

Beyond flexible support for inheritance, Eiffel strongly supports the evolutionary life-cycle of software components - including major changes such as cluster, class and feature renaming or redefinition.

Obsolete clause:

Eiffel includes an obsolete clause that specifies that a cluster, class or routine has been made obsolete by a new version. This new version is usually provided automatically so during compilation, a client is warned of the obsolete component but may continue to use it for the moment and make the necessary changes in the client code at a more convenient time.

Here is an example of an obsolete class:

    ...
    deferred class

        SEQUENCES
    obsolete
        "Use class REPETITION instead. Same features, more consistent name."
    inherit
        REPETITION
    end -- class SEQUENCES


And here is an obsolete feature:


    feature -- Obsolete
        position_in_line: INTEGER is
            obsolete
                "Use ``column_number'' instead"
            do
                Result := column_number
            end

3.1.9 Compatibility: The ease of combining software elements with others.

Eiffel supports compatibility with in some novel ways. An important Eiffel design decision was to separate the specification of software modules from their assembly. Unlike C++, Java and Ada, Eiffel does not burden the programmer with the distraction of maintaining a dependency between classes in the code defining the classes. Instead of requiring statements such as '#include', or 'import' to specify to the compiler exactly what components are being used in the current class and/or where to locate them, the Eiffel compiler figures this out based on an Ace file.

An Ace file is written in LACE (Language for the Assembly of Classes in Eiffel) and it addresses high-level issues such as various compilation options (e.g., optimization levels), where to find various clusters that contain classes that are being used, and where to find external software that is being used and even options to adapt software as described below.

An Ace file differs from a Unix Makefile in that it does not specify the dependencies between code (e.g., makedepend), but rather only where to look for classes (more like -I and -L options to the C compiler).

Cluster-level Redefinitions

LACE provides cluster-level redefinitions similar to those of classes. This supports customization (e.g., renaming conflicting classes, etc.) that may be needed when combining clusters from multiple sources (e.g., vendors). This is cleaner and more flexible than the lower-level approaches such as ugly prefixes (e.g., MS_Complex) used in C++ or hard-coded package path names such as 'import com.ms.numeric' used in Java.

    adapt
        graphics:
            rename
                CURSOR as GRAPHICS_CURSOR,
                WINDOW as GRAPHICAL_WINDOW
External Software

Eiffel supports interfacing to external software - software written in a language other than Eiffel - in several ways:

External Feature Definitions:

    sine (v: DOUBLE): DOUBLE is
            -- Trigonometric sine of radian `v' approximated
            -- in range [-pi/4, +pi/4]
        external
            "C | <math.h>"
        alias
            "sin"
        end
Legacy++

This Eiffel Environment tool reads C++ headers and generates an Eiffel 'wrapper class' for each C++ class encountered.

Cluster-level Specifications:

LACE also supports specifying the other code that external software depends on:

    external
        Object:
            "-L /usr/lib32",
            "-L /home/plessel/lib/internal/c/lib/IRIX",
            "-lFile", "-lMemory", "-lError",
            "-lmalloc -lm";
        include_path:
            "/usr/include -I/usr/include/sys \
            -I/usr/include/rpc -I/usr/include/netinet \
            -I/home/plessel/lib/internal/include -I.";
        C: "Assertions.h", "Error.h", "Memory.h", "File.h";
    generate
        Executable: "./bin/IRIX";
        Object (yes): "./bin/IRIX/xdr_stream_test";
        C (no): "./xdr_stream_package";


3.1.10 Portability: The ease of transferring software products to various hardware and software environments.

Eiffel supports portability by:

Compilation to ANSI C

The Eiffel Environment enables Eiffel code to be compiled to ANSI C which can then be moved to another host and compiled into native code (along with the Eiffel runtime library (for automatic memory management - "garbage collector")). The supported platforms include PCs, all the major UNIX workstations and Cray Supercomputers. (That's more portability than is currently obtainable with Java since ANSI C is and will probably remain the most widely available language - a kind of "portable assembler".)

Compilation to Java

The newest Eiffel Environment will allow compilation of Eiffel into Java source code and/or JVM byte codes. There are several different projects underway in this area [Jive, Bruce]. It is interesting to note that one approach is a translator from compiled Eiffel byte-codes to Java source code - since Eiffel code for multiple inheritance and genericity has been 'expanded' at that level and maps more directly to Java source code - since Java lacks multiple inheritance and genericity. This is further evidence that Eiffel is a higher-level language than Java.

3.1.11 Reusability: The ability of software elements to serve for the construction of many different applications.

Inheritance is the key technique that facilitates maximal reuse. (The other form of code reuse - the client relation - is crucial too but not as flexible.) Eiffel's extremely flexible inheritance model supports reuse by:

Multiple Inheritance

Eiffel supports multiple inheritance of deferred classes and effective classes. Unlike C++, Eiffel elegantly addresses issues involved in combining class definitions allowing: selection, renaming, changing export status (visibility), undefining and redefining. This flexibility allows for a a range of possible kinds of inheritance [7].

Multiple Views

Eiffel uses selected export to allow feature visibility to be mapped to specific closely-related classes when required. This is a more flexible alternative than C++ friends or Java's nested classes.

    ...
    class
        LINKABLE
    ...
    feature {CELL, CHAIN} -- Implementation
    ...

Redefinition of Routines to Attributes

Eiffel allows routine features to be redefined as attributes when desired (to allow descendant classes to trade memory for speed). (See Uniform Access Principle below.)

Cluster-level Redefinitions (covered above) also facilitate reuse.

Genericity

Eiffel supports a static form of polymorphism known as genericity (or parameterized types). Genericity enables the representation of a class of classes that are parameterized by one or more types that are used in the interface and implementation. The typical example of this is generic containers - classes that describe implementations of fundamental data structures such as arrays, linked-lists, etc. and whose implementations differ only in the type of the contained items.

With genericity such classes are implemented just once and are instantiated for various specific types. Here is an excerpt from a declaration for a generic HASH_TABLE class:

    class

        HASH_TABLE [G, H -> HASHABLE]

    ...

    local
        employee_directory: HASH_TABLE [EMPLOYEE, EMPLOYEE_ID]

G stands for the type of items to be stored and H stands for the type used to look-up the items. For example, employee_directory instantiates a HASH_TABLE with G as EMPLOYEE and H as EMPLOYEE_ID.

Without genericity, such classes must be implemented either once for each type that is to be contained - leading to a combinatorial explosion of classes, or implemented in terms of a pointer to a generic type (e.g., void*, or Object) - which sacrifices efficiency (can't contain non-pointer types such as basic types (floating-point numbers, etc.) and/or sacrifices type-safety - there is no pre-runtime way to eliminate type-mismatch errors. (Smalltalk and Java are examples of OOPLs that suffer from this limitation.)

Eiffel uses constrained genericity. This requires that the generic type inherit from a given type - denoted as H -> HASHABALE in the above example. When no constraint is specified, the type ANY (an ancestor of all types) is implied. For example, G in the above declaration is equivalent to G -> ANY. While this restriction prevents type-mismatch errors ad-initio, it is less flexible than unconstrained genericity (e.g., as in C++) since it is intrusive - requiring a class to inherit from the named type rather than simply supporting a conforming interface. On the other hand, Eiffel's flexible multiple-inheritance makes this trivial to achieve except with external third-party code.

3.1.11 Understandability: The ease with which a programmer can understand the source code of a software system.

Eiffel supports understandability in many ways:

Readable Syntax

Eiffel program text is so readable that even non-programmers (e.g., business domain experts) can understand it enough to comfortably participate in reviews during joint application development [4]. To a programmer, it sometimes feels like you are writing pseudo-code. It uses clear English keywords formatted into simple structured clauses making minimal use of punctuation (even semicolons are not required to separate/terminate statements).

Consider the following routine (typical of code found in the Eiffel Libraries) featuring a function definition, local variable declarations, genericity (parameterized types), looping, dynamic allocation and initialization, function calls, etc.:

    linear_representation: ARRAYED_LIST [G] is
            -- Representation as a linear structure
            -- (in the reverse order of original insertion)
        local
            i: INTEGER
        do
            from
                !! Result.make (count)
                i := count
            until
                i < 1
            loop
                Result.extend (i_th (i))
                i := i - 1
            end
        end

Compare it to the equivalent code in your favorite programming language. Show them both to ten people (not all programmers) and ask them which notation they would rather study, master and read daily. For example, here's a typical gem from the C++ Standard Library:


      #ifdef __STL_MEMBER_TEMPLATES
          template <class ForwardIterator>
          iterator allocate_and_copy(size_type n,
                                     ForwardIterator first,
                                     ForwardIterator last) {
            iterator result = data_allocator::allocate(n);
      #         ifdef __STL_USE_EXCEPTIONS
            try {
      #         endif /* __STL_USE_EXCEPTIONS */
              uninitialized_copy(first, last, result);
              return result;
      #         ifdef __STL_USE_EXCEPTIONS
            }
            catch(...) {
              data_allocator::deallocate(result, n);
              throw;
            }
      #         endif /* __STL_USE_EXCEPTIONS */
          }
      #else /* __STL_MEMBER_TEMPLATES */

Simple Statements

Unlike C++ and Java, Eiffel does not allow unstructured constructs such as: goto, break, continue, multiple return statements, etc. It has only one kind of loop statement, only one way to increment a variable (a statement rather than an expression), a simple type declaration statement, and a simple genericity syntax. In Eiffel, you won't have to suffer deciphering abominations such as the following two declarations:

    int (*signal(int sig, int (*func)(int, ...)))(int, ...);

    template <class Key, class Val, class KeyOfValue, class Compare,
              class Alloc>
    rb_tree<Key, Val, KeyOfValue, Compare, Alloc>::iterator
    rb_tree<Key, Val, KeyOfValue, Compare, Alloc>::insert_unique(
          iterator position, const Val& v);

Minimal and Natural Operators

Standard mathematical operators are used only for numeric types. Assignment and comparison operators are := and = rather than C++ and Java's = and ==.

There is no pointer arithmetic in Eiffel (*p++, *( a + j * idim + i )).

Consistent Naming Convention

Class names: ALL_CAPS

Constants: Capitalized

Rest: all_lower - fully spelled out rather than abbreviated horribly as in typical C/C++: fmtmsg.h, fcntl.h, etc.

Formalized Documentation

Eiffel code is documented with an index clause, standard stylized comments, assertions and tools to extract and browse. (See Self-Documentation Principle below for more details.)

Assertions

Assertions are crucial for understanding how to use a class. (This is covered in more detail in Design By Contract).

3.1.13 Testability: The ease of testing a software system for correctness and robustness.

Eiffel supports testability by:

Assertions Checking

Assertions can be selectively checked during testing based on system, cluster, class and kind (debug, check, loop, invariant, ensure, require). An assertion violation indicates an error (bug) in the client or supplier and manifests itself by raising an exception and (if not caught) generating a stack trace to aid in debugging.

Debug Clause

Eiffel also provides a debug clause for optionally (depending on compile options) including statements useful for tracing program execution and state during debugging.

    debug ("count")
        io.put_string ("count = ")
        io.put_integer (count)
        io.new_line
    end

Eiffel Environment Debugging

The Eiffel Environment includes a user-friendly way of stepping through code and examining entities (variable contents).

Eiffel Environment Profiling

A Profiling tool allows instrumentation of code to measure performance bottlenecks and analyze code coverage.

3.2 Modularity Criteria, Rules and Principles [1]

Related to software quality as a whole, are qualities associated with the structure of the software.

3.2 Modularity Criteria

3.2.1 Decomposability: The ability of a software construction method to help in decomposing a software problem into a small number of less complex subproblems, connected by a simple structure, and independent enough to allow further work to proceed separately on each of them.

3.2.2 Composability: The ability of a software construction process to yield modules that can be freely combined with others to produce new systems.

3.2.3 Understandability: The ease with which a programmer can understand a given module without having to examine many others.

3.2.4 Continuity: A small change in specification requires only a small change in implementation (a small number of modules).

3.2.5 Protection: The ability of modules that, at runtime, limit the propagation of failures to few, if any, other modules.

These are the essential motivation for object-based design. Consider how older approaches such as "Structured/Functional Decomposition / Programming by Stepwise Refinement" fail to support composability, continuity or protection (when applied application-wide).

3.3 Modularity Rules

3.3.1 Direct Mapping: The modular structure for modeling a problem should be reflected in the modular structure of the software that solves the problem.

This is a major appeal of the object-oriented approach. However, it is important to remember that an OO program does not model "the real world" but rather a model of a model of... a part of the "real world". "An astronomy program is not a model of the universe; it is a software model of someone's model of some properties of some part of the universe [1]." It is important that the key 'domain objects' be directly representable in the 'solution domain', but what is more important is the usefulness of the set of abstractions employed (Abstract Data Types - the set of operations and their properties) not to what extent they are "realistic".

3.3.2 Few Interfaces: Every module should communicate with as few others as possible.

Object Design Patterns such as Fascade and Mediator [14] can support this goal by greatly reducing overall coupling complexity.

3.3.3 Small Interfaces (weak coupling): If two modules communicate, they should exchange as little information as possible.

Related to this is cohesion supported by another Eiffel principle [1]:

3.3.3.1 Operand Principle: The arguments of a routine should only include operands (no options - "modes of operation").

3.3.4 Explicit Interfaces: Whenever two modules A and B communicate, this must be obvious from the text of A or B or both.

Global variables (and the use of environment variables) violate modularity. Unlike C++ and other OOPLs, Eiffel disallows global variables.

3.3.5 Information hiding: A module must allow only a subset of its properties, called the public interface, to be accessible to client modules while ensuring that the rest remains secret (unknowable and inaccessible).

A class consists of features that are either attributes (data members) or routines. Allowing clients write access to an object's attributes completely violates information hiding. Eiffel disallows this while 'public data members' are frequently found in C++ and Java Standard Libraries.

A related concept is:

3.3.5.1 Law of Demeter: Never retrieve part of an object and then perform an operation on that part, but rather have the object perform the operation itself [15]. Violating the Law of Demeter can effectively makes client code dependent on the implementation details of a supplier thus destroying modularity. (Though there are exceptions to this 'rule'.)

3.4 Modularity Principles

3.4.1 Linguistic Modular Units Principle: Modules correspond to syntactic units in the language used.

This means Module = Type = Class. The mathematical theory of Abstract Data Types (ADTs) should be directly supported by the OO language in the form of classes. In a pure OO language, all entities should be of class type. This is the case with Eiffel and is part of its seamless elegance. (In C++ and Java, however, basic types are not classes and this burdens the programmer with unnecessary complexity, for example, in the design and use of generic containers.)

3.4.2 Self-Documentation Principle: Modules contain canonical minimal and complete documentation on their use.

(To facilitate automatic extraction of such information by software tools.)

Eiffel provides superior support for this through:

Indexing Clauses

Class text begins with an indexing clause that contains a standardized but extensible set of tagged (searchable) lines describing various aspects of the class:

indexing

    description:
        "Dispensers that provide access to only one item at a time."
    status: "Todd Plessel, plessel@vislab.epa.gov"
    names: "dispenser"
    representation: "none"
    access: "fixed", "membership"
    contents: "generic"
    date: "$Date: 1998/02/22 17:00:00 $"
    revision: "$Revision: 1.0 $"

Comments

Eiffel employs a well-developed standard formatting style - including comments (covering both form and content) for classes and their features [3]. Because of this standard, the Eiffel Environment is able to extract comments for use in generating class documentation.

Assertions

The class and routine-level assertions are a crucial part of the class documentation.

Short-Flat

The Eiffel Environment allows classes to be viewed in various ways to facilitate understanding for potential use. Examples include:

Short Form: Displays the client interface defined by a class.

Flat Form: Displays all class features (inherited plus locally defined).

Short-Flat: Displays the effective client view of a classes features. (See listing 5.)

Here again Eiffel goes beyond highly touted more recent approaches such as JavaDoc. ("Back to the future with Eiffel!")

3.4.3 Uniform Access Principle: All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or computation.

Eiffel allows argumentless query routines to be redefined as attributes (to allow trading storage for computation).

To understand this, one must first understand another key Eiffel design principle:

3.4.4 Command-Query Separation Principle (CQSP):

Features should be of two logical types:

Commands: Perform an action - conceptually changing the object state - but do not return a value.

Queries: Answer a question without abstract side-effect (i.e., without changing conceptual state).

For example, item is a query and put is a command:

    feature -- Access

        item: G is
                -- Access top item.
            require
                not_empty: not empty

    feature -- Removal

        remove is
                -- Remove top item.
            require
                not_empty: not empty
            ensure
                not_full: not full
                one_less: count = old count - 1

Application of this principle leads to simpler designs that are more amenable to reasoning about correctness and testing. Although it is possible to apply this important principle in other OOPLs such as C++ and Java, it is extremely rare to find classes that do so (an exception being the well-designed C++ Booch Components). Instead the C++ and Java Standard Libraries violate this principle frequently.

Some typical violations of CQSP in the C++ Standard Library are:

     // insert/erase

     pair<iterator,bool> insert(const value_type& x) {
         return t.insert_unique(x); }
     iterator insert(iterator position, const value_type& x) {
         return t.insert_unique(position, x);
     }

     size_type erase(const key_type& x) {
         return t.erase(x);
     }

And from the Java Standard Library:

    public boolean add(Object o);
    public boolean remove(Object o);
    public Object remove(int index);
    public Object set(int index, Object element);

3.4.5 Open-Closed Principle: Modules are 'closed' - available for immediate use, as is, by clients and 'open' for extension by descendants inheriting from it.

This is a key concept that yields the major benefit of object-oriented designs as opposed to non-OO designs. Inheritance allows classes to be extended to encompass new behavior without requiring changes to the original class - which would potentially require changes to all client code currently using the original class. OO helps "software" live up to its name by supporting malleability without liability.

A related principle is:

3.4.5.1 Liskov Substitution Principle (LSP): derived classes must usable through the base class interface. This is polymorphism implemented by dynamic binding [15].

3.4.6 Single-Choice Principle: Whenever a software system must support a set of alternatives, at most only one module in the system should know their exhaustive set. (E.g., table-driven approaches.)

Adherence to this principle keeps designs OO in nature rather than degenerating into multiple distributed case statements as frequently found in non-OO designs. This leads to a related principle:

3.4.6.1 Dependency Inversion Principle: Details should depend upon abstractions. Abstractions should not depend upon details [16].

4. Example Code

To illustrate some of the ways that Eiffel supports all of these software qualities and modularity criteria, rules and principles, we present a small but complete example:

    MY_DISPENSER
         |
      MY_DEQUE
      /     \
  MY_STACK MY_QUEUE


This small class hierarchy defines generic containers that implement the classic FIFO (first-in-first-out) and LIFO (last-in-first-out) data structures. MY_DISPENSER (listing 1) is the deferred class that defines the abstract data type features (interface routines) for all dispensers.

    indexing

        description:
            "Dispensers that provide access to only one item at a time."
        status: "Todd Plessel, plessel@vislab.epa.gov"
        names: "dispenser"
        representation: "none"
        access: "fixed", "membership"
        size: "fixed"
        contents: "generic"
        date: "$Date: 1998/02/22 17:00:00 $"
        revision: "$Revision: 1.0 $"

    deferred class

        MY_DISPENSER [G]

    feature -- Access

        item: G is
                -- The accessible item that may be dispensed.
            require
                not_empty: not empty
            deferred
            end

        put (an_item: like item) is
                -- Insert `an_item' somewhere.
            require
                not_full: not full
            deferred
            ensure
                one_more: count = old count + 1
            end

    feature -- Measurement

        capacity: INTEGER is
                -- Maximum number of items that may be stored.
            deferred
            ensure
                strictly_positive: Result > 0
            end

        count: INTEGER is
                -- Current number of items stored.
            deferred
            ensure
                non_negative: Result >= 0
            end

    feature -- Status report

        empty: BOOLEAN is
                -- Are there no items stored?
            do
                Result := (count = 0)
            end

        full: BOOLEAN is
                -- Is there no room to insert new items?
            do
                Result := (count = capacity)
            end

    feature -- Element change

        put (an_item: like item) is
                -- Insert `an_item' somewhere.
            require
                not_full: not full
            deferred
            ensure
                one_more: count = old count + 1
            end

    feature -- Removal

        remove is
                -- Remove accessible item.
            require
                not_empty: not empty
            deferred
            ensure
                one_less: count = old count - 1
            end

        wipe_out is
                -- Remove all items, if any.
            deferred
            ensure
                empty: empty
            end

    invariant

        capacity_large_enough: capacity > 0
        count_large_enough: count >= 0
        count_small_enough: count <= capacity
        consistent_empty: empty = (count = 0)
        consistent_full: full = (count = capacity)

    end -- class MY_DISPENSER

MY_DEQUE (listing 2) inherits from MY_DISPENSER and effects (implements) all of the features and extends it to allow access/insertion/removal from either end (of a circular buffer).

    indexing

        description: "Double-ended queues."
        status: "Todd Plessel, plessel@vislab.epa.gov"
        names: "queue", "double-ended", "circular buffer"
        representation: "array"
        access: "fifo", "lifo"
        size: "fixed"
        contents: "generic"
        date: "$Date: 1998/02/22 17:00:00 $"
        revision: "$Revision: 1.0 $"

    class

        MY_DEQUE [G]

    inherit

        MY_DISPENSER [G]
            rename
                item as first_item,
                put as put_last,
                remove as remove_first
            redefine
                first_item,
                put_last,
                remove_first,
                wipe_out
            end

    creation

        make

    feature -- Initialization

        make (maximum_capacity: INTEGER) is
                -- Make an empty deque with the given `capacity'.
            require
                strictly_positive_capacity: maximum_capacity > 0
            do
                !! a.make (0, maximum_capacity - 1)
            ensure
                has_given_capacity: capacity = maximum_capacity
                empty: empty
            end

    feature -- Access

        first_item: G is
                -- The first item in the container.
            do
                Result := a.item (index_of_first)
            end

        last_item: G is
                -- The last item in the container.
            require
                not_empty: not empty
            do
                Result := a.item (index_of_last)
            end

    feature -- Measurement

        capacity: INTEGER is
                -- Current number of items stored.
            do
                Result := a.capacity
            end

        count: INTEGER
                -- Current number of items stored.

    feature -- Element change.

        put_first (an_item: G) is
                -- Make `an_item' the first one.
            require
                not_full: not full
            do
                if count /= 0 then
                    if index_of_first = 0 then
                        index_of_first := capacity - 1
                    else
                        index_of_first := index_of_first - 1
                    end
                end
                count := count + 1
                a.put (an_item, index_of_first)
            ensure
                item_is_first: first_item = an_item
            end

        put_last (an_item: G) is
                -- Make `an_item' the last one.
            do
                if count /= 0 then
                    if index_of_last = capacity - 1 then
                        index_of_last := 0
                    else
                        index_of_last := index_of_last + 1
                    end
                end
                count := count + 1
                a.put (an_item, index_of_last)
            ensure then
                item_is_last: last_item = an_item
            end

    feature -- Removal

        remove_first is
                -- Remove first item.
            do
                count := count - 1
                if count = 0 then
                    index_of_first := 0
                    index_of_last := 0
                elseif count = capacity - 1 then
                    index_of_first := 0
                else
                    index_of_first := index_of_first + 1
                end
            end

        remove_last is
                -- Remove last item.
            do
                count := count - 1
                if count = 0 then
                    index_of_first := 0
                    index_of_last := 0
                elseif index_of_last = 0 then
                    index_of_last := capacity - 1
                else
                    index_of_last := index_of_last - 1
                end
            end

        wipe_out is
                -- Remove all items, if any.
            do
                count := 0
                index_of_first := 0
                index_of_last := 0
            end

    feature {NONE} -- Implementation

        a: ARRAY [G]
                -- Container for items.

        index_of_first: INTEGER
                -- Index of first item, if any.

        index_of_last: INTEGER
                -- Index of last item, if any.

    invariant

        not_void: a /= Void

    end -- class MY_DEQUE

MY_STACK (listing 3) inherits from MY_DEQUE and renames and redefines the interface to be more "stack-like".

    indexing

        description: "FIFO (first in first out) queues."
        status: "Todd Plessel, plessel@vislab.epa.gov"
        names: "queue", "fifo"
        representation: "array"
        access: "fifo"
        size: "fixed"
        contents: "generic"
        date: "$Date: 1998/02/22 17:00:00 $"
        revision: "$Revision: 1.0 $"

    class

        MY_QUEUE [G]

    inherit

        MY_DEQUE [G]
            rename
                count as length,
                put_last as insert,
                remove_first as remove,
                first_item as first,
                wipe_out as clear
            export
                {NONE}
                    put_first, remove_last, last_item
            end

    creation

        make

    end -- class MY_QUEUE

MY_QUEUE (listing 4) does the same to yield a (single-ended) queue.

    indexing

        description: "FIFO (first in first out) queues."
        status: "Todd Plessel, plessel@vislab.epa.gov"
        names: "queue", "fifo"
        representation: "array"
        access: "fifo"
        size: "fixed"
        contents: "generic"
        date: "$Date: 1998/02/22 17:00:00 $"
        revision: "$Revision: 1.0 $"

    class

        MY_QUEUE [G]

    inherit

        MY_DEQUE [G]
            rename
                count as length,
                put_last as insert,
                remove_first as remove,
                first_item as first,
                wipe_out as clear
            export
                {NONE}
                    put_first, remove_last, last_item
            end

    creation

        make

    end -- class MY_QUEUE

(Note that this example is not meant to illustrate an ideal class design but rather to illustrate some Eiffel techniques. Eiffel already comes with a large rich library of classes covering such data structures and much more.)

Short-flat (listing 5) shows the short-flat view of MY_STACK.

    indexing

        description: "LIFO (last in first out) stacks."
        status: "Todd Plessel, plessel@vislab.epa.gov"
        names: "stack", "lifo"
        representation: "array"
        access: "fifo"
        size: "fixed"
        contents: "generic"
        date: "$Date: 1998/02/22 17:00:00 $"
        revision: "$Revision: 1.0 $"

    class interface

        MY_STACK [G]

    creation

        make (maximum_capacity: INTEGER)
                -- Make an empty deque with the given capacity.
                -- (from MY_DEQUE)
            require -- from MY_DEQUE
                strictly_positive_capacity: maximum_capacity > 0
            ensure -- from MY_DEQUE
                has_given_capacity: capacity = maximum_capacity
                empty: empty

    feature -- Access

        top: G
                -- The last item in the container.
                -- (from MY_DEQUE)
            require -- from MY_DEQUE
                not_empty: not empty

    feature -- Element change

        push (an_item: G)
                -- Make an_item the last one.
                -- (from MY_DEQUE)
            require -- from MY_DISPENSER
                not_full: not full
            ensure -- from MY_DISPENSER
                one_more: depth = old depth + 1
            ensure then -- from MY_DEQUE
                item_is_last: top = an_item

    feature -- Initialization

        make (maximum_capacity: INTEGER)
                -- Make an empty deque with the given capacity.
                -- (from MY_DEQUE)
            require -- from MY_DEQUE
                strictly_positive_capacity: maximum_capacity > 0
            ensure -- from MY_DEQUE
                has_given_capacity: capacity = maximum_capacity
                empty: empty

    feature -- Measurement

        capacity: INTEGER
                -- Current number of items stored.
                -- (from MY_DEQUE)
            ensure -- from MY_DISPENSER
                strictly_positive: Result > 0

        depth: INTEGER
                -- Current number of items stored.
                -- (from MY_DEQUE)

    feature -- Removal

        pop
                -- Remove last item.
                -- (from MY_DEQUE)

        clear
                -- Remove all items, if any.
                -- (from MY_DEQUE)
            ensure -- from MY_DISPENSER
                empty: empty

    feature -- Status report

        empty: BOOLEAN
                -- Are there no items stored?
                -- (from MY_DISPENSER)

        full: BOOLEAN
                -- Is there no room to insert new items?
                -- (from MY_DISPENSER)

    invariant

            -- from GENERAL
        reflexive_equality: standard_is_equal (Current)
        reflexive_conformance: conforms_to (Current)
            -- from MY_DISPENSER
        capacity_large_enough: capacity > 0
        count_large_enough: depth >= 0
        count_small_enough: depth <= capacity
        consistent_empty: empty = (depth = 0)
        consistent_full: full = (depth = capacity)

    end -- class MY_STACK

Ace.ace (listing 6) is a file written in LACE that specifies how to build the example.

    system

        TEST

    root

        TEST (root_cluster): "make"

    default

        assertion (all);
        precompiled ("$EIFFEL4/precomp/spec/$PLATFORM/base")

    cluster

        root_cluster: ".";
        access: "$EIFFEL4/library/base/structures/access";
        cursors: "$EIFFEL4/library/base/structures/cursors";
        cursor_tree: "$EIFFEL4/library/base/structures/cursor_tree";
        dispenser: "$EIFFEL4/library/base/structures/dispenser";
        iteration: "$EIFFEL4/library/base/structures/iteration";
        kernel: "$EIFFEL4/library/base/kernel";
        list: "$EIFFEL4/library/base/structures/list";
        set: "$EIFFEL4/library/base/structures/set";
        sort: "$EIFFEL4/library/base/structures/sort";
        storage: "$EIFFEL4/library/base/structures/storage";
        support: "$EIFFEL4/library/base/support";
        table: "$EIFFEL4/library/base/structures/table";
        traversing: "$EIFFEL4/library/base/structures/traversing";
        tree: "$EIFFEL4/library/base/structures/tree";

    end -- system TEST

5. Software Process Quality

Eiffel places great emphasis on product quality - what is is and direct support to achieve it - while offering relatively little advice on the software process (roughly 10 of 1200+ pages in OOSC2 [2].) In this way, the Eiffel Methodology, differs from, for example, the Booch Macro and Micro Development Processes [10] or the Software Engineering Institute's Capability Maturity Model or Watts Humphrey's Personal Software Process [11]. (However, Eiffel strongly supports key aspects of these such as documentation, testing and evolution.)

5.1 Reusability Culture Principle:

1. Develop all software under the assumption that it will be reused.

2. Do not trust that any software will be reusable until you have seen it reused.

Eiffel's raison d'etre is the development of reusable software components. Therefore Eiffel augments the basic mini-life-cycle iteration loop:

1. Specification

2. Design

3. Implementation

4. Verification & Validation

with a crucial step:

5. Generalization: generalize the classes so they can be reused in other applications.

Concurrent Engineering Through Clusters

Cluster: a group of related classes or, recursively, of related clusters.

Clusters are Eiffel's mechanism for organizing software into manageable relatively independent units suitable for development by individuals.

Eiffel supports multiple independent integration of clusters through LACE (Language for the Assembly of Classes in Eiffel) to allow concurrent development, updating and testing of subsystems (clusters of clusters).

5.2 Seamlessness

A software development process which uses a uniform method and notation throughout all activities, such as problem modeling and analysis, design, implementation and maintenance.

The Eiffel text notation is suitable for all phases of development. However there is a graphical notation - BON (Business Object Notation) - that is so closely tied to the text notation that changes to either are reflected in the other automatically in the Eiffel Environment.

5.3 Reversible:

A software development process that lets insights gained in later phases affect the results obtained in earlier phases.

More often than developers would like to admit, the need to make important changes can be discovered late in the development process. Rather than treat this as a failure in the analysis or design phases, Eiffel not only recognizes this reality but actually supports it through its high-level text notation and interactive CASE tools.

Traditional texts on software development encourage the clear separation of roles: analysts, designers, coders, etc. and downplay the importance of the implementation phase. Eiffel recognizes the important discoveries and contributions that are made during implementation. Some discoveries can have profoundly positive affects on design (e.g., Command Classes).

5.4 Quality First Process Model

It is covered in more detail in a separate document - Design By Contract: A Missing Link In The Quest For Quality Software. Quality First is described in section 2.5.

This is made possible by adopting the following incremental bottom-up discipline supported by the Eiffel Environment:

Get the cosmetics right first and maintain it.

Syntax: Syntax errors should be trapped as soon as possible: when they are typed.

Style: Eiffel includes a well-developed style guide that addresses all aspects of the written text. After all, text is first and foremost for humans to read (and re-read many times over - "Write Once Read Many").

Continuously Compile Incremental Changes.

The Eiffel Environment includes an incremental byte-code compiler that makes it feasible to keep the software compiled at nearly all times.

Continuously Execute.

The Eiffel Environment Interpreter allows you to maintain a 'current demo' at all times.

6. How the alternatives fall-short compared to Eiffel

We have defined software quality and modularity and seen how Eiffel supports these through its methodology (principles), language (features) and environment (tools). Now we will briefly list the major short-comings of alternative non-Eiffel approaches.

6.1 Smalltalk

Inefficient: Smalltalk is interpreted which makes it notoriously inefficient in execution speed compared to compiled languages.

Unreliable: Smalltalk is dynamically-typed which makes it difficult to construct reliable, correct software because 'mismatched type errors' are not discovered until runtime. In statically-typed languages, such errors are caught at compile-time.

Lacks multiple inheritance, making it difficult to model problem domain solutions naturally.

Lacks genericity (parameterized types), making it impossible to employ efficient static polymorphism.

6.2 C++

C++ has many flaws, most originating from its C compatibility requirement, design philosophy, and development history.

Some major problem areas include:

Poor Readability:

C++ inherits C's cryptic, terse syntax and expands it with even more punctuation to master.(E.g., , (comma operator), ++, <<, &&, ||, %, ~=, etc.)

Complexity:

Mastering C++ is difficult, even for expert C programmers, largely because the language harbors many subtle interaction complexities in areas such as inheritance, templates, exceptions, object construction and destruction (C++ lacks automatic memory management "garbage-collection") [17]. (See 'Obfuscated C++' column - last page in each C++ Report. It presents strange C++ code and challenges readers to guess what the program does! [17]

Poor Portability:

Lack of availability of standard/compatible compilers and libraries across multiple platforms makes porting C++ code a nightmare. A C++ compiler for the Cray (when available) costs $25,000!

Lack of standardized libraries.

Although C++ offers excellent interoperability with C libraries, the lack of standard, compatible C++ libraries results in a plethora of partially redundant, mutually incompatible home-grown and commercial libraries.

Problematic Single Inheritance:

In C++, the programmer is constantly forced to make trade-offs between efficiency and reusability when deciding whether or not to use inheritance. The implementation cost of inheritance (a virtual table pointer per object and extra level of indirection upon dispatch) is unavoidable once inheritance is chosen. In Eiffel, the compiler has the responsibility of recognizing and optimizing 'static' dispatch.

Problematic Multiple Inheritance:

C++'s multiple inheritance semantics are notoriously flawed - which explains why it is avoided by most C++ programmers.

There are numerous papers on this subject. Here is one available on-line: (Compares C++ to Eiffel and Java:) http://www.progsoc.uts.edu.au/~geldridg/cpp/cppcv3/

(Note: perhaps the only advantages of C++ over Eiffel are somewhat better efficiency and a more general and powerful genericity model.)

6.3 Java

Java can be though of as "C++ -- ++". That is, Java is C++ with some features removed and some features added. Java is in many ways a significant improvement over C++ however, it also inherits some flaws from its ancestors: C++ and Smalltalk and has been described as having "the elegant simplicity of C++ and the blazing speed of Smalltalk":

Poor Readability: Java uses the same cryptic, terse C syntax (e.g., {,}, >>>, &&, !=, ==, etc.).

Lack of genericity:

Java lacks the 'parameterized typing' of C++ opting instead for weak typing (casting of untyped void pointers) - undoubtably a Smalltalk influence. This significantly reduces reliability and efficiency. (Note: proposals have been put forth to introduce genericity to the language, but it is unclear if and when this will occur.)

Lack of assertions:

By removing the C pre-processor (a good step) Java now lacks a built-in mechanism for supporting efficient 'Design By Contract'. (Though iContract, an external third-party tool that provides support for DBC, is very useful, it cannot help with Java Standard Library code or code received from other parties [19].)

Poor Reliability: lack of genericity and assertions, as well as other 'holes' in the language specification, make it difficult to engineer reliable, efficient software.

Inefficient: Java's byte-code interpreting cannot compete with the execution speed of true compiled languages. (Though Just-In-Time (JIT) native compilers will reduce this problem for some platforms.)

Immature compilers and standardized libraries.

Java, like any new language, suffers from a lack of standard/compatible compilers and libraries. (However, the Java community seems to be moving toward standardization much faster than the C++ community was able to do.) When will Java be available on Crays?

6.4 CLOS

The Common Lisp Object System is an object-oriented foundation built on top of CommonLisp. CLOS is a very powerful and flexible programming tool that makes it a superior solution for certain classes of problems (e.g., AI research, programming language design, etc.). However it has some features which negatively impact the goals addressed by a tool like Eiffel:

Poor Readability: Lisp syntax is very simple yet notoriously difficult to read. (Lisp may be the most 'writable' language but it is not easy to comprehend. "Write Once, Read Never".)

Inefficiency: Lisp is a dynamically typed, interpreted language and despite some use of 'tagged' type hints, it is difficult to write CLOS programs that achieve the runtime speed of equivalent C code. (Exceptions to this include the Connection Machine in which a flow solver written in 'Star Lisp' achieved 1.3 GFLOPS - a world record at the time.)

Poor Reliability: Lisp's dynamic typing also makes it vulnerable to runtime 'type-mismatch' errors.

Poor Portability: Commercial-quality, compatible implementations will be hard to find for a variety of platforms (including Crays).

Complexity: CommonLisp is a notoriously large language to attempt to master. Even other Lisp advocates, such as the Scheme community, have avoided CommonLisp like the plague.

6.5 Ada

Ada was designed to meet many of the same goals as Eiffel. In fact, Ada was a major influence on Eiffel. But Ada was originally not an object-oriented language. As a result it suffers from some of the problems of a 'hybrid language' with ties to a procedural past:

Complexity:

There are too many constructs in this very large language designed by committee. Ada shops often set-up guidelines that advise (or require) programmers to avoid numerous 'problem areas' in the language.

Lack of disciplined assertions (integrated into exception handling):

Standard Ada 95 does not include the 'Anna' assertions package. This prevents Design by Contract and its benefits in program documentation and reliability. (This has been cited as a cause of a recent failed rocket launch.)

6.6 Sather

Sather, a close derivative (cousin) of Eiffel, is perhaps its best challenger. Sather is a research project at the University of California Berkeley's International Computer Science Institute [20]. It has similar goals and many of the same advantages that Eiffel has over other alternatives (including ANSI-C portability). It has been used for some research projects in scientific computing and parallel processing applications at various places including: UCB, Oak Ridge National Lab, Karlsruche University (Germany) and even at the EPA (Mark Bolstad has some experience with Sather for parallel rendering applications). Sather has been described as "Eiffel for Ph.D's".

More information on Sather is available at: http://www.icsi.berkeley.edu/~sather/

Possible Sather advantages with respect to Eiffel:

Free: The entire Sather system (compiler, libraries, emacs files, class browser, runtime garbage collector, etc.) is available for downloading from the Sather WEB page. (Note: a free Eiffel compiler, SmallEiffel, is also available.)

Iterators: Powerful iteration abstractions unique to Sather.

Supertyping construct: This construct facilitates code reuse by effectively merging different inheritance hierarchies.

Parallel constructs: Sather has constructs for distributed/parallel programming. (Note: Eiffel will introduce one new keyword, 'separate', that will apparently allow for all forms of parallel programming - known as SCOOP (Simple Concurrent Object-Oriented Programming.)

Excellent C and FORTRAN interfacing.

Focus on high-performance parallel scientific applications. (Though in some local benchmarks Eiffel has out-performed Sather.)

Sather disadvantages with respect to Eiffel:

Less readability due to more terse syntax. More use of operators and abbreviated keywords (<, $ELT!, $CPX_NUMBER, etc.)

Incomplete assertions. Sather has assertions for pre/post/invariant checking but not in abstract classes. (This is part of on-going research in Sather's contra-variant approach.)

Limited libraries. Sather includes class libraries for IO, basic containers, Math, Tcl/Tk Widgets, parallel programming, etc. but does not compare to Eiffel's extensive libraries of hundreds of high-quality classes.

Unstable: The future of Sather depends on the research goals of forthcoming graduate students at UCB.

7. Conclusions

Eiffel is a comprehensive approach to the development of high-quality software. It includes a methodology, a language and an environment. It is a proven and portable technology that strongly supports the requirements of large-scale team-oriented development in many application areas, from embedded systems to distributed enterprise applications.

The Eiffel language includes many important features such as:

Eiffel can serve as an excellent component combinator - wrapping and combining proven software components developed in languages other than Eiffel. This enables development teams to incorporate external code, such as Fortran libraries for numerical computation, into a flexible OO architecture. This is far better than trying to implement OO features in a non-OO language or even in another OO language that is less supportive of software quality than Eiffel.

Of course, the quality of the final result is limited by the quality of the wrapped component too - the "weakest link" notion - so it is important to ensure that components selected for wrapping be of verified high quality.

Eiffel is a comprehensive discipline that ambitiously targets many key aspects of software development. It fosters, indeed requires, a shift in mindset that prioritizes certain qualities - correctness, robustness, understandability, testability, extendibility, reusability over others, such as functionality. That is, the Quality First approach to development recognizes that some qualities simply cannot be retrofitted - they must be built-in from the beginning and upheld throughout the entire development process while others, such as functionality, naturally increase over time.

This is not a small step but it is not a difficult one either. Eiffel goes far beyond paying lip service to good software engineering principles. It actively supports, and even enforces, them like no other development approach in widespread use. And it does so effortlessly in a seamless integrated environment, unlike other approaches that require significant extra-linguistic mechanisms to try and inject and uphold quality. Eiffel's elegant simplicity makes it significantly easier to master than other OOPLs. The introduction of Eiffel as a mainstream development approach has "raised the bar" on what constitutes "best practices".

Eiffel's technical superiority is clear. The question is not "Why Eiffel?" but"Why not Eiffel?" On your next project, why not rise above the popular norm and embrace a superior approach? No budget? Download the very inexpensive PC versions or try the free compilers. Need more info? Check-out the Web references below. When you are ready to embrace Quality First, pick-up a copy of Object-Oriented Software Construction, read it and pass it around to your fellow developers. You will undoubtably be on the road to significantly improving your software development practice.

8. More on Eiffel

See the ISE (Interactive Software Engineering) Web page: http://www.eiffel.com/

See the links:

" Eiffel in a Nutshell"

" 1 minute summary"

" Why your next project should use Eiffel"

" All about ISE Eiffel"

Eiffel World: http://www.eiffel.com/doc/eiffelworld/

The Eiffel Liberty Daily: http://www.elj.com/new/

The Eiffel Page: http://www.cm.cf.ac.uk/Eiffel/

The Eiffel Forum: http://www.eiffel-forum.org/

Object Tools Visual Eiffel: http://www.object-tools.com/

Halstenbach Eiffel: http://www.halstenbach.com/

Free GNU/SmallEiffel Compiler: http://www.loria.fr/projets/SmallEiffel/

elj-win32: a free win32 distribution of the GNU/SmallEiffel Compiler: http://www.elj.com/elj-win32/

SEEd: a free IDE for elj-win32: http://www.elj.com/elj-win32/seed/

Eiffel Forum Archive: http://www.eiffel-forum.org/archive/

Eiffel Liberty: http://www.elj.com/

Jive - Eiffel-to-Java Compiler: http://www.spin.ch/~kalberer/jive/

Project Bruce - Eiffel-to-Java Compiler: http://www.elj.com/elj/v1/n1/jp/

Eiffel vs Java: http://www.zdnet.com/pcmag/insites/dvorak_print/jd971202.htm

Eiffel vs C++ vs Java: http://www.elj.com/cppcv3/

References

  1. Meyer, Bertrand, Object-Oriented Software Construction, Second Edition, Prentice Hall, Upper Saddle River, New Jersey, 1997, pp. 3-19, 39-64.
  2. ___, pp 923 - 934.
  3. ___, pp 875 - 902.
  4. ___, Object Success: A Manager's Guide to Object Technology, its Impact on the Corporation, and its Use for Reengineering the Software Process, Prentice Hall, 1994.
  5. ___, "Lessons From The Design Of The Eiffel Libraries," Communications of the ACM, vol. 33, no. 9, September 1990, pp. 68-88.
  6. ___, "Practice To Perfect: The Quality First Model," IEEE Computer, vol. 30, no. 5, May 1997, pages 102-106.
  7. ___, " The Many Faces Of Inheritance: A Taxonomy Of Taxonomy," IEEE Computer, vol. 29, no. 5, May 1996, pages 105-108.
  8. Corbato, Fernando J., "On Building Systems That Will Fail," Communications of the ACM, vol. 34, no. 9, September 1991, pp. 72-90.
  9. McConnell, Steve, "The Art, Science, and Engineering of Software Development," IEEE Software, vol. 15, no. 1, January 1998, pp. 118 - 120.
  10. Booch, Grady, Object-Oriented Analysis And Design With Applications, Second Edition, Benjamin Cummings, Redwood City, 1994, pp. 229 - 290.
  11. Humphrey, Watts, A Discipline for Software Engineering, Addision-Wesley, Reading, MA, 1995.
  12. Walden, Kim and Jean-Marc Nerson, Seamless Object-Oriented Software Architecture: Analysis And Design Of Reliable Systems, Prentice Hall, Hempel Hempstead (UK), 1995.
  13. Plessel, Todd, " Design By Contract: A Missing Link In The Quest For Quality Software," 1998.
  14. Gamma, Erich, et al., Design Patterns: Elements of Reusable Object-Oriented Software, Addision-Wesley, Reading MA, 1995.
  15. Korson, Tim and John D. McGregor, "Understanding Object-Oriented: A Unifying Paradigm," Communications of the ACM, vol. 33, no. 9, September 1990, pp. 40-60.
  16. Martin, Robert, "OO Design Quality Metrics," September, 1995. http://www.oma.com/PDF/oodmetrc.pdf.
  17. Muller, Harald M., "Ten Rules For Handling Exception Handling Successfully," C++ Report, vol. 8, no. 1, January 1996.
  18. Papurt, David M., "The Use Of Exceptions," The Journal of Object-Oriented Programming, Vol. 11, No. 2, pp 13-17,32, May 1998.
  19. Kramer, Reto, "iContract - The Java Design By Contract Tool," http://www.promigos.ch/kramer
  20. Omohundro, Stephen and David Stoutamire, "Sather 1.1," 1996. http://www.icsi.berkeley.edu/~sather/Documentation/Specification/Sather-1.1/.
[3rd edition of the C++ Critique is now available ``including comparisons with Java and Eiffel''.. Ian Joyner (4 Nov 96)]
(Ed: Banner provided as an OO Community Service.)

[ Eiffel Liberty (New) | GUERL | sOOap ] [ Top ]

Page is http://www.elj.com/eiffel/why-eiffel/
Last Modified: 02 Dec 1998 (Created: 02 Dec 1998)