Object oriented programming

\$= The contribution of object oriented programming to the practice of program develop\-ment lies in the facilities it offers to specify in a declarative way the structure of a program as a collection of related objects. An object oriented program reflects the conceptual structure of a problem domain by specifying the proper data types, and their relation by means of inheritance. Object oriented programming can best be regarded as a methodology, the essence of which is captured by the directive implied by the phrase object oriented modeling. \oopindex{modeling} Object oriented languages contribute to this methodology by providing the necessary technology. In this section, we will study the various mechanisms incorporated in object oriented programming languages and at the end we will come back to the more general issues that play a role in requirements analysis and the design of an object oriented system.\ftn{ See  [Sau89] and  [BPS89] for an overview and comparison of object oriented programming languages. } A succinct formulation of the basic ingredients of object oriented programming languages is given by the equation [M] OOP = encapsulation + inheritance These mechanisms, encapsulation by means of abstract data types and inheritance, allow to construct a declarative model of a given problem domain. When speaking of the declarative nature of object oriented programming, we do not intend to say that an object oriented program has a logical interpretation that is as strictly defined as the declarative interpretation of a logic program, but that in a rather loose sense an object oriented program may reflect part of the reality that it intends to model. The objects that we describe in an object oriented specification may thus partly correspond to entities in reality, although inevitably there will be objects for which such correspondence is not evident. These correspondences enhance our conceptual understanding of a program. \nop{ Evidently, this comes not for free, but is the result of careful design, that is a discipline rooted in the intention to specify an adequate model of the problem domain. }

History

\oopindex{history} Object oriented programming has its root in simulation. The first language supporting objects was Simula  [DaNy66]. Simula has been primarily used to simulate complex dynamic systems. Since Simula supported co-routining, objects could coexist in a quasi-concurrent fashion, exchanging messages in order to direct the flow of control. The conception of computation as the exchange of messages between objects proved to be fruitful, as became apparent with the introduction of Smalltalk. Originally, Smalltalk was intended as a language for programming interactive graphic work-stations. This intention has been realized, to the extent that similar languages are used nowadays to implement menu-driven, window-based user interfaces. C.f.  [LVC89],  [Meyro86]. The introduction of Smalltalk meant both the introduction of a new language and the introduction of a radically different style of programming. Along with a new style came a new terminology, the now familiar terminology of objects, classes, methods, messages and inheritance.

Data abstraction

\oopindex{data abstraction} Why has the introduction of object oriented programming resulted in such a radical change of our conception of programming? Differently, phrased, what have been the developments that have led to the acceptance of object oriented programming as a new paradigm? These developments may be traced back to the introduction of structured programming in the 1970s. The advent of structured programming goes hand in hand with the dominance of what we may call the procedural style of programming. Developing a program by means of stepwise refinement was generally taken to consist of breaking up a problem into a number of abstract steps or procedures, that were then gradually refined by more detailed procedures. The effort went primarily into finding a proper algorithm and the procedures implementing it. The major disadvantage of the procedural approach was its inadequacy with respect to the representation of data as structured entities. The theory of abstract data types offered a correction to these shortcomings and may be regarded as the most important constituent of object oriented programming, since it provides a mathematically well-founded notion of encapsulation as a means to specify the behavior of an entity in an abstract way. An example of the abstract specification of a data type stack is given by the algebraic theory below. \oprog{stack}{ .so stackadt } The algebraic specification of a stack has three components: a signature component that declares the functions needed to create and manipulate a stack, a component that specifies the preconditions that must be met when calling these (partial) functions, and a component that specifies the semantic constraints characterizing the behavior of a stack by means of equational axioms. The preconditions for applying the functions pop and top state that the stack may not be empty, otherwise the result of the function will not be defined. The axioms fully describe the behavior of a stack. As an example, the composition of a pop and push operation, with push executed first, is evidently an identity operation for any conceivable stack. .so adt

Data hiding

\oopindex{hiding} An abstract data type specifies the behavior of a category of entities. Internal details of how such behavior is implemented remain hidden from the user of such entities. In this sense, the theory of abstract data types has certainly contributed to our awareness of the importance of data hiding. These developments have been taken up by programming language designers and have resulted in the introduction of modules and a distinction between specification and implementation parts. An example of a language supporting such features is Modula-2  [Wirth83]. The major disadvantage of modules, however, is that they do not constitute a type and may not be regarded as first class entities, however valuable they may be in providing a modularization mechanism. As argued in  [St88] and  [Meyer88], objects may be regarded as implementations of abstract data types, since they encapsulate data and behavior. Moreover, an interface may be specified that hides all internal details of an object and prevents unprotected access to its data. Not all object oriented languages, however, provide such a protection mechanism. Smalltalk  [GR83], C++  [St86] and Eiffel  [Meyer88] are languages that do provide such support.

