Previous | Table of Contents | Next |
This implementation relies on the loop construct described later (from introduces the loop initialization) and on deferred features of the class that allow traversal of a list based on moving a fictitious cursor: start to bring the cursor to the first element if any, after to find out whether all relevant elements have been seen, and forth (with precondition not after) to advance the cursor to the next element. For example, forth appears as
forth is -- Advance cursor by one position require not_after: not after deferred ensure moved_right: index = old index + 1 end
where indexanother deferred feature of the classis the integer position of the cursor.
Although the preceding version of feature count is time-consumingit implies a whole traversal just for the purpose of determining the number of elementsit has the advantage of being applicable to all variants, without any commitment to a choice of implementation, as would follow, for example, if we decided to treat count as an attribute. Proper descendants can always redefine count for more efficiency.
Function count illustrates one of the most important contributions of the method to reusability: the ability to define behavior classes that capture common behaviors (such as count) while leaving the details of the behaviors (such as start, after, and forth) open to many variants. As noted earlier, traditional approaches to reusability provide closed reusable components. A component such as LIST, although equipped with directly usable behaviors such as count, is open to many variations, to be provided by proper descendants.
A class B inheriting from a deferred class A may provide implementationseffective declarationsfor the features inherited in deferred form. In this case, there is no need for the equivalent of a redefine subclause; the effective versions simply replace the inherited versions. The class is said to effect the corresponding features. If after this process, there remain any deferred features, B is still considered deferred, even if it introduces no deferred features of its own, and must be declared as deferred class.
In the example, classes such as LINKED_LIST and ARRAYED_LIST effect all the deferred features they inherit from LISTextend, start, and so onand hence be effective.
Note thatexcept in some applications restricted to pure system modelingdeferred classes and features only make sense thanks to polymorphism and dynamic binding. Because extend has no implementation in class LIST, a call of the form my_list.extend (...) with my_list of type LIST [T] for some T can only be executed if my_list is attached to a direct instance of an effective proper descendant of LIST, such as LINKED_LIST; then it uses the corresponding version of extend. Static binding would not even be meaningful here.
Even an effective feature of LIST such as count may depend on deferred features (start and so on) so that a call of the form my_list.count can only be executed in the context of an effective descendant.
All this indicates that a deferred class must have no direct instance (it has instances, the direct instances of its effective descendants). If it had any, we could call deferred features on them, leading to execution-time impossibility. The rule that achieves this goal is simple: If the base type of x is a deferred class, no creation instruction of target x, of the form !! x ..., is permitted.
Deferred classes cover abstract notions with many possible variants. They are widely used in Eiffel where they cover various needs:
As the reader will have noted, these applications make deferred classes a central tool of the Eiffel methods support for seamlessness and reversibility. The last one in particular uses deferred classes and features to model objects from an application domain, without any commitment to implementation, design, or even software (and computers). Deferred classes are the ideal tool here: They express the properties of the domains abstractions, without any temptation of implementation bias, yet with the precision afforded by type declarations, inheritance structures (to record classifications of the domain concepts), and assertions to express the abstract properties of the objects being described.
Unlike approaches using a separate object-oriented analysis and design method and notation (Booch, OMT, UML, and so on), this technique integrates seamlessly with the subsequent phases (assuming the decision is indeed taken to develop a software system): It suffices to develop the deferred classes progressively by introducing effective elements, either by modifying the classes themselves or by introducing design- and implementation-oriented descendants. In the resulting system, the classes that played an important role for analysis, and are the most meaningful for customers, remain important; as we have seen (section 9.3.2) this direct mapping property is a great help for extendibility.
Previous | Table of Contents | Next |