Lecture
Trends in design
Generations of programming languages. Looking back over the short but colorful history of the development of programming, one cannot fail to notice two trends that have succeeded one another:
Most contemporary commercial software systems are larger and substantially more complex than their predecessors were even a few years ago. This growth in complexity has given rise to a great deal of applied research into design methodology, especially into decomposition, abstraction, and hierarchy. The creation of more expressive programming languages has added to the achievements in this area. A trend has emerged of moving from languages that tell the computer what to do (imperative languages) toward languages that describe the key abstractions of the problem domain (declarative languages).
Wegner grouped some of the best-known high-level languages into four generations according to which language constructs first appeared in them:
| FORTRAN I | Mathematical formulas |
| ALGOL-58 | Mathematical formulas |
| Flowmatic | Mathematical formulas |
| IPL V | Mathematical formulas |
| FORTRAN II | Subroutines, separate compilation |
| ALGOL-60 | Block structure, data types |
| COBOL | Data description, file handling |
| Lisp | List processing, pointers, garbage collection |
| PL/I | FORTRAN+ALGOL+COBOL |
| ALGOL-68 | A more rigorous successor to ALGOL-60 |
| Pascal | A simpler successor to ALGOL-60 |
| Simula | Classes, abstract data |
In each successive generation the abstraction mechanisms supported by the languages changed. First-generation languages were oriented toward scientific and engineering applications, and the vocabulary of that domain was almost exclusively mathematical. Languages such as FORTRAN I were created to simplify the programming of mathematical formulas, so as to free the programmer from the difficulties of assembler and machine code. The first generation of high-level languages was a step that brought programming closer to the problem domain and away from the particular machine. In the second generation of languages the main trend became the development of algorithmic abstractions. At that time the power of computers was growing rapidly, and the computer industry made it possible to expand their areas of application, especially in business. The chief task became instructing the machine what to do: first read these employee questionnaires, then sort them and print out the results. This was another step toward the problem domain and away from the particular machine. At the end of the 1960s, with the appearance of transistors and then integrated circuits, the cost of computers dropped sharply while their performance grew almost exponentially. It became possible to solve ever more complex problems, but this required the ability to process the most varied types of data. Languages such as ALGOL-68 and then Pascal came to support data abstraction. Programmers were able to describe their own data types. This became yet another step toward the problem domain and away from being tied to a particular machine.
The 1970s were marked by a frenzied burst of activity: some two thousand different languages and their dialects were created. The inadequacy of earlier languages for writing large software systems had become obvious, and so the new languages had mechanisms that removed this limitation. Only a few of these languages managed to survive (try finding a recent textbook on the languages Fred, Chaos, or Tranquil), yet many of their principles were reflected in new versions of earlier languages. Thus we obtained the languages Smalltalk (an innovatively reworked legacy of Simula), Ada (a successor to ALGOL-68 and Pascal with elements of Simula, Alphard, and CLU), CLOS (which united Lisp, LOOPS, and Flavors), C++ (which arose from the marriage of C and Simula), and Eiffel (descended from Simula and Ada). Of the greatest interest for the discussion that follows is the class of languages called object-based and object-oriented, which best meet the task of object-oriented decomposition of software.
The topology of first- and early second-generation languages. To clarify what has been said, let us consider the structures characteristic of each generation. Figure 2-1 shows the topology typical of most first-generation languages and of the first stage of the second generation. By "topology" we mean the basic elements of a programming language and their interaction. It may be noted that for languages such as FORTRAN and COBOL the basic building block is the subroutine (the paragraph in COBOL terms). Programs implemented in such languages have a relatively simple structure, consisting only of global data and subroutines. The arrows in the figure indicate the influence of subroutines on the data. During development one can logically separate data of different kinds, but the mechanisms of the languages provide practically no support for such separation. An error in any part of the program can have far-reaching consequences, since the data area is open to all subroutines. In large systems it is difficult to guarantee data integrity when changes are made to any part of the system. In the course of operation, confusion arises within a short time because of the large number of cross-connections among subroutines, tangled control schemes, and unclear meaning of the data, which threatens the reliability of the system and definitely reduces the clarity of the program.