Inheritance

\oopindex{inheritance} As a method of program development, the paradigm of data abstraction may be characterized as the activity of finding the right data types and specifying the appropriate behavior for these types. Abstract data types alone, turn out to be rather inflexible and inconvenient for specifying large programs in a structured way. C.f.  [St88]. Inheritance allows such specifications to be related in the sense that they may be shared or organized in a specialization hierarchy. In the view of most, with the exception of  [Booch86], inheritance is an integral part of the object oriented approach.

Encapsulation

\oopindex{encapsulation} The encapsulation mechanism offered by object oriented languages is embodied by the notion of an object. In most languages, objects are defined by a class declaration that characterizes the properties of the objects belonging to that class. In operational terms, a class may be regarded as a template for creating objects.

Objects

\oopindex{objects} may be thought of as structures that comprise both data and procedures that may operate on these data. Using Smalltalk terminology, we may write down the equation [M] object = data + methods The variables that refer to the data contained by an object are often called instance variables, since such variables are privately owned by each instance of a class.

Methods{\rm ,}

\oopindex{methods} in Smalltalk, have exclusive access to the instance variables of the object for which they are defined. The methods of an object are, no more and no less, priviliged procedures that may operate on the data of an object. In other words, methods provide a functional abstraction mechanism that allows the contents of an object to remain hidden. A pictorial representation of an object counter, containing an instance variable n, and supporting the methods initialize, increment and value is given below. .so instance

Messages

\oopindex{messages} A method is executed for an object in response to a message. For a message to be a legal method call, the method must be defined in the method interface of the object. Methods that are private to an object may not be called. \oopindex{method interface} Operationally, we may regard an object as encapsulating a state that is dependent on the values of the instance variables, and behavior that results from the functions embodied in the methods defined for the object. We may capture this operational view in the equation [M] object = state + behavior that closely corresponds to our previous characterization of an object as encapsulating data and methods. Computation in an object oriented system is sending messages. Sending a message to an object may result in a change of the state of the object and may possibly cause the object to send messages to other objects. We may remark, following  [St88], that an object oriented language supports data abstraction to the extent that access to the state of an object is effectively forbidden, except for the methods defined for the object. To provide further protection, some languages allow to define an external method interface that states which methods are externally visible.

Responsibilities

\oopindex{responsibilities} In addition to the syntactic and operational characterization given of objects, we may characterize an object from the perspective of program development as an entity with certain responsibilities. From this perspective an object can be regarded as providing a service in response to a message. We will refer to the object sending a message to ask for a service as a client of the object that supplies the service. \oopindex{external interface} The external interface of an object, that comprises the methods visible to the clients of an object, may now be viewed as stating the responsibilities of an object, that is the services it is willing to supply. \oopindex{constructors} \oopindex{destructors} \oopindex{modifiers} \oopindex{selectors} \oopindex{iterators} As concerns the operations of an object (its responsibilities), a distinction can be made between methods to create and initialize an object (constructors), methods to dispose of an object (destructors), methods to change the state of an object (modifiers), methods to evaluate the current state (selectors) and methods to visit parts of the object (iterators). In a design, the description of an object will usually not contain a precise characterization of its constructor and destructor methods. However, when objects have activity of their own, the constructor characterizes this activity or may lay down the protocol for interaction with other objects. In these cases, the description of the constructor enhances a conceptual understanding of the functionality of the object.

Classes versus prototypes

\nop{ An object is an entity that comes into existence during an actual computation. } Object creation may be static, in which case all objects are created at the beginning of a computation, or dynamic. Dynamic object creation allows objects to come into existence at the moment that they are needed. The three languages mentioned, Smalltalk, C++ and Eiffel, all support dynamic object creation.

Types

