You get a bonus - 1 coin for daily activity. Now you have 1 coin

2. Criteria of Object Orientation

Lecture



On Criteria

We shall limit ourselves to a minimum of explanation, so on a first reading you should not expect to grasp the details of all the criteria listed; explaining them is the task of the book's remaining chapters. You may think of this discussion as a preview screening — not the real movie, but a trailer. Unlike a trailer, though, this lecture is closer to what is called a spoiler — it retells the plot, at times disrupting the book's overall plan. In this it differs from the other lectures, especially lectures 3-6, which patiently build up object technology, examining problem after problem on the way to obtaining and justifying a solution. If you like the idea of a survey preceding an in-depth study of the issues, this lecture is for you. But if you prefer not to spoil the pleasure of discovering the solutions one by one, simply skip it.

Let us consider the choice of criteria that allow us to assess a system's object orientation (objectness).

To what extent should we be dogmatic?

The list given below includes all the properties that seem essential for producing high-quality software with the OO method. Our list may seem uncompromising, even dogmatic. What conclusions should be drawn if an environment satisfies some, but not all, of these criteria? Should it be considered entirely inadequate?

Only you, my reader, can answer this question for your own context. Here are a few reasons why a compromise may be necessary:

[x]. Being an OO system is not a Boolean condition. Of two environments A and B, the first may be more object-oriented while still not being 100% so. So if external constraints narrow your choice down to just A and B, you should choose A as the lesser of two evils.

[x]. Not everyone always needs every property.

[x]. Object orientation may be just one of the factors driving our decision, so a balance must be struck between the criteria given here and other considerations.

None of this changes the obvious: making an informed choice, even when practical constraints force far-from-perfect solutions, requires seeing the full picture.

Categories

The set of criteria is divided into three parts:

[x]. Method and Language: these two almost indistinguishable aspects cover the thought processes and the notation used for the analysis, design, and programming of software. Note that (especially in object technology) the term "language" refers not only to a programming language in the strict sense, but also to the languages of analysis and design and the notation, textual or graphical, used in them.

[x]. Implementation and Environment: the criteria in this category describe the basic properties of the tools that let developers apply OO ideas.

[x]. Libraries: object technology is based on the reuse of software components. The criteria in this category describe both the availability of basic libraries and the mechanisms needed to use them and to create new libraries.

Such a division is convenient but not absolute, since some criteria belong to two or three categories at once. For example, the criterion labeled "memory management" belongs to the language category, since a language may or may not support automatic garbage collection. This same criterion also belongs to the implementation and environment category.

Method and Language

The first set of criteria relates to the method and the notation that supports it.

Seamlessness

The OO approach is ambitious: it covers the entire software life cycle. When considering OO solutions, one should verify that the method, the language, and the tools supporting them are applicable to analysis and design as well as to implementation and maintenance. The language, in particular, must serve as a tool for thought, helping at every stage of the work.

The result is a seamless development process, in which the commonality of concepts and notation helps smooth the transitions between successive stages of the life cycle.

These requirements rule out two frequently encountered cases — both unsatisfactory:

[x]. Using OO concepts at the analysis and design stage with a notation that cannot be used at the programming stage.

[x]. Using an OO programming language that is unsuited to the analysis and design stage.

An OO language and an OO environment, together with the method that supports them, must be applicable to the entire life cycle, minimizing the complexity of the transitions between successive steps.

Classes

The OO method is based on the notion of a class. Informally, a class is a software element describing an abstract data type and its partial or full implementation. An abstract data type is a set of objects defined by a list of components (features) — the operations applicable to those objects and their properties.

The notion of a class must be the central concept of the method and the language.

Assertions

The components of an abstract data type have formally specified properties, reflected in the corresponding classes.

Assertions — preconditions and postconditions of a class's routines, and class invariants — play this role.

Assertions have three main uses: they help build reliable software, they provide systematic documentation, and they serve as a tool for testing and debugging software.

The language must make it possible to: supply a class and its components together with assertions (preconditions, postconditions, and invariants); include tooling for producing documentation from these assertions; and monitor assertions during program execution.

In the community of software modules, where classes are the cities and instructions are the executive branch of power, assertions represent the legislative branch. Below we shall see what plays the role of the judicial system in such a community.

