Design by Contract

After this first glance at the terminology and mechanisms employed in object-oriented computation, we will look at what I consider to be the contribution of an object-oriented approach (and the theme of this book) in a more thematic way. The term `contract' in the title of this section is meant to refer to an approach to design that has become known as design by contract, originally introduced in  [Meyer88], which is closely related to responsibility-driven design (see Wirfs-Brock, 1989). Of course, the reader is encouraged to reflect on alternative interpretations of the phrase responsibilities in OOP. The approach captured by the term contract stresses the importance of an abstract characterization of what services an object delivers, in other words what responsibilities an object carries with respect to the system as a whole. Contracts specify in a precise manner the relation between an object and its `clients'. Objects allow one to modularize a system in distinct units, and to hide the implementation details of these units, by packaging data and procedures in a record-like structure and defining a message interface to which users of these units must comply. Encapsulation refers to the combination of packaging and hiding. The formal counterpart of encapsulation is to be found in the theory of abstract data types. An abstract data type (ADT) specifies the behavior of an entity in an abstract way by means of what are called operations and observations, which operationally amount to procedures and functions to change or observe the state of the entity. See also section adt-modules.

Abstract data types, that is elements thereof, are generally realized by employing a hidden state. The state itself is invisible, but may be accessed and modified by means of the observations and operations specified by the type. See slide 1-ADT.


Encapsulation

ADT = state + behavior

Object-oriented modeling


slide: Abstract data types -- encapsulation

Complex applications involve usually complex data. As observed by  [Wirfs89], software developers have reacted to this situation by adopting more data oriented solutions. Methods such as semantic information modeling and object-oriented modeling were developed to accommodate this need. See also sections methods and behavioral-encapsulation.

Objects may be regarded as embodying an (element of an) abstract data type. To use an object, the client only needs to know what an object does, not (generally speaking) how the behavior of the object is implemented. However, for a client to profit from the data hiding facilities offered by objects, the developer of the object must provide an interface that captures the behavior of the object in a sufficiently abstract way. The (implicit) design guideline in this respect must be to regard an object as a server that provides high level services on request and to determine what services the application requires of that particular (class of) object(s). See slide 1-respons.


Responsibilities

Client

client/server model


Server


slide: Responsibilities in OOP

Naturally, the responsibilities of an object cannot be determined by viewing the object in isolation. In actual systems, the functionality required is often dependent on complex interactions between a collection of objects that must cooperate in order to achieve the desired effect. However, before trying to specify these interactions, we must indicate more precisely how the communication between a server and a single client proceeds. From a language implementation perspective, an object is nothing but an advanced data structure, even when we fit it in a client-server model. For design, however, we must shift our perspective to viewing the object as a collection of high level, application-oriented services. Specifying the behavior of an object from this perspective, then, means to define what specific information the object is responsible for and how it maintains the integrity of that information. See slide 1-contracts.

object = information + responsibilities

Contracts

  • a set of services

Behavioral refinement

  • improving contracts

slide: Contracts and behavioral refinement

The notion of contracts was introduced by  [Meyer88] to characterize in a precise manner what services an object must provide and what requirements clients of an object must meet in order to request a service (and expect to get a good result). A contract specifies both the requirements imposed on a client and the obligations the server has, provided the requirements are met. When viewed from the position of a client, a contract reveals what the client can count on when the requirements are fulfilled. From the position of the server, on the other hand, when a client does not fulfill the requirements imposed, the server has no obligation whatsoever. Formally, the requirements imposed on the client and the obligations of the server can be specified by means of pre- and post-conditions surrounding a method. Nevertheless, despite the possibility of formally verifying these conditions, the designer must specify the right contract for this approach to work at all. A problem of a more technical nature the designer of object-oriented systems faces is how to deal with inheritance. Inheritance, as a mechanism of code reuse, supports the refinement of the specification of a server. From the perspective of abstract data types, we must require that the derived specification refines the behavior of the original server. We must answer the following two questions here. What restrictions apply, when we try to refine the behavior of a server object? And, ultimately, what does it mean to improve a contract?

Behavioral refinement

Inheritance provides a very general and powerful mechanism for reusing code. In fact, the inheritance mechanism is more powerful than is desirable from a type-theoretical perspective.

Conformance -- behavioral refinement

if B refines A then B may be used wherever A is allowed


slide: Behavioral refinement

An abstract data type specifies the behavior of a collection of entities. When we use inheritance to augment the definition of a given type, we either specify new behavior in addition to what was given, or we modify the inherited behavior, or both. The restriction that must be met when modifying behavior is that the objects defined in this way are allowed to be used at all places where objects of the given type were allowed. This restriction is expressed in the so-called conformance rule that states that {\em if B refines A then B may be used wherever A is allowed}. Naturally, when behavior is added, this condition is automatically fulfilled. See slide 1-conformance. The conformance rule gives a very useful heuristic for applying inheritance safely. This form of inheritance is often called `strict' inheritance. However, it is not all that easy to verify that a class derived by inheritance actually refines the behavior specified in a given class. Partly, we can check for syntactic criteria such as the signature (that is, type) of the individual methods, but this is definitely not sufficient. We need a way in which to establish that the behavior (in relation to a possible) client is refined according to the standard introduced above. In other words we need to know how to improve a contract. Recall that from an operational point of view an object may be regarded as containing data attributes storing information and procedures or methods representing services. The question {\em `how to improve a contract?'} then boils down to two separate questions, namely: (1) {\em `how to improve the information?'} and (2) {\em `how to improve a service?'}. To provide better information is, technically speaking, simply to provide more information, that is more specific information. Type-theoretically, this corresponds to narrowing down the possible elements of the set that represents the (sub) type. To provide a better service requires either relieving the restrictions imposed on the client or improving the result, that is tightening the obligations of the server. Naturally, the or must be taken as non-exclusive. See slide 1-services.

Attributes

refine


  • more information

Services

  • better services

Contracts

  • more and better services

A better service

  • fewer restrictions for the client
  • more obligations for the server

slide: Improving services

To improve a contract thus simply means adding more services or improving the services that are already present. As a remark,  [Meyer88] inadvertently uses the term subcontract for this kind of refinement. However, in my understanding, subcontracting is more a process of delegating parts of a contract to other contractors whereas refinement, in the sense of improving contracts, deals with the contract as a whole, and as such has a more competitive edge. Summarizing, at a very high level we may think of objects as embodying a contract. The contract is specified in the definition of the class of which that object is an instance. Moreover, we may think of inheritance as a mechanism to effect behavioral refinement, which ultimately means to improve the contract defining the relation between the object as a server and a potential client.

Object-oriented modeling

  • prototyping, specification, refinement, interactions

OOP = Contracts + Refinements


slide: Object-oriented modeling

To warrant the phrase contract, however, the designer of an object must specify the functionality of an object in a sufficiently abstract, application-oriented way. The (implicit) guideline in this respect is to construct a model of the application domain. See slide 1-modeling. The opportunity offered by an object-oriented approach to model concepts of the application domain in a direct way makes an object-oriented style suitable for incremental prototyping (provided that the low-level support is available). The metaphor of contracts provides valid guidelines for the design of objects. Because of its foundation in the theory of abstract data types, contracts may be specified (and verified) in a formal way, although in practice this is not really likely to occur. Before closing this section, I wish to mention a somewhat different interpretation of the notion of contracts which is proposed by  [HHG90]. There contracts are introduced to specify the behavior of collections of cooperating objects. See section formal-coop.