\oopindex{types} An object is an instance of a class. A class acts as a template for creating objects. In a strictly typed language, where strictly typed means that each expression in the language may be assigned a type, the type of an object is determined by the class of which it is an instance. Dynamically, an object may be regarded, using the characterization given in the annotated reference manual for C++, as a region of storage, {\it the meaning of which is determined by the type of the expression used to access it}. See  [ES90]. Static typing allows to resolve the well-typedness of a program at compile-time and enables to deal in an efficient way with method calls, provided that these are not overridden in an inheritance hierarchy. Smalltalk is an untyped language, that enforces no type checking. However, Smalltalk may be regarded as a dynamically typed language, since each computational entity created during the execution of a program is an object in the sense of being an instance of a class. Each object carries its own type information, telling to which class it belongs. When executing a method in response to a message, the object searches its method table, which is shared with the other instances of its class, for the appropriate procedure. When a method is not defined in the object's own method table, the method tables of the classes from which the object inherits are searched. The language C++, in contrast, is statically and strictly typed, although it allows for exceptions to the type scheme. Calling a method, which is called a member function in C++, amounts to calling an ordinary function since the existence of that function may be assessed at compile-time. However, for a function declared virtual, which allows such a function to be overridden by inheriting classes, the appropriate binding cannot be resolved statically but must be computed at run-time.

Classes and instances

\oopindex{class} \oopindex{instance} Classes are templates for creating objects as instances of a class. Being an instance of a class, an object embodies the functionality of that class as well as the functionality inherited from the classes of which the object's class is a subclass. In other words, in a class-based system, we have two relations that determine the behavior of an object, the instance relation, denoting that an object is an instance of a class, and the subclass relation, indicating that a class is a subclass of a class. We may capture these relations pictorially by the arrows \begin{center} .so rel \end{center} which must be read, respectively as, class C is a subclass of (the parent) class P, and object O is an instance of class C. \nop{ As an example, the picture below represents the case that we have an object i that is an instance of a class Integer, which is a subclass of the class Sortable. \begin{center} .so sort \end{center} } As an example, imagine the case that we have an object i that is an instance of a class Integer, which is a subclass of the class Sortable. Using inheritance allows to create an abstract superclass, such as the class Sortable, that contains the common properties of a number of subordinate classes. An example of such a property is the existence of the relation less-than by which instances of (subclasses of) Sortable may be compared and which can be used to define a generic sorting algorithm. As another subclass of Sortable, think of a class String that supports a lexicographical ordering among strings. \oopindex{object creation} Having classes as a means to create objects, it is rather natural to introduce a mechanism that allows all instances of a certain class to share class-wide resources. Smalltalk, for example, allows to declare so-called class variables and class methods that are, in opposition to the common instance variables and methods, shared by all objects of that class. Class-variables are in other words a kind of global variable, with access restricted to the instances of the class. \oopindex{class methods} Class-methods are procedures. These procedures are not allowed to access the instance variables defined for that class since these are private to each instance. Calling a class method is possible without creating an instance of the class. Class-methods are often used to create and initialize instances of a class. \oopindex{members} In C++, variable or function members may be declared static. Static members are shared by all instances of a class, similar to class variables and class methods in Smalltalk. Like class methods, static member functions are not allowed to access ordinary member variables.

Prototypes

\oopindex{prototypes} The disadvantage of making a distinction between the functionality of classes (supporting class variables and class methods) and objects (that are instances of classes, supporting instance variables and ordinary methods), is that programmers are faced with the complexity of making a design choice with respect to where functionality must be put. The choice between class methods or (object) methods will not always be evident. An alternative approach to creating objects is sketched in  [US87], where prototypes are introduced as a unifying concept combining the functionality of classes and objects. Prototypes may be used as templates for creating objects, by cloning, and as computational entities that execute a method in response to a message. Cloning an object amounts to making a copy of an object, including its state at the moment the copy is made. \oopindex{object copying} An interesting variant of cloning is to allow for differential copying, in which the differences with respect to certain attributes of the cloned object can be stated when creating the copy. In effect, the newly created object then inherits the functionality of the object from which it is created, possibly overriding or adding behavior by its differential specification. See also  [RC91]. \oopindex{delegation} As an alternative way to share behavior between objects, inheritance may be implemented as delegation to a designated parent object. Using delegation instead of subclassing or differential cloning to effect inheritance allows to build inheritance structures dynamically, during the computation. The advantage of using prototypes instead of classes to organize the conceptual structure of a program lies first of all in the simplicity that arises from the absence of classes. The primary relation supported by such an approach is the inherits relation, for which we have the choice to allow static inheritance by means of differential specifications or dynamic inheritance by making use of delegation. An additional advantage of discarding classes is that one-of-a-kind objects are naturally supported, since no extra class for such an object needs to be introduced. Another advantage, mentioned in  [US87] is that the problem of whether to support metaclasses, and to what extent, does not occur.

Metaclasses