Classes as modules

Object orientation is, first and foremost, an architectural technique: it primarily concerns the modular structure of a system.

Here again the role of classes is great. A class describes not only a type of objects, but also a modular unit. In the pure OO approach:

Classes must be the only kind of module.

In particular, the notion of a main program disappears, and routines do not exist as independent modular units (they can only appear as part of classes). There is no need for "packages" as used in languages such as Ada. Although for management purposes it is convenient to group classes into administrative units called clusters.

Classes as types

The notion of a class is powerful enough to avoid the need for any other typing mechanism:

Every type must be based on a class.

Even basic types such as INTEGER and REAL can be regarded as classes; such classes are usually built in.

Component-based computation

In OO computation there is only one basic computational mechanism. There is some object, which by the preceding rule is always an instance of some class, and computation consists in this object calling some component of that class. For example, to display a window on the screen, the display component of the object representing the window — an instance of class WINDOW — is called. Components can have arguments: to raise the salary of an employee e, on date d, by n dollars, the raise component of object e is called, with arguments n and d.

Basic types are treated as predefined classes, and the basic operations (for example, adding numbers) are treated as special predefined cases of component calls — the general computation mechanism:

Component call must be the basic mechanism of computation.

A class containing a call to a component of class C is called a client of class C.

A component call is sometimes called message passing; in this terminology, the call above would be described as sending object e the message: "raise your salary" with arguments d and n.

Information hiding

When creating a class, it is often necessary to include a component that is needed only for internal purposes, that is part of the class's implementation but not of its interface. Other components of this class — possibly accessible to clients — may call this internal component for their own needs. But this possibility should not be given to the client.

A mechanism that makes certain components unavailable to clients is called information hiding.

In practice it is not enough for the information hiding mechanism to support exported components (accessible to all clients) and hidden components (accessible to no client).

The creators of classes must also be able to selectively export components to selected clients.

The author of a class must be able to specify that a component is available: to all clients, to no client, or to selected clients.

A direct consequence of this rule is the strict limitation of interaction between classes. In particular, a good OO language should not include the notion of a global variable. Classes exchange information solely through component calls and the inheritance mechanism.

Exception handling

Various anomalies can occur while a program is running. In OO computation these correspond to calls that cannot be executed properly: for example, as a result of a hardware failure, an overflow during an arithmetic operation, or software errors.

To build reliable software, it must be possible to recover the normal course of computation. This is the purpose of the exception handling mechanism.

The language must provide a recovery mechanism for unexpected abnormal situations.

In the community of software modules, the exception handling mechanism is the third branch of power — the judicial system and the police force that supports it.

Static typing

When a certain object in a system calls some component, how do we know that the object is able to handle the call? (In message terminology: how do we know that the object can handle the message?)

To guarantee correct execution, the language must be typed. This means that it obeys several compatibility rules:

[x]. Every entity is explicitly declared with a specific type, generated by a class. An entity is understood as a name used in the software text to refer to run-time objects.

[x]. Every component call is a call to an accessible component of the corresponding class.

[x]. Assignment and argument passing obey conformance rules, requiring compatibility between the source type and the target type.

In a language that includes such a policy, static type checking is possible. Then, already at compile time, such errors will be detected, and at run time the absence of errors of the kind "component not available to object" is guaranteed.

A well-defined type system guarantees the safety of working with objects while the software system is running.

Genericity

For typing to be practical, it must be possible to define classes with parameters that specify a type. Such classes are known as generic. The generic class LIST [G] describes lists of elements of an arbitrary type G — the "formal generic parameter".

Classes that specify particular lists will be its derivatives; for example, LIST [INTEGER] and LIST [WINDOW] use the types INTEGER and WINDOW as "actual generic parameters". All derived classes share the same text of the generic class.

It must be possible to create classes with formal generic parameters representing arbitrary types.

This form of type parameterization is called unconstrained genericity. An additional facility, described below, is constrained genericity, which uses the notion of inheritance.

Single inheritance

Software development involves creating a large number of classes, many of which are variants of previously created classes. Managing the potential complexity of such a system requires a classification mechanism known as inheritance. Class A is an heir of class B if it embeds (inherits) the components of class B in addition to its own. A descendant is a direct or indirect heir; the converse notion is an ancestor.

