To gain an understanding of some new area, it is virtually unavoidable to be immersed in the material for a while without exactly understanding where it will lead.
Additional keywords and phrases:
object, data abstraction,
analysis, design, implementation, distribution
In design, objects are our primary unit of decomposition. In our design, objects may reflect real life entities, such as Employer and Employee, but may also represent system artefacts, such as stacks or graphics.
In actual development, that is in the implementation, objects are our unit of implementation. Each object itself may be regarded as a collection of functions. But it is the collection of functions, and the behavior that they describe, that we take as our unit; not the individual function.
A consequence of adopting an object-oriented approach is that we have to spend more time in describing and understanding the structure and behavior of the system, and to learn the formalisms and tools that enable us to do so.
To master this complexity, we need to think about how objects can be made to fit together. To benefit from an object-oriented approach, we need to design a software architecture that defines and regulates the interactions between objects.
A framework is a kind of library of reusable objects. However, in contrast with ordinary software libraries, frameworks may at times take over control. The best-known examples of frameworks are in the GUI domain; frameworks in other domains (e.g. the business process domain) are emerging.
Using a framework may simplify your life, since a framework provides generic solutions for a particular application domain. But the price you pay is twofold. You have to understand what (patterns of) solutions the framework provides, and you have to comply with the rules of the game imposed by the framework.
Objects provide the means by which to structure a system. In Smalltalk (and most other object-oriented languages) objects are considered to be grouped in classes. A class specifies the behavior of the objects that are its instances. Also, classes act as templates from which actual objects may be created. Inheritance is defined for classes only. From the perspective of design, inheritance is primarily meant to promote the reuse of specifications.
Finally, an important feature of object-oriented
languages is their support for polymorphism.
Polymorphism is often incorrectly identified with inheritance.
Polymorphism by inheritance makes it possible
to hide different implementations behind a common interface.
However, other forms of polymorphism may arise by overloading functions
and the use of generic (template) classes or functions.
See sections polymorphism and
flavors.
Features and benefits of OOP
Encapsulation promotes modularity, meaning that objects must be regarded as the building blocks of a complex system. Once a proper modularization has been achieved, the implementor of the object may postpone any final decisions concerning the implementation at will. This feature allows for quick prototyping, with the risk that the `quick and dirty' implementations will never be cleaned up. However, experience with constructing object-oriented libraries and frameworks has shown that the modularization achieved with objects may not be very stable.
Another advantage of an object oriented approach, often considered to be the main advantage, is the reuse of code. Inheritance is an invaluable mechanism in this respect, since the code that is reused seldom offers all that is needed. The inheritance mechanism enables the programmer to modify the behavior of a class of objects without requiring access to the source code.
OO = encapsulation + inheritance
benefits of OOP
Despite the fact that our basic mathematical model of a computing device (and hence our notion of computability) has not altered significantly, the development of high level programming languages has meant a drastic change in our conception of programming. Within the tradition of imperative programming, the introduction of objects, and object-oriented programming, may be thought of as the most radical change of all. Indeed, at the time of the introduction of Smalltalk, one spoke of a true revolution in the practice of programming.
the object model
message
encapsulation
protocol
In the (ordinary) sequential machine model, the result of a computation is (represented by) the state of the machine at the end of the computation. In contrast, computation in the object model is best characterized as cooperation between objects. The end result then consists, so to speak, of the collective state of the objects that participated in the computation. See slide 2-model. Operationally, an object may be regarded as an abstract machine capable of answering messages. The collection of messages that may be handled by an object is often referred to as the protocol obeyed by the object. This notion was introduced in the Smalltalk programming environment originally to provide the means to group the messages to which an object may respond. For instance, the distinction between methods for initialization and methods for modification or processing may be convenient in developing or using a program. The notion of protocol may also be given a more formal interpretation, as has been done for instance in the notion of contracts (introduced in Eiffel) stating the requirements that must be adhered to in communicating with an object.
Structurally, an object may be regarded as a collection of data and procedures. In principle, the data are invisible from the outside and may be manipulated only by invoking the right procedure. In a pure object-oriented language such as Smalltalk and Eiffel, sending a message to an object is the only way of invoking such a procedure. Combined, data-hiding and message interface abstraction will be referred to as encapsulation. Actually, object-oriented languages, while in some way supporting objects as collections of data and procedures, may differ subtly in the degree and way in which they support data-hiding and abstraction.
For now, we need not be concerned with the precise
mathematical details of our model of a computing device.
For a very much more precise and elaborate description
of the Turing machine, the interested reader is referred
to
An interesting, but perhaps somewhat distressing, feature
of the Turing machine model is that it is the strongest model
we have, which means that any other model
of computation is at best equivalent to it.
Parallel computation models in effect do extend the power
of (sequential) Turing machines, but only in a linear
relation with the number of processors.
In other words, the Turing machine defines what we may regard
as computable and establishes a measure
of the complexity of a computation, in space and time.
The awareness of the intrinsic limitations imposed
by a precise mathematical notion of computability
has, for example, led us to regarding the claims of artificial intelligence
with some caution, see
An equally important feature of the Turing machine model is that it gives us an illustration of what it means to program a computing device, that is to instruct the machine to perform actions dependent on its input and state. As an extension to the model, we can easily build a universal computing device, into which we may feed the description of some particular machine, in order to mimic the computation of that machine. Apparently, this gives us a more powerful machine. However, this has proven not to be the case. Neither does this universal device enlarge the class of computable problems, nor does it affect in any significant sense the computational complexity of what we know to be computable. See slide 2-devices.
Object-oriented programming does not enlarge the class of computable problems, nor does it reduce the computational complexity of the problems we can handle.
Our model of a computing device does quite precisely delimit the domain of computable problems, and gives us an indication of what we can expect the machine to do for us, and what not. Also, it illustrates what means we have available to program such a device, in order to let it act in the way we want. Historically, the Turing machine model may be regarded as a mathematical description of what is called the Von Neumann machine architecture, on which most of our present-day computers are based. The Von Neumann machine consists of a memory and a processor that fetches data from the memory, does some computation and stores the data back in memory. This architecture has been heavily criticized, but no other model has yet taken its place. This criticism has been motivated strongly by its influence on the practice of programming. Traditionally, programs for the Von Neumann architecture are conceived as sequences of instructions that may modify the state of the machine. In opposition to this limited, machine-oriented view of programming a number of proposals have been made that are intended to arrive at a more abstract notion of programming, where the machine is truly at the service of the programmer and not the other way around.
One of these proposals to arrive at a more abstract notion of programming is advocated as the object-oriented approach. Before studying the intrinsics of the object-oriented approach, however, it may be useful to reflect on what we may expect from it. Do we hope to be able to solve more problems, or to solve known problems better? In other words, what precisely is the contribution of an object-oriented approach?
Based on the characterization of a computing device,
some answers are quite straightforward.
We cannot expect to be able to solve more problems,
nor can we expect to reduce the computational complexity
of the problems that we can solve.
What an object-oriented approach can contribute,
however, is simply in providing better means
with which to program the machine.
Better means, to reduce the chance of (human)
errors, better means, also, to manage the complexity
of the task of programming (but not to reduce
the computational complexity of the problem itself).
In other words, by providing abstractions that
are less machine oriented and more human oriented,
we may enlarge the class of problems that we can
tackle in the reality of software engineering.
However, we simply cannot expect that an object-oriented
approach may in any sense enlarge our notion
of what is computable.
Some history
The first abstraction mechanism beyond the level of assembler language and macros is provided by procedures. Procedures play an important role in the method of stepwise refinement introduced by the school of structured programming. Stepwise refinement allows the specification of a complex algorithm gradually in more and more detail. Program verification amounts to establishing whether the implementation of an algorithm in a programming language meets its specification given in mathematical or logical terms. Associated with the school of structured programming is a method of verification based on what has become known as Hoare logic, which proceeds by introducing assertions and establishing that procedures meet particular pre- and post-conditions.
Other developments in programming language research are aimed at providing ways in which to capture the mathematical or logical meaning of a program more directly. These developments have resulted in a number of functional programming languages (e.g. ML, Miranda) and logic programming languages, of which Prolog is the best-known. The programming language Lisp may in this respect also be regarded as a functional language.
The history of object-oriented programming may be traced back to a concern for data abstraction, which was needed to deal with algorithms that involved complex data structures. The notion of objects, originally introduced in Simula (Dahl and Nygaard, 1966), has significantly influenced the design of many subsequent languages (e.g. CLU, Modula and Ada). The first well-known object-oriented language was Smalltalk, originally developed to program the Dynabook, a kind of machine that is now familiar to us as a laptop or notebook computer. In Smalltalk, the data-hiding aspect of objects has been combined with the mechanism of inheritance, allowing the reuse of code defining the behavior of objects. The primary motivation behind Smalltalk's notion of objects, as a mechanism to manage the complexity of graphic user interfaces, has now proven its worth, since it has been followed by most of the manufacturers of graphic user interfaces and window systems.
Summarizing, from a historical perspective, the introduction of
the object-oriented approach may be regarded as a natural extension to
previous developments in programming practice, motivated
by the need to cope with the complexity of new applications.
History doesn't stop here. Later developments,
represented by Eiffel, C++ (to a certain extent) and Java,
more clearly reflect the concern with abstraction and verification,
which intrinsically belongs to the notion of abstract data types
as supported by these languages.
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
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.
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.
object = information + responsibilities
Contracts
if B refines A then B may be used wherever A is allowed
OOP = Contracts + Refinements
Data abstraction
When programs became larger and data more complex, the design of correct algorithms
was no longer the primary concern.
Rather, it became important to provide access to data in a representation
independent manner.
One of the early proponents of data hiding was, see
An elegant solution is provided by Java which offers multiple interface inheritance, by allowing multiple interfaces to be realized by an actual class.
With an increase in the number of software products
not satisfying user needs, prototyping
has become quite popular!Software development models
The problem domain -- complex reality
Communication -- with domain experts
Continual change -- user requirements
Reuse -- of analysis results
Concluding this brief exploration of the analysis phase, I think we may safely set as the goal for every method of analysis to aim at stable abstractions, that is a conceptual model which is robust with respect to evolving user requirements. Also, we may state a preference for methods which result in models that have a close correspondence to the concepts and notions used by the experts operating in the application domain.
With respect to notation UML (the Unified Modeling Language,
see Appendix UML) is the obvious choice.
How to apply UML in the various phases of object-oriented software construction
is an altogether different matter.
Design
In an object-oriented approach,
the distinction between analysis
and design
is primarily one of emphasis;
emphasis on modeling the reality of
the problem domain versus
emphasis on providing an architectural
model of a system that lends itself
to implementation.
One of the attractive features of such
an approach is the opportunity of a seamless
transition between the respective phases
of the software product in development.
The classical waterfall model can no
longer be considered as appropriate
for such an approach.
An alternative model, the
fountain model, is proposed by
To my mind, there is yet another reason for the extra
effort involved in design.
In practice it appears to be difficult and time
consuming to arrive at the appropriate
abstract data types for a given application.
The implementation of these structures,
on the other hand, is usually straightforward.
This is another indication that the unit of
reuse should perhaps not be small pieces of code, but
rather (the design of) components that fit into
a larger framework.
From the perspective of software quality and maintenance,
these mechanisms of encapsulation
and inheritance may be characterized as powerful means
to control the complexity of the code needed to realize a system.
In Design assignments
An Interior Design Assistant (IDA)
is a tool to support an interior design architect.
When designing the interior of a house or building,
the architect proceeds from the spatial layout
and a list of furniture items.
IDA must allow for
placing furniture in a room.
It will check for constraints. For example
placing a chair upon a table will be prohibited.
For each design, IDA must be able to
give information with respect to pricing
and the time it takes to have the furniture
items delivered.
In addition to the design facilities, IDA must also
offer a showroom mode, in which
the various designs can be inspected and compared
with respect to price and delivery time.
An Agenda Support System assists the user in maintaining a record of important events, dates and appointments. It moreover offers the user various ways of inspecting his or her agenda, by giving an overview of important dates, an indication of important dates on a calendar, and (more advanced) timely notification.
A Multi-user Agenda Support System extends a simple Agenda Support System by providing facilities for scheduling a meeting, taking into account various constraints imposed by the agendas of the participants, as for example a special event for which a participant already has an entry in his or her agenda. A minimal Multi-user Agenda Support System must provide facilities for registering important dates for an arbitrary number of users. It must, moreover, be able to give an overview of important dates for any individual user, and it must be possible to schedule a meeting between an arbitrary subset of users that satisfies the time-constraints for each individual in that particular group.
This minimal specification may be extended with input facilities, gadgets for presenting overviews and the possibility of
adding additional constraints. Nevertheless, as a piece of advice, when developing a Multi-user Agenda Support System, follow
the KISS principle: Keep It Simple ...
Implementation
In principle, the phase of implementation
follows on from the design phase.
In practice, however,
the products of design may often
only be regarded as
providing a post hoc justification
of the actual system.
As noted, for instance, in
Design is of particular importance in projects
that require long-term maintenance.
Correcting errors or adapting the functionality of
the system on the basis of code alone
is not likely to succeed.
What may help, though, are tools that extract
explanatory information from the code.
Testing and maintenance
The language Eiffel, described by
The C++ language (Stroustrup, 1991) has a somewhat different history. It was originally developed as an extension of C with classes. A primary design goal of C++ has been to develop a powerful but efficient language. In contrast to Smalltalk and Eiffel, C++ is not a pure object-oriented language; it is a hybrid language in the sense that it allows us to use functions in C-style as well as object-oriented constructs involving classes and inheritance.
The newest, and perhaps most important, object-oriented language around is Java, which owes its popularity partly to its tight connection with the Internet. Java comes with a virtual machine that allows for running Java programs (applets) in a browser, in a so-called sandbox, which protects the user from possibly malicious programs.
As the final language in this brief overview, I wish to mention the distributed logic programming language DLP (see Eliëns, 1992). The DLP language combines logic programming with object-oriented features and parallelism. I mention it, partly because the development of this language was my first involvement with OOP. And further, because it demonstrates that other paradigms of programming, in particular logic programming, may be fruitfully combined with OOP. The language DLP provides a high level vehicle for modeling knowledge-based systems in an object-oriented way.
A more extensive introduction to the Smalltalk, Eiffel, C++, Java and DLP languages is given in the appendix.
Equally focused on understanding structure and behavior,
but more from a modeling perspective, is the
Unified Modeling Language (UML), which has resulted
from a common effort of
leading experts in object-oriented analysis and design,
Grady Booch, Ivar Jacobson and James Rumbaugh, also known as
`The Three Amigos'.
UML, indeed the second trend,
aims at providing the full notational repertoire needed
for modeling every conceivable structural
and behavioral aspect of software systems.
An excellent introduction to UML is given in
With respect to technology, the field is still very much in flux.
A dominant factor here is the rapid increase in Internet usage and,
more in particular, the Web.
The Web has boosted the interest of the IT business world
in the deployment of distributed object or component
technology to extend their range of business.
Nevertheless, the very existence of this infrastructure
is in itself somewhat embarrassing, in that the Web
and the technology around which it is built is not
object-oriented. Perhaps it should be, but it simply isn't.
Our embarrassment is aggravated when we observe,
following
Challenges
More concretely, a major challenge for the next decade will be to develop and deploy frameworks that operate in areas such as finance, medical care, social welfare and insurance. This is explicitly not only a technical problem, but also a problem of coming to agreement with respect to the abstractions and corresponding standards that provide the computational infrastructure for these domains. Also on my wish-list is the separation of logic and control, by which I mean the decoupling of the more or less invariant functionality as may be provided by for example business objects and business processes and the more variable logic that controls these processes. In other words, it is necessary that the business logic is made explicit and that it is factored out of the code effectuating it.
Both our hardware and software technology are improving rapidly. Yet, we are still stuck with the WIMP interfaces. In my opinion, it is time for a change. What I would like to see is an exploration of 3D user interfaces and 3D visualisations of the structure and processes underlying information-intensive applications. Although not specifically related to object-oriented software development, this is an area where object orientation can prove its worth.
When we think about real applications, for example information or business services on the Internet, they are usually the kind of applications that we may characterize as knowledge-intensive applications. In a somewhat idealistic vision, we may think of application development that consists of composing components from perhaps even a number of frameworks, so that we don't have to bother with the tiresome details of network access and GUI development. Then what remains to be done is to glue it all together, and provide the information and knowledge that enables our application to deliver its services. Partly we can rely on database technology for the storage and retrieval of information. But in addition we will need other declarative formalisms for expressing, for example, our business logic or, as another example, for expressing the synchronisation constraints of our multimedia presentation.
Considering Web applications, even as they are today, we see applications that consist of a mixture of code, tools and information. The phrase fragmented applications seems apt here. For example a store selling books on the Internet needs everything ranging from Javascript enabled webpages, to a secure CORBA-based accounting server. It is very likely that such applications will be developed partly by composing already existing components.
In his book, Summary
Nowadays there are many books that may serve
as a starting point for reading about OO.
Dependent on your interest, you may look
at
draft version 0.1 (15/7/2001)