\oopindex{metaclasses} In a class-based approach objects are organized in taxonomies along the class abstraction. A class describes the semantics of a set of objects and acts as a mould from which to create instances. A class in itself is not an object but a syntactical construct used to describe objects. However, a number of class-based languages like Loops, Smalltalk, CommonLoops and Clos, allow a class to be characterized in a more abstract way by means of a metaclass. Metaclasses, in these languages, are used to implement the behavior of a class regarded as an object, behavior that is embodied in class variables and class methods. In addition, the implementation of a class by means of a metaclass allows the programmer to inspect the properties of a class dynamically. For instance a class may answer to a request whether it supports a particular method.\ftn{ Static members in C++ do not support such reflective capabilities, although library packages exist -- as for example the NIHCL-library  [GOP90] -- that do provide such features for C++. } \oopindex{modeling} From the perspective of object oriented modeling, metaclasses provide the means to capture general properties of the system on a higher level, by defining the appropriate metaclass for a category of classes.

Three-level architecture

In Smalltalk and Loops, the dichotomy between classes and objects gives rise to a three-level architecture based on the distinction between objects, classes and metaclasses. The inheritance and instantiation structure of this architecture is pictured in the diagram below. .so three The diagram embodies two hierarchies, the hierarchy determined by the subclass relation (indicated by the solid arrows), and the hierarchy determined by the instance relation (indicated by the dotted arrows). The diagram contains three system-defined entities, namely Object, Class and MetaClass. Object is the root of the inheritance hierarchy, since every class (including metaclasses) must inherit the functionality of Object. Conversely, every ordinary class is an instance of MetaClass, or a subclass thereof. Metaclasses, such as Class and the user defined ListMetaClass, are instances of the system-defined entity MetaClass. MetaClass has a quite peculiar status in this diagram since (the appropriate arrow is omitted) it must be regarded as an instance of itself. The capability of creating instances ultimately comes from MetaClass. This capability is inherited by both the (system-defined) metaclass Class and all user defined metaclasses.\ftn{ In Smalltalk, the user is not allowed to define own metaclasses. In Smalltalk, to each class corresponds a metaclass that is hidden from the user. } In the diagram, the object level contains a single object b1 that is an instance of the class Book. Another user defined class is the class Point, which is an instance of Class and a subclass of the class Object. The architecture sketched by this diagram has a fixed number of levels, corresponding to the distinct notions of object, class and metaclass. The disadvantage of such an architecture from the point of view of object oriented modeling is that generalizations with respect to the functionality of the system may be taken only one level up above the class level. In principle, one would like to allow an arbitrary number of levels at which such generalizations are possible.

Reflective architecture

\oopindex{reflection} \nop{ The architecture sketched above has a fixed number of levels corresponding to the conceptually distinct notions of object, class and metaclass. } What we need is a view that unifies the notions of object, class and metaclass in a way that allows us to define metaclasses to an arbitrary level. In  [Cointe87] a solution is given that unifies these concepts by taking a class as an object defined by a real class. The key to this solution is to provide a reflective definition of a class, as illustrated in the diagram below. \begin{center} .so reflect \end{center} This diagram pictures that Object is an instance of Class. Class, on the other hand, inherits its behavior from Object but is an instance of itself. \oopindex{postulates} The reflective model introduced in  [Cointe87] is fully described by the following postulates: In other words, these postulates require that Object lies at the root of the inheritance hierarchy since every class is an object as well, and that Class lies at the root of the instantiation hierarchy as it provides the capability of creating new instances. Having Class at the root of the instantiation hierarchy entails a circular definition of Class, since Class must be its own instance. In order to act as an object, a class must have an attribute name that records the class name, an attribute supers that tells from which classes attributes and methods are inherited, an attribute iv that records the local variables of the instances of the class, and an attribute methods that contains the methods defined for objects of the class. In accordance with this discussion, we may instantiate the (metaclass) Class by the reflective pattern below. \yprog{Class}{
    name     supers            iv                           methods
  
   Class    (Object)     (name supers iv methods)     (new ...)
  
} Each class that displays such a reflective pattern may be regarded as a metaclass, since its instance variables reflect exactly the properties of a class. Minimally, a class must support the method new in order to create instances. In the picture below, this scheme is illustrated by using Class as a metaclass for a Point class, that has two points as actual (object) instances. \$=
\begin{center} .so reflexam \end{center} For each class the instance variables are given in brackets. The class Point is an ordinary instance of Class and need not contain the instance variables of Class. In contrast, a metaclass is created by inheriting from Class. It contains all the instance variables defined for Class. In addition, such a metaclass may contain the properties common to a category of classes. The architecture described allows to define an arbitrary number of metaclasses on top of an ordinary class. It is doubtful, however, whether the use of such a tower of metaclasses will often occur in practice. \nop{ A metaclass, created by inheriting from Class, would contain all the instance variables given for Class. In contrast, the class Point is an instance of Class and need not contain the instance variables of Class The instance variables for each class are given in brackets. }