Fig. 2-1. The topology of first- and early second-generation languages.
The topology of late second- and early third-generation languages. Beginning in the mid-1960s, the role of subroutines as an important intermediate link between the problem being solved and the computer began to be recognized . Shaw notes: "The first software abstraction, called procedural abstraction, follows directly from this pragmatic view of software... Subroutines arose before 1950, but at that time they were not appreciated as an abstraction... They were regarded as devices that simplified the work... But it soon became clear that subroutines are abstract program functions" .
The use of subroutines as an abstraction mechanism had three significant consequences. First, languages were developed that supported a variety of parameter-passing mechanisms. Second, the foundations of structured programming were laid, which found expression in language support for mechanisms of subroutine nesting and in the scientific study of control structures and scoping. Third, methods of structured design arose, encouraging developers to build large systems using subroutines as ready-made building blocks. The architecture of the programming languages of this period (Fig. 2-2), as one would expect, represents variations on the themes of the previous generation. Certain improvements were introduced into it, in particular the control of algorithmic abstractions was strengthened, but the problem of programming "in the large" and of data design remained unsolved.
The topology of late third-generation languages. Beginning with FORTRAN II and thereafter, an important new structuring mechanism began to develop for solving the problems of programming "in the large." The growth of software projects meant an increase in their size and in the teams of programmers, and consequently the need for independent development of separate parts of a project. The answer to this need was the separately compiled module, which at first was simply a more or less arbitrary collection of data and subroutines (Fig. 2-3). Into such modules were gathered subroutines that, it seemed, were most likely to change together, and few regarded them as a new technique of abstraction. In most languages of this generation, although modular programming was supported, no rules were introduced to ensure the consistency of module interfaces. A programmer composing a subroutine in one of the modules might, for example, expect that it would be called with three parameters: a real number, an array of ten elements, and an integer denoting a Boolean value. But in some other module, contrary to the author's assumptions, this subroutine might be called by mistake with actual parameters in the form of: an integer, an array of five elements, and a negative number. Similarly, one module might set up a common data area and consider it its own, while another module might violate this assumption by freely manipulating that data. Unfortunately, since most languages provided at best rudimentary support for abstract data and types, such errors were detected only when the program was executed.

Fig. 2-2. The topology of late second- and early third-generation languages.
The topology of object-based and object-oriented languages. The significance of abstract data types in resolving the problem of system complexity was well expressed by Shankar: "Abstraction achieved through the use of procedures is well suited to describing abstract actions, but is not suited to describing abstract objects. This is a serious shortcoming, since in many practical situations the complexity of the objects one has to work with constitutes the main part of the complexity of the whole problem" . Recognizing this leads to two important conclusions. First, design methods based on data flows arise, which bring order to data abstraction in algorithmically oriented languages. Second, a theory of types appears, which is embodied in languages such as Pascal.
The natural culmination of the realization of these ideas, which began with the language Simula and was developed in subsequent languages in the 1970s and 1980s, was the comparatively recent appearance of such languages as Smalltalk, Object Pascal, C++, CLOS, Ada, and Eiffel. For reasons that we shall shortly explain, these languages came to be called object-based or object-oriented. Figure 2-4 shows the topology of such languages as applied to problems of small and medium complexity. The basic element of construction in these languages is the module, composed of logically related classes and objects, rather than the subroutine, as in first-generation languages.

Fig. 2-3. The topology of late third-generation languages.
In other words: "If procedures and functions are verbs, and data are nouns, then procedural programs are built out of verbs, and object-oriented ones out of nouns" . For the same reason, the structure of programs of small and medium complexity under the object-oriented approach is represented by a graph rather than a tree, as is the case with algorithmic languages. Moreover, the area of global data is reduced or absent. Data and actions are now organized in such a way that the basic logical building blocks of our systems become classes and objects rather than algorithms.
At present we have advanced far beyond programming "in the large" and are faced with programming "in the huge." For very complex systems, classes, objects, and modules are necessary but not sufficient means of abstraction. Fortunately, the object approach scales and can be applied at ever higher levels. Clusters of abstractions in large systems can be represented as a multilayered structure. At each level one can identify groups of objects interacting closely to solve a problem at a higher level of abstraction. Within each cluster we shall inevitably find the same kind of set of interacting abstractions (Fig. 2-5). This corresponds to the approach to complex systems set out in Chapter 1.
The fundamentals of the object model
The methods of structured design help simplify the process of developing complex systems by using algorithms as ready-made building blocks. Similarly, the methods of object-oriented design were created to help developers apply the powerful expressive means of object-based and object-oriented programming, which uses classes and objects as its blocks.
But the object model reflects many other factors as well. As shown in the sidebar below, the object approach has proved itself as a unifying idea for all of computer science, applicable not only in programming but also in the design of user interfaces, databases, and even computer architectures. The reason for such breadth is that an orientation toward objects allows us to cope with the complexity of systems of the most varied nature.