It must be possible to declare a class as the heir of another class.

Inheritance is one of the central notions of the OO method; it has a major influence on the software development process.

Multiple inheritance

It is often necessary to combine different abstractions. Consider a class modeling the notion of "infant". It can be viewed as a class "person" with the components associated with that class. It can also be viewed more prosaically — as a class "item subject to taxation", entitled to deductions when taxes are computed. Inheritance is justified in both cases. Multiple inheritance is the guarantee that a class can be the heir not just of one class, but of many, whenever this is conceptually justified.

Multiple inheritance raises several technical problems, for example the resolution of name clashes (components inherited from different classes having the same name). Any notation that offers multiple inheritance must provide an adequate solution to these problems.

A class must be able to be the heir of several classes.

Name clashes under inheritance are resolved by an adequate mechanism.

The solution developed in this book is based on renaming the conflicting components in the heir class.

Repeated inheritance

Under multiple inheritance, a situation of repeated inheritance arises when a certain class becomes an heir of the same class multiple times, by way of different inheritance branches:

2. Criteria of Object Orientation

Fig. 2.1. Repeated inheritance

In this case the language must provide precise rules determining what happens to components inherited repeatedly from a common ancestor (in the figure, this is A). In some cases it is desirable for a component from A to produce only a single component in D (sharing), while in others it should produce two (duplication). Developers must have flexible means allowing them to prescribe one of these possibilities independently for each component.

Under repeated inheritance, the fate of the components must be governed by precisely defined rules that let developers choose, for each such component, either sharing or duplication.

Constrained genericity

Combining genericity and inheritance yields a useful technique — constrained genericity. Now you can define a class with a generic parameter that represents not an arbitrary type, but only a type that is a descendant of a given class.

The generic class SORTABLE_LIST describes lists; it contains a component sort, which sorts the elements of the list according to a given order relation. The parameter of this generic class specifies the type of the list's elements. But this type cannot be arbitrary: it must support an order relation. The actual generic parameter must be a descendant of the library class COMPARABLE, which describes objects equipped with an order relation. Constrained genericity allows us to declare our generic class as follows: SORTABLE_LIST [G -" COMPARABLE] .

The genericity mechanism must support a form of constrained genericity.

Redefinition

When a class is the heir of another class, it may be necessary to change the implementation or other properties of some of the inherited components. A class SESSION, which manages user sessions in an operating system, might have a component terminate that performs cleanup at the end of a session. Its heir might be a class REMOTE_SESSION, which manages a session on a remote computer over a network.

If terminating a remote session requires additional actions, such as notifying the remote computer, the class REMOTE_SESSION will redefine the component terminate.

Redefinition can affect a component's implementation, its signature (the type of its arguments and result), and its specification.

It must be possible to redefine the specification, signature, and implementation of an inherited component.

Polymorphism

Under inheritance, the static typing requirement discussed above would become too restrictive if it meant that every entity of type C could only be attached to an object of exactly that same type C. For example, in a navigation control system, an entity of type BOAT could not be used for objects of class MERCHANT_SHIP or SPORTS_BOAT, even though both classes are descendants of class BOAT.

As already noted, "entity" is a name to which various values may become attached during execution. An entity is a generalization of the traditional notion of a variable.

Polymorphism is the ability to attach objects of various possible types to an entity. In a statically typed environment, polymorphism will not be arbitrary but will be governed by inheritance.

It must be possible, at run time, to attach to an entity objects of various possible types, under the control of inheritance.

Dynamic binding

Combining the last two mechanisms, redefinition and polymorphism, directly leads to the following mechanism. Suppose there is a call whose target is a polymorphic entity, for example an entity of type BOAT calls the component turn. Various descendants of class BOAT may have redefined this component in different ways. Clearly, there must be an automatic mechanism guaranteeing that the version of turn used always matches the actual type of the object, regardless of how the entity was declared. This capability is called dynamic binding.

A call by an entity to a component must always trigger the component that matches the type of the attached object, not the type of the entity.

Different executions of the same call may trigger different components.