Inheritance

\oopindex{inheritance} The popularity of the object oriented style of programming is to a large extent due to the possibility of sharing object (and class) specifications by means of inheritance. Objects in themselves provide the means to encapsulate the implementation of behavior and to regulate the interactions between objects by defining an external interface that establishes the responsibilities of the object. In addition to the clients that request a service of an object by invoking one of the methods listed in the external method interface, inheritance introduces a new category of clients that make use of the functionality of (classes of) objects by sharing functional resources of these objects, that is instance variables and methods. \oopindex{modeling} Inheritance may play a crucial role in object oriented modeling since it allows to factorize the properties common to a collection of classes in a common ancestor class. Because of this feature abstract classes may be specified to define the external interface of a collection of classes. The subclasses of an abstract class may then refine the definition provided by the abstract class by providing an implementation. From a software engineering perspective, inheritance promotes the reuse of software since when a class only captures part of the required functionality it may -- using inheritance -- easily be refined into a class that does capture all the functionality required.

Specialization hierarchies

\oopindex{specialization} In its most simple form, the inheritance mechanism provides a way of sharing common attributes, and thus allows to create a specialization hierarchy among a number of concepts. An example of such a specialization hierarchy is given in the tree below that depicts the relation between a variety of fruit. \begin{center} .so fruit \end{center} Since oranges are not the only fruit, we may encounter in this hierarchy a subtree that specifies some of the varieties of apples. As a common attribute of all the items in the tree we may think of the property of being edible. Specializations will occur with respect to the texture of their skin and the place of growth for example. The most common interpretation of this kind of taxonomies is given by a predicate logic rendering of the relations expressed by the specialization tree. The hierarchy depicted above, for example, states that all apples are (a kind of) fruit, or in a predicate logic formula: \A x. apple(x) -> fruit(x). Semantically, each node in the tree corresponds to a set of individuals (elements of a domain of discourse). This set is exactly the set described by the information provided by that particular node. In a specialization tree, each descendant of a node provides more specific information and thus restricts the number of individuals to which the description applies. In other words, taken as sets of individuals, the relation apple \< fruit holds.

Multiple inheritance

\oopindex{multiple inheritance} Instead of one ancestor, as in the specification hierarchy above, a concept may as well have multiple parents from which it inherits. For instance, if we have a concept edible then we may make apple inherit from edible to express that all apples are edible. As another example, if we have an object (type) machine with attributes age and fuel and an object type vehicle with attribute age and speed then we may create the object type car with attributes age, speed and fuel by inheriting from both machine and vehicle, as pictured below. \begin{center} .so car \end{center} The meaning of the concept car is the set of individuals that is both a machine and a vehicle, in other words the cross-section of the sets corresponding to machine and vehicle. See  [Ca84].

Conformance

\oopindex{conformance} Ideally, the inheritance relation in object oriented programming languages conforms to the notion of refining a description of a concept by providing more information. In that case we have a substitution property that states when a concept conforms to another concept: \begin{quotation} (Substitution) {\em whenever we may use an instance of a concept we may also use an instance of a refinement of that concept.} \end{quotation} This holds also in the case of multiple inheritance. For instance, if I am asked for a vehicle then I may hand over my car. \oopindex{polymorphism} Technically, conformance may be checked by identifying concepts or classes with types. Regarding concepts as types, we speak of polymorphism since the inherited concepts may be taken to be subtypes of the original concept. \nop{ In practice this appears to be an unrealistic goal, since many applications of inheritance violate this principle of refinement. } However, conflict may arise when properties are inherited that contradict each other. A famous example, illustrating the possibility of ambiguity in property inheritance systems, is the so-called Nixon-diamond. \begin{center} .so nixon \end{center} The inheritance diamond above states that Nixon is both a Quaker and a Republican. Knowing that Quakers are notorious pacifists and Republicans equally notorious non-pacifists, the question arises whether Nixon is a pacifist or a non-pacifist. Notorious, no doubt. With regard to the diamond, evidently the logical theory expressed by the inheritance graph is clearly inconsistent. C.f.  [To86]. If all the properties of the inherited concepts are preserved in the inheriting concept we say that the inheritance relation is monotonic. Otherwise, we say that the inheritance relation is non-monotonic. As observed in  [WZ88], incremental system evolution often turns out to be non-monotonic, in practice. Non-monotonicity occurs when either exceptions or overridings are used to effect the desired behavior. In these cases we can no longer speak of behavioral refinement to characterize the inheritance relation employed.