Fig. 2-4. The topology of small and medium-sized applications in object-based and object-oriented languages.
Object-oriented analysis and design reflect an evolutionary rather than a revolutionary development of design; the new methodology does not break with earlier methods but is built with prior experience in mind. Unfortunately, most programmers at present are formally and informally trained to apply only the methods of structured design. To be sure, many good designers have created and continue to refine a great many software systems on the basis of this methodology. However, algorithmic decomposition helps only up to a certain limit, and a turn to object-oriented decomposition is necessary. Moreover, in attempts to use languages such as C++ or Ada as traditional, algorithmically oriented ones, we not only lose their inner potential - the result will most likely be even worse than when using the ordinary languages C and Pascal. To give an electric drill to a carpenter who has never heard of electricity is to have it used as a hammer. He will bend a few nails and smash his fingers, because an electric drill is of little use as a substitute for a hammer.
OOP, OOD, and OOA
Having inherited from many predecessors, the object approach has unfortunately also inherited a confused terminology. A programmer in Smalltalk uses the term method, in C++ the term virtual function, in CLOS generic function.

Fig. 2-5. The topology of large applications in object-based and object-oriented languages.
In Object Pascal the term used is type coercion, while in the Ada language the same thing is called type conversion. To reduce the confusion, one should define what is object-oriented and what is not. Definitions of the most commonly used terms and concepts will be found in the glossary at the end of the book.
The term object-oriented, in Bhaskar's opinion, is "worn out to the point of losing its meaning, like "motherhood," "apple pie," and "structured programming"" . One may agree that the concept of the object is central to everything relating to object-oriented methodology. In Chapter 1 we defined an object as a tangible entity that clearly exhibits its behavior. Stefik and Bobrow define objects as "entities that combine procedures and data, since they perform computations and preserve their own local state" . Defining an object as an entity answers the question to some degree, but the main thing in the concept of an object is still the union of the ideas of data abstraction and algorithms. Jones refines this concept as follows: "In the object approach the emphasis shifts to the concrete characteristics of the physical or abstract system that is the subject of software modeling... Objects possess an integrity that must not - and in fact cannot - be violated. An object can only change state, behave, be managed, or enter into a definite relation with other objects. In other words, the properties that characterize an object and its behavior remain unchanged. For example, an elevator is characterized by the unchanging properties that it can move up and down while remaining within the confines of its shaft... Any model must take these properties of the elevator into account, since they are part of its definition" [32].
| The fundamentals of the object model
Yonezawa and Tokoro testify: "the term "object" appeared practically independently in various fields connected with computers, and almost simultaneously in the early 1970s, to denote that which can have various manifestations while remaining integral. In order to reduce the complexity of software systems, the components of a system or fragments of represented knowledge were called objects" . In Levy's view, the object-oriented approach was connected with the following developments:
Closely connected with object-oriented architecture are object-oriented operating systems (OS). Dijkstra, working on the multiprogramming system THE, was the first to introduce the concept of a machine with levels of state as a means of building a system [18]. Among the first object-oriented operating systems the following should be noted: Plessey/System 250 (for the Plessey 250 multiprocessor), Hydra (for the CMU C.mmp), CALTSS (for the CDC 6400), CAP (for the Cambridge CAP computer), UCLA Secure UNIX (for the PDP 11/45 and 11/70), StarOS (for the CMU Cm*), Medusa (also for the CMU Cm*), and iMAX (for the Intel 432) [19]. The next generation of operating systems, such as Microsoft Cairo and Taligent Pink, will in all likelihood be object-oriented. The most significant contribution to the object approach has been made by object-based and object-oriented programming languages. The concepts of classes and objects were first introduced in the language Simula 67. The Flex system and the dialects that followed it - Smalltalk-72, -74, -76 and, finally, -80 - took the methods of Simula as their basis and carried them through to their logical conclusion, performing all actions on the basis of classes. In the 1970s a number of languages were created that realized the idea of data abstraction: Alphard, CLU, Euclid, Gypsy, Mesa, and Modula. Then the methods used in the languages Simula and Smalltalk were employed in traditional high-level languages. The introduction of the object-oriented approach into C gave rise to the languages C++ and Objective C. On the basis of the language Pascal there arose Object Pascal, Eiffel, and Ada. Dialects of LISP appeared, such as Flavors, LOOPS, and CLOS (Common LISP Object System), with the capabilities of the languages Simula and Smalltalk. The features of these languages are set out in more detail in the appendix. The first to point out the necessity of building systems in the form of structured abstractions was Dijkstra. Later Parnas introduced the idea of information hiding [20], and in the 1970s a number of researchers, chiefly Liskov and Zilles [21], Guttag [22], and Shaw [23], developed mechanisms of abstract data types. Hoare supplemented these approaches with a theory of types and subclasses [24]. Database construction technologies, which developed fairly independently, also influenced the object approach [25], primarily thanks to the so-called entity-relationship model (ER) [26]. In ER models, first proposed by Chen [27], modeling proceeds in terms of entities, their attributes, and their interrelationships. Developers of ways of representing data in the field of artificial intelligence also made their contribution to the understanding of object-oriented abstractions. In 1975 Minsky put forward a theory of frames for representing real-world objects in pattern recognition and natural language systems [28]. Frames came to be used as an architectural basis in various intelligent systems. The object approach has been known since long ago. To the Greeks belongs the idea that the world can be regarded in terms of both objects and events. And in the seventeenth century Descartes noted that people usually have an object-oriented view of the world [29]. In the twentieth century this theme was developed by Rand in her philosophy of objectivist epistemology [30]. Later Minsky proposed a model of human thinking in which the human mind is regarded as a community of variously thinking agents [31]. He argues that only the joint action of such agents leads to meaningful human behavior. |
Object-oriented programming. So what is object-oriented programming (OOP)? We define it as follows:
Object-oriented programming is a methodology of programming based on representing a program as a collection of objects, each of which is an instance of some class, and the classes form an inheritance hierarchy.
Three parts can be distinguished in this definition: 1) OOP uses as its basic elements objects, rather than algorithms (the "part of" hierarchy, which was defined in Chapter 1); 2) each object is an instance of some particular class; 3) the classes are organized hierarchically (see the concept of the "is a" hierarchy in the same place). A program will be object-oriented only if all three of these requirements are observed. In particular, programming that is not based on hierarchical relationships does not belong to OOP but is called programming based on abstract data types.
In accordance with this definition, not all programming languages are object-oriented. Stroustrup put it this way: "if the term object-oriented language means anything at all, then it must mean a language having facilities for good support of the object-oriented style of programming... Providing such a style in turn means that it is convenient to use this style in the language. If writing programs in the OOP style requires special efforts or is impossible altogether, then this language does not meet the requirements of OOP" [33]. Theoretically it is possible to simulate object-oriented programming in ordinary languages such as Pascal and even COBOL or assembler, but this is extremely difficult. Cardelli and Wegner say that: "a programming language is object-oriented if and only if the following conditions are satisfied:
Support for inheritance in such languages means the possibility of establishing the "is-a" relationship ("is," "this is," "is a"), for example, a red rose is a flower, and a flower is a plant. Languages that do not have such mechanisms cannot be classified as object-oriented. Cardelli and Wegner called such languages object-based, but not object-oriented. According to this definition the object-oriented languages are Smalltalk, Object Pascal, C++, and CLOS, while Ada is an object-based language. But since objects and classes are elements of both groups of languages, it is desirable to use the methods of object-oriented design in both.
Object-oriented design. Programming above all implies the correct and efficient use of the mechanisms of particular programming languages. Design, by contrast, devotes its main attention to the correct and efficient structuring of complex systems. We define object-oriented design as follows:
Object-oriented design is a design methodology that combines the process of object decomposition with techniques for representing the logical and physical, as well as the static and dynamic, models of the system being designed.
This definition contains two important parts: object-oriented design 1) is based on object-oriented decomposition; 2) uses a variety of techniques for representing models that reflect the logical (classes and objects) and physical (modules and processes) structure of the system, as well as its static and dynamic aspects.
It is precisely object-oriented decomposition that distinguishes object-oriented design from structured design; in the first case the logical structure of the system is reflected by abstractions in the form of classes and objects, in the second by algorithms. At times we shall use the abbreviation OOD, object-oriented design, to denote the method of object-oriented design set out in this book.
Object-oriented analysis. The object model was influenced by an earlier model of the software life cycle. The traditional technique of structured analysis, described in the works of De Marco [35], Yourdon [36], Gane and Sarson [37], and with refinements for real-time modes in Ward and Mellor [38] and Hatley and Pirbhai [39], is based on the flows of data in the system. Object-oriented analysis (or OOA, object-oriented analysis) is directed at creating models of real-world reality on the basis of an object-oriented worldview.
Object-oriented analysis is a methodology in which the requirements for a system are perceived from the standpoint of the classes and objects identified in the problem domain.
How are OOA, OOD, and OOP related? On the results of OOA, models are formed on which OOD is based; OOD in turn creates the foundation for the final implementation of the system using the methodology of OOP.
Comments