Dynamic binding has a major influence on the structure of an OO application, since it lets developers write simple calls, for example an object my_boat calls the component turn. In reality, this call stands for several possible calls, depending on the corresponding run-time situations. This does away with the need for the many repeated checks (is the object a merchant_ship? Is it a sports_boat?) that flood the software products built with conventional approaches.

Determining an object's type at run time

Developers of OO software soon develop a healthy distaste for any style of computation based on explicitly choosing among different object types. Polymorphism and dynamic binding are far preferable. In some cases, however, an object arrives from outside, so that the author of the software has no way of predicting its type with certainty. In particular, this happens if the object is retrieved from external storage, received over a network, or passed in by some other system.

The software then needs a mechanism providing a safe way to access the object without violating the constraints of static typing. Such a mechanism must be designed with great care, so as not to lose the benefits of polymorphism and dynamic binding.

The assignment attempt operation satisfies these requirements. It is a conditional operation: it attempts to attach an object to an entity; if, when the operation is executed, the object's type matches the entity's type, it behaves like a normal assignment; otherwise the entity receives the special value void. Thus one can manipulate objects whose type is not known for certain, without compromising the safety of the type system.

It must be possible to determine at run time whether the type of an object conforms to a statically given type.

Deferred features and classes

In some cases where dynamic binding provides an elegant solution, removing the need for explicit checks, there is no initial version of the component to be redefined. For example, class BOAT may be so general that it cannot provide a default implementation of turn. Yet we want to be able to call the component turn on an entity of type BOAT, if we are certain that at run time it will receive the value of objects of fully defined types such as MERCHANT_SHIP and SPORTS_BOAT.

In such cases, BOAT can be declared as a deferred class (a class that is not fully implemented) with a deferred implementation of the component turn. Deferred features and classes can still have assertions describing their abstract capabilities, but their implementation is postponed to descendant classes. If a class is not deferred, it is considered effective.

It must be possible to write a class or component as deferred, that is, specified but not fully implemented.

Deferred classes (also called abstract classes) are especially important for OO analysis and high-level design, since they make it possible to specify the essential aspects of a system while leaving the details for a later stage.

Memory management and garbage collection

It might seem that this criterion of method and language should belong to the next category — implementation and environment. In fact it belongs to both categories. The most essential requirements are placed on the language; the rest is a matter of good engineering.

OO systems, even more than traditional systems — with the possible exception of Lisp — tend to create a large number of objects, sometimes with complex interdependencies. A policy that places responsibility for memory management on developers harms both the efficiency of the development process and the safety of the resulting system. It becomes difficult to reclaim memory occupied by objects that are no longer needed, programs become more complicated, all of this takes up developers' time, and the risk of mishandling memory areas increases. In a good OO environment, memory management will be automatic, under the control of a garbage collector — a component of the runtime system.

Automatic garbage collection is a matter of the language just as much as of the implementation. If a language is not explicitly designed for automatic memory management, implementation often becomes impossible. This is true for languages where, for example, a pointer to an object of a given type can be converted (using a cast) into a pointer of another type or even into an integer — such facilities make it impossible to build a reliable garbage collector.

The language must make reliable automatic memory management possible, and the implementation must provide an automatic manager that handles memory, whose function includes garbage collection.

Implementation and Environment

We now turn to important properties of the development environment that supports the creation of OO software.

Automatic update

Software development is an incremental process. Developers do not usually write thousands of lines at once; they work by adding and modifying, most often starting from a system that already has a substantial size.

When performing such an update, it is important to have a guarantee that the resulting system will be consistent. For example, if you change some component f of class C, you must be sure that any descendant of C that does not redefine f will get the new version of f, and that every call to f by a client of C or of a descendant of C will trigger this new version.

Traditional approaches to this problem involve manual work, forcing developers to record all dependencies and track their changes using special mechanisms known as "make files" and "include files". This is unacceptable in modern software development, especially in the OO world, where interdependencies between classes arising from inheritance relations are often complex but can be derived from a systematic examination of the software text.

Updating the system after a change must be automatic, with the analysis of inter-class dependencies carried out by tooling, not by developers by hand.