Polymorphism

\oopindex{polymorphism} To decide on whether an object type conforms to another object type we need to have a notion of type and subtype, since then we may replace conforms to by being a subtype. \oopindex{subtyping} Pragmatically, we may regard a class inheriting from a class as a subtype of the type defined by the class, since the meaning of a class is operationally defined by the method lookup procedure employed by instances thereof. See  [WZ88]. However, viewing classes as types is really overspecifying the notion of conformance and moreover such a view does not allow static type checking, since type errors are only dynamically detected as the result of a failing method lookup. \oopindex{dynamic method lookup} The procedure for dynamic method lookup (in the case of single inheritance) may be phrased recursively as follows. \yprog{lookup}{
  procedure lookup(method, class)
       if method = localmethod then do localaction
       elsif inheritedclass = nil then undefined
       else lookup(method, inheritedclass)
  
} Multiple inheritance gives rise to more complicated lookup algorithms. See  [DH88]. With regard to polymorphism, whenever a method is defined for a superclass then no type error will occur when calling the method for an instance of a subclass since all methods of the superclass are inherited by the subclass. They may however be redefined, which involves the risk of specifying contradictory behavior that (intuitively) does not conform to the intended behavior of the inherited class. \oopindex{dynamic binding} \oopindex{virtual functions} The dynamic binding that occurs with virtual functions, for instance in C++, resembles the method lookup procedure sketched above. Conformance to a supertype however is statically checked in C++. Also, in C++ there are ways to use inheritance in a non-standard way, for instance to restrict the functionality of a class of objects. C.f.  [HOB87]. A classical example of a nonstandard application of inheritance is to derive a stack from a double ended queue by disabling the ordinary deque operation (that delivers the first, that is oldest element of the queue). However, from the perspective of types, the relation [D] DQueue <_{subtype} Stack holds, since a double ended queue may be regarded as inheriting all the behavior of a stack while adding a deque operation.

Objects as records

\oopindex{objects as records} In order to grasp the subtype relation between object classes we need to introduce a more formal notion of object types. To establish the type of an object, we may regard an object as a record containing attributes and functions that are accessible by labels. The notions employed here are due to  [Ca84]. An example of a record with values is the record [] {{ a = 1, b = true, c = "hello" }} We will use type expressions of the form e:%t to denote that the expression e is of type %t. \oopindex{conformance} The conformance rule for arbitrary expressions may now be stated as follows. [D] $(Conformance) if e:%t' and %t' <= %t then e:%t The rule expresses that an expression may always be regarded as being of an appropriate supertype. For simple attributes a, say of type integer or subranges thereof, this property is easy to establish. For instance, when a:[2..5] then also a:[1..6], taking [i..j] to be the interval ranging from i to j. The subtype relation for simple types corresponds to the subset relation with respect to the sets of individuals denoted by these types. However, for functions f:%s -> %t, where %s is the type of the domain and %t the type of the range of f, it is much more difficult to establish whether a function f' of type %s' -> %t' conforms to (the type of) f. In  [Ca84] the function conformance rule is given by [] $(Functions) if %s <= %s' and %t' <= %t then f':%s' -> %t' <= f:%s -> %t The difficulty of applying this rule is brought about by the contravariance between the domain types, namely %s <= %s' whereas f' <= f. We hope to give a more intuitive understanding of this rule in the next section by exploring the notion of behavioral refinement for functions. In order to define a subtype relation for objects we state the following conformance rule for records. [D $(Records) if %t_1' <= %t_1 and ... and %t_n' <= %t_n then {{ a1:%t_1',...,a_{n+m}:%t_{n+m}' }} <= {{ a1:%t_1,...,a_n:%t_n }} D] In other words, a record type is a subtype of another record type if for each of the field a_i:%t_i (for i=1,...,n) there is a corresponding field a_i:%t_i' for which %t_i' <= %t_i. The subtype may, however, contain additional fields. As an example, defining the record types [D vehicle = {{ age : integer, speed : integer }} machine = {{ age : integer, fuel : string }} D] we may establish that the record type car defined by [D car = {{ age : integer, speed : integer, fuel : string }} D] is a subtype of both vehicle and machine. We now have a (syntactic) notion of types that enables us to decide whether an object (type) conforms to -- is a subtype of -- another object (type).\ftn{ \oopindex{object types} In this treatment, we have not paid any attention to the recursive structure of object types. For a treatment of these aspects see  [CW85] and  [CoHC90]. } In the next section we will provide a more intuitive notion of (behavioral) refinement based on the notion of subtype conformance introduced here. .so refinement

Object oriented analysis and design

\seindex{analysis} \seindex{software life-cycle} In the previous section we have given an inventory of the mechanisms provided by object oriented programming languages and we have indicated the possible use of these mechanisms in the enterprise of object oriented modeling. The question we wish to raise now is what influence object oriented technology may exert on the software life cycle and what issues play a role in object oriented analysis and design. \nop{ Traditionally, the software life cycle is subdivided in an analysis phase in which the requirements for a system are laid down, a design phase in which a conceptual architecture for a system meeting the requirements put forward in the analysis phase is proposed, an implementation phase in which the conceptual structure resulting from the design phase is embodied in actual software and an operational phase that includes installing and maintenance of the system. The transition from one phase to the next usually involves a significant effort of translating the results into the formalism appropriate to that phase. The use of object oriented techniques in the design and implementation phase has resulted in a shift of emphasis in favor of the design phase since the effort of implementing a system in an object oriented programming language on the basis of an object oriented design may be regarded as a process of refining the decisions laid down in the design. The use of object oriented techniques has thus effectively reduced the gap between the concept-oriented world of design and the technology-oriented world of implementation. C.f.  [WWW90] and  [Meyer88]. } \nop{ The central guideline in developing an object oriented program comes down to the advice to construct an (object oriented) model of the problem domain. Object oriented modeling is also the main activity in (an object oriented approach to) the rquirement analysis and the design of a system. }

Object oriented analysis

The main issue in the analysis phase is to extract the needs of the person or organization for which the software will be developed. Analysis is not so much concerned with the development of the system as with an adequate description of the problem domain, to enable the problem to be solved. A basic requirement to any analysis method is that it provides the means to handle the complexity of the underlying problem domain. In  [CY90] some currently used analysis methods, such as functional decomposition, the data flow approach and semantic modeling are discussed and compared with object oriented analysis. Functional decomposition amounts to breaking up the problem into functional steps that have to be carried out to complete the task. The data flow approach primarily models the flow of information and the events that are of influence on this flow. Information (or semantic) modeling comes closest to an object oriented approach, since it models the objects occurring in the domain, their attributes and their relationships. \seindex{information modeling} Object oriented analysis may be regarded as extending the information modeling approach by providing the means to model not only the attributes but also the behavior of the entities occurring in the domain and by the use of inheritance to elucidate the conceptual relationships between these entities. Both the information modeling approach and the object oriented approach aim at modeling the problem domain, by identifying the objects that exist in that reality. \oopindex{object identification} \seindex{linguistic analysis} \seindex{requirements} As a possible method to identify the proper objects,  [Booch86] and  [WWW90] suggest a linguistic analysis of a (written down) natural language account of the requirements. As a first attempt, objects are suggested by thinking of objects that correspond to the nouns occurring in the document and of operations or methods to correspond with the verbs used. Clearly, such a method provides only a first step towards a model or a design. \oopindex{modeling} \seindex{modeling} \seindex{rapid prototyping} A major advantage of a modeling approach to analysis is that it facilitates the communication with domain experts, since these may be supposed to be well-acquainted with the objects that constitute the problem domain.\ftn{ In contrast, domain experts are usually not well-acquainted with the methods used in functional decomposition or the data flow approach, nor are they generally willing to acquire that expertise. } Another advantage of the modeling approach is that it allows for rapid prototyping. In particular in cases where there is a continual change of user requirements, prototyping may be helpful in establishing these requirements. \nop{ Evidently, communication with domain experts plays a crucial role in successfully completing the analysis phase. }

Object oriented design