This requirement can be satisfied in a compiled environment (where the compiler works together with tooling that performs dependency analysis), in an interpreted environment, or in an environment combining both of these language implementation techniques.

Fast update

In practice, the system update mechanism must be not only automatic but also fast. More precisely, it must be proportional to the size of the changes, not to the size of the system as a whole. Without this property, the method and environment could only be applied to small systems, whereas they need to be applied to large ones.

The time needed to process a set of changes to a system, producing an updated version, must be a function of the size of the changed components and must not depend on the size of the system as a whole.

Both compiled and interpreted environments can satisfy this criterion, although in the latter case the compiler must be incremental (it must not recompile everything from scratch). Alongside the incremental compiler, the environment may of course also include a global optimizing compiler that operates on the whole system. Provided the global compiler is only needed for producing the final product, development will mostly use the incremental compiler.

Persistence

Many applications, probably most, need to preserve objects from one session to the next. The environment must provide a mechanism for doing this in a simple way.

An object often contains references to other objects, which in turn also contain references to objects. So each object may have a large number of dependent objects, with a possibly complex dependency graph (which may contain cycles). It usually makes no sense to save or restore an object without all of its direct and indirect dependent objects. A persistence mechanism is said to support persistence closure if it can automatically save the dependent objects along with the object itself.

There must exist a storage mechanism supporting persistence closure. It saves an object together with all its dependent objects on external devices and restores them during the same session or a different one.

For some applications, simple persistence support is not enough; such applications need full database support. The notion of an OO database is explained in one of the later lectures, which also explores other persistence issues, such as schema evolution, the ability to safely restore objects even if the corresponding classes have changed.

Documentation

The developers of classes and systems must provide management, customers, and other developers with clear, high-level descriptions of the software being built. They need tooling to help with this work. Most of the documentation should be produced automatically from the software texts. Assertions, as already noted, help make such documents, extracted from the software, precise and informative.

Tools must be available for automatically producing documentation about classes and systems.

Browsing

When working with a class it is often necessary to obtain information about other classes; in particular, the components of a given class are often defined not in the class itself, but in one of its various ancestors. The environment must provide developers with tools for exploring the text of a class, finding related classes, and quickly switching from the text of one class to another.

This is precisely the task of browsing. Typical good browsing capabilities include: searching for classes that are clients, suppliers, descendants, ancestors; searching for all redefinitions of a component; searching for the original declaration of a redefined component. (Definition: S is a supplier of C if C is a client of S. The term "client of a class" was explained above.)

Interactive browsing tools must let software developers trace dependencies between classes and components quickly and conveniently.

Libraries

One of the characteristic aspects of developing software the OO way is the ability to build it on top of existing libraries. An OO environment must provide good libraries and mechanisms for creating new libraries.

Basic libraries

Computer science first studies fundamental data structures — sets, lists, trees, stacks — and the algorithms associated with them: sorting, searching, traversal, pattern matching. These structures and algorithms are ubiquitous in software development. It is not uncommon for yet another developer to reimplement them in their own system. This is not only wasteful but also harms the quality of the software, since an individual developer implementing a data structure not as an end in itself, but as a component of some application, is unlikely to achieve optimal reliability and performance.

An OO development environment must provide reusable classes that satisfy common needs.

Reusable classes implementing fundamental data structures and algorithms must be available.

Graphics and user interfaces

Many modern software systems are interactive. Graphics and a convenient, most often graphical, interface are widely used for interacting with the user. This is one of the areas where the OO model has proved most impressive and useful. Developers must be able to use graphics libraries to build interactive applications quickly and efficiently.

Reusable classes must be available for developing applications that provide users with pleasant graphical user interfaces.

Library evolution mechanisms

Developing high-quality libraries is a long and difficult task. There is no way to guarantee that the libraries built will be perfect right away. Consequently, an important issue is giving library developers the ability to update and modify their designs without harming existing systems that are based on those libraries. We have placed this important evolution criterion in the libraries category, but it also belongs to the method and language category.

Mechanisms must be available that ease library evolution with minimal disruption to client software.

Library indexing mechanisms

Another pressing problem of libraries is the need for mechanisms to identify classes that satisfy particular needs. This criterion touches all three categories: libraries, the language (since there must be a way to insert indexing information into the text of each class), and the tooling (to process queries for classes satisfying certain conditions).

Library classes must be equipped with indexing information that allows searches based on properties.

Continuing the overview

For a deep understanding of the concepts, it is preferable to read this book sequentially; however, readers who wish to supplement this theoretical overview may, before going further, see how the method works on a practical example. For this, one should turn to the course "Foundations of Object-Oriented Design", where a specific problem is examined and the OO solution is compared with a solution using a traditional technique.

Studying this lecture is largely self-contained, so you will be able to understand the solution without reading the intervening lectures. But if you do look there, you must promise to come back so as to continue studying the material sequentially, starting from .

Bibliographic references and object resources

An introduction to OO criteria is a good place to give a list of works that provide good introductions to object technology as a whole.

[Walden 1995] discusses the most important issues of object technology, paying particular attention to analysis and design; this book is probably the best reference guide on the subject.

[Page-Jones 1995] gives an excellent overview of the method.

[Cox 1990] (the first edition dates from 1986) is based on a somewhat different view of object technology; the book helped spread OO concepts to a wide audience.

[Henderson-Sellers 1991] (a second edition is in preparation) gives a brief overview of OO ideas. The book is intended for people whose company asks them to "go and see what this object-oriented programming thing is"; it contains ready-to-copy slide masters, in some cases very valuable ones. Another overview is [Eliens 1995].

The Object Technology Dictionary [Firesmith 1995] provides extensive reference material on many aspects of the method.

All of these books, to varying degrees, are addressed to technically minded people. There is also a need to educate managers.

The book [M 1995] grew out of a lecture originally intended for this very book, and became a full-fledged book in which object technology is discussed from the standpoint of management personnel. It begins with a short technical presentation using professional terminology, and then provides an analysis of management issues (life cycle, project management, reuse policy). Another management-oriented book, [Goldberg 1995], offers an additional perspective on many important topics. [Baudoin 1996] emphasizes life-cycle issues and the importance of standards.

Returning to technical presentations, three important books on OO languages, written by the creators of these languages, contain general methodological discussions that make them interesting even for readers who do not use these languages, or who may even view them unfavorably. The history of programming languages and the books about them shows that creators do not always write best about their own creations, but in this case they did it well. These books are:

[x]. Simula BEGIN [Birtwistle 1973] (two other authors are creators of the language — Nygaard and Dahl.)

[x]. Smalltalk-80: The Language and its Implementation [Goldberg 1983].

[x]. The C++ Programming Language, Second Edition [Stroustrup 1991].

Quite recently, some introductory programming textbooks have started using OO ideas from the very beginning, since there is no reason to let "ontogeny repeat phylogeny". There is no need for poor students, like their predecessors, to go through the entire history of hesitation and error before arriving at the right ideas. The first such text (as far as I know) was [Rist 1995]. Another good book meeting the same needs is [Wiener 1996]. At the next level are second-course programming textbooks. A discussion of data structures and algorithms based on the notation of this book can be found in [Gore 1996] and [Wiener 1997]; [Jezequel 1996] presents the principles of OO software engineering. The teaching of the technology is also discussed in the course "Foundations of Object-Oriented Design".

The Usenet newsgroup comp.object, on several sites of the network, is a natural venue for discussing many object technology issues. Like all such forums, it is a mix of the good, the bad, and the terrible. The Object Technology column in Computer (IEEE), which I have edited since its inception in 1995, frequently features pieces by leading experts.

Journals devoted to Object Technology:

[x]. Journal of Object-Oriented Programming (the first journal in this field, centered on technical discussions but intended for a broad audience), Object Magazine (more general in scope, with some articles for managers), Objekt Spektrum (in German), Object Currents (online), address .

[x]. Theory and Practice of Object Systems, an archival journal.

[x]. L'OBJET (in French), address .

Major international conferences on object orientation:

OOPSLA (annual, USA or Canada, see ); Object Expo (varying times and places, see ); and TOOLS (Technology of Object-Oriented Languages and Systems), organized by ISE three times a year (USA, Europe, Pacific), see the proceedings at: , which is also a general resource for object technology and the topics discussed in this book

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Object oriented programming"

Terms: Object oriented programming