\seindex{design} After completing the analysis phase, the next step is to design the system. Applying an object oriented approach, the transition between the analysis phase and the design phase may be rather smooth. The objects comprising the design may (to a certain extent) be thought of as refining the object identified during analysis. Similarly, when using an object oriented language, the objects actually implemented may be considered as a further refinement specifying the implementation details. In addition, however, to the object identified in the analysis phase, objects will play a role in the design for which no clearly identifiable counterpart exists in reality. As an example of such objects, think of the objects that are needed to develop a (graphical) user interface. Also, objects may be present that embody hypothetical entities of the domain. For instance, in a medical expert system objects may be used to define the reasoning capabilities of the experts in a domain-independent manner. These objects do not exist in reality, but are artefacts needed to explain the functional behavior of the entities involved. An example of a very elementary design method is the method introduced in  [BC89]. \oopindex{CRC cards} \seindex{CRC cards} The authors propose the use of so-called CRC-cards. An example of such a card (approximately the size of a small postcard) is pictured below. \fboxClassname & Collaborators \\ Responsibilities & \vspace{3cm} \\ \hline \end{tabular} The abbreviation CRC stands for Class, Responsibility and Collaborators. One such card may be used to describe the responsibilities of a particular class (of objects) and to indicate what kind of object classes are needed to provide the services listed under the responsibilities of the (object) class. The design of a system consists of a collection of such cards, one for each class. The authors report that the use of these cards facilitates the design, in particular when working in groups; and, perhaps more importantly, that such a card design may be conveniently used to explain the design of a system to others. Using these cards, the dynamic operation of a system may be mimicked by selecting the appropriate card whenever a particular service is needed. Surprisingly simple, yet apparently quite effective. A similar method is proposed in  [CY90]. As an example of the use of CRC-cards we will discuss the design of the classes underlying the user interface of Smalltalk programs. The approach taken by the designers of the Smalltalk system has become known as the MVC-paradigm. The basic postulate in this approach is that the classes describing the functionality that is needed to solve a problem must be separated from the classes that implement the interaction with the user. The class describing the problem related functionality is known as the model. Smalltalk provides a class Model that incorporates the methods needed to specify the other classes in an independent way. A CRC-card describing the class Model is pictured below. {\mbox{}\hspace{1cm}\begin{tabular}{|p{5cm}|p{4cm}|} \hline \fboxModel & \\ Maintain problem-related info.\nl Broadcast change information. & ... \\ \hline \end{tabular}} A model class is defined as a subclass of Model. To enable the (graphic) display of a model, a view class must be associated with the model. To factor out the display, the model maintains a list of dependents of which the view object is a member. When the state of the model is changed, then the model notifies the objects in its list of dependents to allow these objects to update the information they have of the model. The functionality of a view class is rendered by the CRC-card depicted below. {\mbox{}\hspace{1cm}\begin{tabular}{|p{5cm}|p{4cm}|} \hline \fboxView & \\ Render the model.\nl Transform coordinates. & Controller \n Model \\ \hline \end{tabular}} Apart from the possibility of displaying its state, a model may need input from the user. In the MVC-paradigm, obtaining user input is delegated to a so-called controller object. A controller object in its turn cooperates with both the model and the view. An additional function of a controller object is to distribute the control in response to the input from the user. The functionality of a controller is summarized in the CRC-card depicted below. {\mbox{}\hspace{1cm}\begin{tabular}{|p{5cm}|p{4cm}|} \hline \fboxController & \\ Interpret user input.\nl Distribute control. & View \n Model \\ \hline \end{tabular}} A model may allow for a variety of views and a variety of ways to obtain user input. Hence, to each model may correspond a multiple of view--controller pairs. A design by CRC-cards may be regarded as a first attempt at modeling a real system. It presents, when properly documented, an overview of the design of the system that may be used to explain the decisions taken. However, it lacks the details needed to provide an adequate model that gives insight in the behavioral aspects of the system. \nop{ The example above shows that CRC-cards may be helpful in giving an outline of the design. However, as observed by the authors, an explanation needs to accompany such a design to make it understandable. }

Object oriented modeling

In an object oriented system, computation amounts to sending messages among objects.  [Booch86] identifies the roles that objects may play in such a system. An object may be an actor that operates by sending requests to other objects but provides no services. An object may be a server that may be requested to perform some task. The most usual case, however, is that an object both suffers and requires actions. These objects, called agents in  [Booch86], need as well as provide services. \id{ With regard to the distinctions made, we may want to phrase the design guideline {\it `minimize and localize dependencies among objects'} as a recommendation to maximize the number of actors and servers in a system, under the proviso that no more objects are introduced than necessary. } \oopindex{concurrency} An important advantage of an object oriented approach is that objects allow for a natural introduction of concurrency. A client of an object does not have to be aware of whether the object has activity of its own or is passive and only activated to respond to a message. In contrast, when applying a functional decomposition method to design a system the introduction of concurrency requires to reorganize the modular structure of the program, since modules there represent the major computation steps. The central guideline in developing an object oriented program comes down to the advice to construct an (object oriented) model of the problem domain. Object oriented modeling is hence the main activity in (an object oriented approach to) the requirement analysis and the design of a system.