1.1 Introduction
Object-Oriented Strategies
Object-oriented programming embodies in software structures a number of powerful design strategies that are based on practical and proven software engineering techniques. By incorporating support for these strategies in software structures, object-oriented programming enables the manageable construction of more complex systems of software than was previously possible. The nature of these software structures has been shaped by decades of software engineering experience. The basic design strategies that are embodied in object-oriented programming are presented in the Table 1.1. The design strategies evolved as techniques for dealing with complex natural and man-made system. Because these strategies are so fundamental, they are encountered in other contexts and in other programming language forms. What is stressed here is the relationship of these strategies to the design and construction of object-oriented software. These strategies are widely supported by existing object-oriented languages though different languages may present them in different ways and some languages may support other variations of each one. For example, some object-oriented languages have additional ways of supporting generalization.
The design strategies in object-oriented programming are effective for constructing software models of entities in the problem domain. In fact, some have argued that software design is largely about constructing a software model of the "real world" where each "real" entity is represented in the program by a corresponding software object; the software object simulates the actions and conditions of its real-world counterpart. The "programming as modeling" philosophy is most evident in three-dimensional virtual environments where the visual and auditory characteristics of the real world are simulated within the virtual world.
Table 1.1: Design Strategies Embodied In
Object-Oriented Programming | abstraction | simplifying to its essentials the description of a real-world entity | | separation | treating independently "what" an entity does from "how" it does it | | composition | building complex "whole" systems by assembling simpler "parts" in one of two basic ways: * association * aggregation | | generalization | identifying common elements among different entities in one of three ways: * hierarchy * polymorphism * patterns | |
A Plan of Study
The study of a body of material depends on the learning style of the individual and is often iterative in progression. Some people learn more efficiently by understanding the overall concepts first before proceeding to more concrete details. Other people learn more efficiently by intermixing abstract concepts and concrete examples. Regardless of the preference for a breadth-first approach or a depth-first approach, there are necessarily some ideas that must be learned before others because the ideas build on each other and are not independent. In both styles, a single reading is usually not sufficient. Backtracking and revisiting earlier concepts often enriches understanding and allows the formation of deeper insights.
The overall structure of the material presented here is shown in the Figure 1.1. The design strategies are shown at the left. It is possible to read about these design strategies top-to-bottom and obtain a broad overview of object-oriented concepts and ideas. At any point it is also possible to follow one of the arrows to the right. Following an arrow in this direction leads to a more concrete presentation of the concept and its eventual description in Java. There are six major milestones shown in the overall guide. Each milestone falls into one of three roles that are shown at the bottom of the figure. Each milestone is associated with a major design concept and represents a significant step forward in the practical skill of developing object-oriented software systems. The milestones, however, are ordered. It is not possible to proceed to a later one (one lower and more to the right in the figure) before all of the earlier ones (ones higher and to the left) have been completed.
Figure 1.1: Guide to Material | |
The milestones form a progression of roles as shown along the bottom of Figure 1.1. The simplest role is that of a programmer using a single class or several classes that has already been developed. Simple, but interesting systems will be constructed in this first role. Initially, a single class will be used to explore and master basic issues of creating and manipulating objects. Several more classes are then added, allowing the construction of simple systems of interacting objects. Learning how to use existing classes to create and manipulate objects is the first step in learning about object-oriented programming. This first role is important not only because it establishes the foundation for the basic concepts of object-oriented programming but also because this is the preferred role in developing real systems. As a user, you are able to benefit from the hard work already done by the designers and implementors of existing classes. The ability to use existing classes is one of the major benefits of software reuse, in general, and object-oriented programming in particular. The second role is that of a developer creating one or more new classes. Initially, each class captures an independent abstraction. More difficult, but more powerful, is the development of collections of related classes using one of the forms of generalization. These first two roles will be extensively used. The third role involves pattern - general organizations of classes and objects that have proven useful in solving commonly occurring design problems. This role will be explored, but only minimally. Mastery of the use of patterns requires considerable experience in building systems in one or more application domains.
Examples
Throughout the book, design concepts and programming constructs are illustrated and motivated by using continuing examples from two independent problem domains. One set of examples is based on graphical user interfaces (GUIs). The examples in this domain illustrate how object-oriented features of Java and the design strategies are applied in using a set of predefined GUI classes to build increasingly more complicated interfaces for small applications. The second set of examples is based on simulating a simple ecological environment. These examples use simple text output. Two independent sets of examples are used to give different examples of each concept and to allow a more varied choice of programming exercises. The examples continue from section to section and chapter to chapter so that their progressive development can be seen as more design strategies and Java features are presented. In some cases, an earlier design is revised to take advantage of a newly introduced strategy or feature.
The UML Design Notation
The ability to design an object-oriented system is simultaneously the most important skill for a software developer to possess and the most elusive to learn. Design skill is important because a system's design determines to what extent the system realizes most of the crucial software-engineering principles such as separation, encapsulation, information hiding and key design goals such as abstraction and generalization. The skill of good object-oriented design is elusive because, like all design activities, it cannot be reduced to a mechanical process. Good designers bring to their task both native ability that emerges as insight and inspiration, and experience gained through practicing the art of object-oriented design on numerous software projects. While insight, inspiration, and experience cannot be articulated, the heuristic rules and guidelines that characterize a good design may be elicited for study.
Designing an object-oriented system is aided by a semi-formal, high-level, graphical representation of classes, objects, and their relationships. The graphical nature of the representation allows easy visualization of the structural aspects of the system. The design is represented at a high level in that only the essential elements of the design are present independent of lexical details or the specifics of the implementation. Design representations are useful in that they externalize a designer's ideas so that they may be subject to analysis, evaluation, and comparison with alternative designs, act as a blueprint for those implementing the design, and document the final design for those performing later maintenance and extension.
There are three specific uses for a high-level, graphical design representation. First, the representation provides a language that quickly describes an idea being considered by a single designer or a design team. Because it can be quickly drawn and easily modified, the representation aids in thinking about the design and exploring design alternatives. Second, the representation is a means of documenting a design. The design document serves as a guideline for the implementor and, later, for those who will modify and maintain the system. The graphical and high-level nature of the representation are useful as a documentation device because only the essential structural elements are described, more detailed information is left to the code and other documentation. Third, once represented, the design may itself be the object of reuse. Commonly occuring design solutions may be identified, documented, and collected for use in situations other than the ones in which they were initially encountered. Students of object-oriented design should study the artifacts (designs) produced by good designers in much the same way that art students study the great masters, architecture students study famous structures and buildings, and writing students study the classics in literature. For students of object-oriented design, the notion of a design pattern has recently emerged and allows great (or at least commnly needed and fundamental) designs to be studied.
The Unified Modeling Language (UML) is used throughout the book to visually depict object-oriented designs. UML is a well-known graphical notation for representing the components and organization of an object-oriented system. As new Java language features are introduced throughout the book, the corresponding UML notation is presented. UML is a rich visual language, more comprehensive than is required for the purposes of this book. Thus, only a core subset of the UML notation is described here. There are many other books that describe the full UML notation.
General Organization
The material in this book is generally divided into two parts. The first part of the book, encompassing Chapters 1-6, focuses on object oriented programming concepts and Java as an object oriented programming language. These chapters are organized as shown in Figure 1.1. Chapter 1 briefly describes the design strategies that are listed on the vertical axis in Figure 1.1 and summarized in Table 1.1. Chapters 2 through 6 follow the progressive steps shown on the horizontal axis in Figure 1.1. Chapter 2 uses a single existing class to introduce the basic concepts of classes and objects. Several existing classes are used in Chapter 3 to show how small systems of objects can be built. Implementing a new class is described in Chapter 4. Chapter 5 presents concepts and techniques for designing, debugging, and documenting new classes. Chapter 6 explains how related classes can be organized in a way that allows them to be more easily understood, implemented, and extended using object-oriented inheritance. Design strategies for class hierarchies created by inheritance and design patterns are also described in Chapter 6.
The second part of the book contains material about advanced features of Java that are not inherently part of its object orientation, and libraries that accompany the Java distribution. These topics are included because they are fundamental to working in the Java environment for anything beyond simple demonstration examples. In fact, when people talk about "Java" they often are referring to the utilities provided by the libraries and not by the basic language itself. The tools, features, and libraries presented in this book are organized as follows: The Abstract Windowing Toolkit (Chapter 7) the classes, interfaces, and conventions for building graphical user interfaces Java using the standard AWT.
I/O (Chapter 8) the facilities defined in Java for transferring data to standard devices and files. These facilities allow for the handling of data in a text format, binary format, and object format.
Threads (Chapter 9) the mechanisms in Java for concurrent programming. This material shows how to create and synchronize independent, asynchronous activities.
Since Java and its libraries are something of a moving target, the material presented here conforms to the Java 2 release. Every effort has been made to use those features of Java and the libraries that are more central and less subject to change.
Lets get to it!
Exercises. 1. Search the world-wide web to find references to different object-oriented languages? How many can you find? Can you recognize in their descriptions any of the basic strategies identified above? 2. Search the world-wide web to find locations of other courses on object-oriented programming. What topics do these courses have in common? Save these links for reference during your study. 3. Look at the Free On-Line Dictionary of Computing . Find the definitions of the terms: * abstraction * aggregation * encapsulation * hierarchy * genericity * polymorphism
Save this link for future reference. http://foldoc.org 4. Look at the Java Virtual Library. Browse this library for ten minutes and report on three interesting things that you found. Keep this link for reference.
1.2 Abstraction
Abstraction is a design technique that focuses on the essential aspects of an entity and ignores or conceals less important or non-essential aspects. Abstraction is an important tool for simplifying a complex phenomenon to a level where analysis, experimentation, or understanding can take place. For example, in attempting to understand the mechanics of the solar system, early mathematicians and astronomers applied abstraction to a "planet", treating the planet as a body all of whose mass is concentrated at a single point. Such an abstraction ignores a wealth of details about each planet - its actual diameter, its atmospheric content, its average temperature, etc. However, these details are not relevant to understanding and modeling the basic orbital mechanics of the solar system.
In software, abstraction is concerned with both the "attributes" and "behavior" of entities. Attributes refer to the properties or characteristics associated with an entity while behavior refers to the set of actions that the entity can perform. In a software object, attributes are represented by data that is associated with the object. For a sales tracking system relevant attributes of a salesperson, see Figure 1.2.1, might be: name, number of vehicles sold, value of vehicles sold, list of customers, commission rate, total commissions. An action of the object corresponds to an operation or function associated with the object. Actions for a "salesperson" might include "sellCar", "reportIncome" and "increaseCommisionRate".
Abstraction is vital to creating tractable software objects because the real-world objects are far too complex to be captured in complete detail. Consider the simple "salesperson" object referred to above. A real "salesperson" has an identity, a family genealogy, a medical history, a genetic profile, a credit record, a set of talents, and many more. Similarly there is a rich set of actions of which the salesperson is capable (sellCar, answerPhone, buyHouse, haveChild, getSick, increaseCreditLimit, payBills, etc.). Trying to capture even a small part of this enormous detail in a software object is pointless. It is important to capture only those aspects of a "salesperson" that are relevant to the development of a particular system (e.g., the sales tracking system).
The objects in an object-oriented system are often intended to correspond directly to entities in the "real world". Objects such "salesperson" and "automobiles" that might occur in an automobile dealership tracking system correspond to the actual people on the staff of the dealership and the actual cars owned and sold by the dealership. The correspondence between the software objects and the real-world entity that they represent is often so direct and real that computer-based theft or fraud often involves tampering with the software objects that are trusted by others to correspond to real-world artifacts. This sense of correspondence is also expressed as the "program" being a "simulation" or " model" of the real-world, changes in one being reflected in the other. A "good" program is one which models or simulates accurately what is happening in the real-world.
Figure 1.2.1: Abstraction of a SalesPerson | |
The examples above motivate the following definition of abstraction:
Abstraction
A named collection of attributes and behavior relevant to modeling a given entity for some particular purpose.
A single entity may have many valid abstractions. While the genetic profile of a salesperson is not relevant to a sales tracking system, it may be relevant to a medical database system. This alternative abstraction is shown in Figure 1.2.2. Correspondingly, the medical database system developer would not consider the number of vehicles sold to be a relevant aspect. The name of the abstraction is useful to distinguish among different abstractions for the same entity and among abstractions for different entities. A critical part of object-oriented design is deciding which attributes and behavior to include in a given abstraction.
Figure 1.2.2: Abstraction of a Patient in a Medical Database | |
Properties of a Good Abstraction
While there may be many abstractions of the same entity, each abstraction should have certain properties that distinguish it as a "good" abstraction. These desirable properties are: well named the nature of an abstraction is conveyed by the name given to that abstraction. An abstraction is well named if the meanings, intuitions, impressions, and expectations implied by a name accurately reflect the nature of the abstraction. Whether a name is meaningful depends on the community of people who will use the abstraction. In some cases the name might be a technical term in an application domain that communicates an abstraction perfectly to the group of people in that application area but may mean little to a non-technical group. In other cases, abstractions for widely known entities (e.g., "Automobile" or "ZipCode") may have names recognizeable to a general population. coherent the abstraction should contain a related set of attributes and behavior that make sense from the viewpoint of the modeler. The attributes and behavior have to be what is needed and expected in a given setting. For example, defining a SalesPerson abstraction that consists of the attributes "commisionRate", "family", and "talents" is not a coherent abstraction, it does not make sense from the viewpoint of a designer building a sales tracking system. accurate the abstraction contain only attributes or behavior that are a part of the entity being modeled. The abstraction should not be endowed with "powers and abilities" far beyond those of the actual entity. While this principle is usually observed, there are special circumstances where this principle may be relaxed. For example, in a virtual environment it may be possible to "walk through the walls" in a scene. Such behavior clearly violates the behavior of real walls. minimal the abstraction should not contain extraneous attributes or behavior for the purpose for which it is defined. For example, adding a mailAddress or telephoneNumber attribute to the SalesPerson abstraction would be extraneous if these additional attributes were not required for the sales tracking system. complete the abstraction should contain all of the attributes and behavior necessary to manipulate the abstraction for its intended purpose. Assuming that the sales tracking system needed to know the commisionRate for each SalesPerson, an abstraction which did not include this attribute would not be complete.
These properties are clearly subjective and qualitative in nature. This fact implies that the ability to form good abstractions requires good judgment and the benefit of practice and experience.
Exercises.
1. Define plausible abstractions for an "automobile" from the point of view of: * the manufacturer, * the owner, * the governmental agency that licenses vehicles 2. Define plausible abstraction for a "book" from the point of view of: * a reader * a publisher * a bookstore 3. Define plausible abstractions for an "airplane flight" from the point of view of: * a travel agent * an airline company * a passenger * an airport 4. Evaluate your "automobile" abstractions against the four properties of good abstractions. 5. Evaluate your "book" abstractions against the four properties of good abstractions. 6. Evaluate your "airplane flight" abstractions against the four properties of good abstractions. 7. Identify a common, real-world entity and at least three different points of view that would lead to different abstractions of that entity.
1.3 Separation
Separation refers to distinguishing between a system's observable behavior and the means or mechanism by which this behavior is achieved. This is often described as separating "what" is to be done from "how" it is to be done. These and other pair of terms that reflect the idea of separation are shown in Table 1.3.1. Separation is also often expressed as drawing a boundary between the external, visible aspects of a system, the "what" part of the system, and the system's internal, hidden machinery, the "how" part of the system. Drawing such a boundary is useful both to those developing the system as well as to those using the system. Table 1.3.1: Terms reflecting separation | what | how | goals | plans | policy | mechanism | product | process | interface, specification, requirement | implementation | ends | means |
Separation is a powerful aid for developing complex systems because it allows the development activities to be organized into two distinct levels, often characterized as the design level and the implementation level. At the design level, the system's behavior can be stated, discussed, evaluated, communicated, and related to the behavior of other systems without concern for how the behavior is achieved. The activities at this level are simplified because the details of achieving the behavior are set aside. The implementation level focuses on how to achieve desired behavior. Since the systems behavior is fixed by the design, the activities at this level are concentrated on specifying the operational details and evaluating tradeoffs among different ways of manifesting the behavior. The two levels have different measures of quality. The design level is evaluated in terms of the utility of the system and how well it matches the user's requirements while the implementation level is evaluated in terms such as efficiency and maintainability.
Separation helps a user work with a complex system because the system's effects are often simpler to understand than the means needed to achieve the effect. For example, it is easier to know that "this button justifies the highlighted text in a document" than it is to understand the algorithmic details of how the words in the text are rearranged to align the margins so that distracting vertical runs of whitespace are avoided. Programmers are also familiar with this aspect of separation as it is present in all manuals and documentation. For example, a typical description of the command read(f, buffer, nbytes) is that the command transfers nbytes of data from file f to the specified buffer. The mechanism required to achieve this effect involves the disk hardware, software device drivers, the file system, the disk block management code, and run-time I/O library routines, none of which need be mentioned in the description of the read command.
Separation is reflected in the designed structure and operational use of many machines and organizations that we encounter in day-to-day life. Consider, for example, a television. From a development perspective it is useful to separate the television's external controls from its internal electronic components. The external controls are related to the television's effect: when should it produce its images, from what channel is the signal drawn, at what volume is the sound generated, and so on. There are numerous issues about these external controls such as where they are located, whether they are labelled by icons or words, are they operated by remote control, and so on. The internal electronic components react to the control and produce the required visual effect. The engineers who implement the internal components face many decisions on the arrangement of the circuits, the power supply required, the heat generated, and so on. From a user's perspective, only the television's external controls need to be understood. The separation conceals the internal electronic machinery from the user and allows the user to operate the television without concern for how the electronic components function. The television's outer shell or box is a physical manifestation of the separation.
A well established use of separation in software artifacts is the separation of an interface from an implementation of that interface. In the context of software system, an interface is defined as:
Software Interface the external, visible aspects of a software artifact by which the behavior of the artifact is elicited.
This definition captures the notion that the interface is the means by which the software artifact is manipulated from the outside. For a programmer (re)using the artifact as part of a system, the artifact's software interface is the external aspect that must be understood to use the software artifact. The terms "software interface" and "interface" will often be used interchangeably. There is some possibility of confusion, however, because Java has a specific programming construct that will be seen later which also uses the term "interface". Which meaning is intended should be clear from context, the more specific terms will be used when necessary for clarity.
Behind the visible interface is the implementation. The implementation carries out the work promised by the interface. Specifically for a software context, the implementation is defined as:
Software Implementation the programmed mechanism that realizes the behavior implied by an interface.
The implementation is viewed as the hidden, internal aspect of the software that is important only to the implementor. An implementation is said to satisfy an interface if the behavior specified by the interface is provided by the implementation.
The concept of separation as it is used in a software context can then be defined as:
Separation in software systems, the independent specification of a software interface and one or more software implementations of that interface.
The interface-implementation separation is suggested by Figure 1.3.1. Separation is often viewed as establishing a "contract" between a software artifact and the code using that artifact. The artifact's interface represents the terms of the contract. The contract is a guarantee to the using code that the implementation will provide the promised behavior. The contract is also a guarantee to the implementation that it is required to provide only that behavior advertised in the interface. The contract may also be viewed from the perspective of the people developing the system. In this case, the contract is an understanding between the developer programming the implementation of a software artifact and the developer using the artifact through its interface. Problems in developing sofware systems often arise because the contract between the developers is not understood the same way by both parties or because the original contract was later found not to be sufficient. Figure 1.3.1: Separation of Interface from Implementation | |
Separation is evident in software systems in several different ways. Manual pages for libraries describe only the interface properties of individual operations without describing how any of the operations is implemented. A complex layer of software (e.g., a windowing system, or a networking environment) may be described by an Application Programmer's Interface (API). The API defines what data structures and facilities are available for use by the application programmer without defining how the structure and facilities are implemented. Another example is a software standard. A standard is a commonly accepted definition of a service (e.g., the TCP/IP communication protocols standard). Such a standard defines the external behavior that a compliant system must exhibit but leaves the implementor free to decide how to implement that behavior.
Separation provides flexibility in developing a system because different implementations may satisfy the same interface. The several implementations may differ in time or space efficiency, purchase price, maintainability, documentation quality, reliability, or other non-functional characteristics. If separation is fully observed, one implementation for a given interface can be replaced by a different implementation of that interface without altering the overall operation of the larger system of which it is a part. The ability to associate different implementations with the same interface is shown in Figure 1.3.2. Two different interchangeable implementations of the same interface are said to be "plug compatible"; unplug the current implementation and plug in its replacement. Certainly many non-software products take advantage of this interchangeability: tires on a car, speakers on a stereo, and monitors on a computer.
Figure 1.3.2: Interchangeability of Implementations | |
Through separation a single implementation can satisfy simultaneously several interfaces. In such a case, the implementation contains the union of all of the methods required by each of the interfaces (and possibly additional methods that are not used by any of the current interfaces). Figure 1.3.3 shows a single implementation, named Graphics, that contains two methods, DrawText and DrawShape. Two interfaces are also shown: TextInterface that defines only a DrawText method, and ShapeInterface that defines only a DrawShape method. Clearly the Graphics implementation satisfies both of these interfaces. As shown in the figure, each interface provides a different view of the implementation. Each view may expose only a subset of the implementation's full capabilities. Such a restricted view is useful in isolating those capabilities that are required in a given situation or by a specific part of the system. Isolating the most limited set of capabilities needed makes it possible to replace a more capable implementation (e.g., Graphics) that may contains unneeded operations (e.g., DrawShape) by a smaller implementation that contains only those methods defined in the more limited interface. For example, suppose that a part of the system needed only the methods defined in the TextInterface. This need could be satisfied by a more limited class that implements only the TextInterface and not the ShapeInterface. Because it is more specific, the smaller implementation may be more efficient in execution time, need less memory, be easier to maintain, or more portable across different platforms.
Figure 1.3.3: Interchangeability of Implementations | |
Exercises. 1. Explain how separation is used in the following commonly occurring, real-world entities: * a telephone * a package or postal delivery service * a stereo or television * a restaurant. 2. Explain how separation is used in the following software entities: * an operating system * a web browser * a text editor * a compiler 3. Look at the documentation for your computer system and identify three instances of separation. For each instance, hypothesize several aspects of the implementation that are hidden by the separation.
1.4 Classes, Objects, and Abstract Interfaces
Software development centers on rendering abstractions in a form that allows them to be manipulated within a software system. An abstraction was described as a collection of attributes and a behavior. As shown in Figure 1.4.1, the attributes of an abstraction are mapped to a set of data (variables, array, lists, complex data structures, etc.) and the behavior of an abstraction is mapped to a set of methods (also known as operations, functions, actions). The rendering of abstractions in software has always been the implicit goal of programming though it may have been overshadowed by more mechanical considerations. What object-oriented programming brings to the task of capturing abstractions in software are more sophisticated structures, namely classes, objects, and interfaces, for representing abstractions. These new software structures permit abstractions to be represented more easily, more directly, and more explicitly.
Figure 1.4.1: Mapping Abstractions to Software | |
Class
A class defines the specific structure of a given abstraction (what data it has, what methods it has, how its methods are implemented). The class has a unique name that conveys the meaning of the abstraction that it represents. The term "class" is used to suggest that it represent all the members of a given group (or class). For example, a SalesPerson class might represent all individuals in the group of "people selling cars at an automobile dealership". An object represents a specific member of this group.
Separation of interface from implementation is used to divide the class into two parts, commonly referred to as the private part and the public part. The data and methods are mapped to these two parts of the class as shown in Figure 1.4.2. This separation of private data and code from the visible names of methods is motivated by long years of software engineering experience showing that it is important to protect the data against unexpected, unwanted, and erroneous access from other parts of the software that is manipulating the object. The separation also serves to hide the algorithm used to perform a method.
Figure 1.4.2: The General Structure of a Class | |
The relationship of a class to abstraction and separation is reflected in the following definition:
Class
A named software representation for an abstraction that separates the implementation of the representation from the interface of the representation.
The obvious similarity in the definitions of the terms "abstraction" and "class" underscores how explicitly and directly a class is meant to model a real-world entity.
It is interesting to note that there are object-oriented languages that do not have classes but use other techniques to achieve a similar effect. Languages, like Java, C++, and others that have a class concept are referred to as "class-based languages."
Object
While a class defines the structure of an entire collection of similar things (e.g., anyone who is a SalesPerson), an object represents a specific instance of that class (e.g., the sales person whose name is "John Smith", who has a commission rate of 15%, etc.). The class definition allows the common structure to be defined once and reused when creating new objects that need the structure defined by the class. An object's properties are exactly those described by the class from which it was created.
The key aspects of the object structure, as shown in Figure 1.4.3, mirrors the structure of the class from which it was created. As with a class, the two main parts of an object are its: * implementation: the data and the implementation of the methods that are hidden inside of the object, and * Interface: the collection of all methods that are visible outside of the object.
In the figure below, the methods that can be invoked from outside the object are shown as small dark circles on the border of the object's boundary. The code that implements the methods is part of the object's implementation and is hidden inside the object. The term "encapsulation" is often used to describe the hiding of the object's implementation details. It is common to read that "an object encapsulates its data". Encapsulation is defined as:
Encapsulation
In object-oriented programming, the restriction of access to data within an object to only those methods defined by the object's class.
The notion of encapsulation is fundamental to an understanding of objects and object-oriented programming.
Figure 1.4.3: The General Structure of an Object | |
The term instantiation is used to describe the act of creating an object from a class and the object is called an instance of the class. Numerous instantiations can be made of a given class, each yielding a distinct object. This leads to definition of an object as:
Object
A distinct instance of a given class that encapsulates its implementation details and is structurally identical to all other instances of that class.
This definition highlights the fact that all objects that are instances of a given class are structurally identical: they have the same arrangement of data and can respond to the same set of method invocations. However, as shown in Figure 1.4.4, each object may, and usually does, have different values for its data. In the figure there are two instances of the SalesPerson class. Both objects encapsulate the same type of data (name, commissionRate, and totalSales). The two objects currently have different values for the name and totalSales data but they currently have the same value for the commissionRate data. Both objects have two methods that can be invoked: sellCar and reportSales. Figure 1.4.4: Multiple Instances of a Class | |
The relationship between a class and the objects that can be created using the class is often likened to that of a factory and the things produced by that factory. An automobile factory produces automobiles in the same way that an Automobile class can be used to create Automobile objects. Also, an Automobile class can produce only Automobile objects just as an automobile factory can produce only automobiles, not vacuum cleaners or rocket ships.
Classes and objects are often discussed by developers in life-like, personal, anthropomorphic terms. A developer might say that "The class should not be responsible for that." or "I expect the object to reply to inquiries about its current state.". The developer may even assume the identity of the object to better understand the role of the object in a system's design. In this mode, a developer may ask "What is expected of me?", "How will I be able to do that?", or "Why do you want to know that about me?". Questions such as these often lead a developer to a better intuitive understanding of an objects and its relation to other objects. The personification of objects reflects the correspondence developers see between the real world entity and its the abstraction, on one hand, and the classes and objects that are their software counterparts, on the other. This view also reflects the autonomy and encapsulation assigned by designers to objects and classes.
A simple and popular design technique is intuitively related to the anthropomorphic view of objects. This technique focuses on identifying for each object (or class of objects) its responsibilities and its collaborators. An object's (or class's) responsibilities are those properties or duties the object (or class) is obligated to maintain or perform. An object's (or class's) collaborators are those other objects (or classes) with which the given object (or class) must interact. The terms "responsibilities" and "collaborators" reflect the anthropomorphic view of objects.
Abstract Interfaces
The advantages of separation are only partially realized by the class structure. In a class, separation achieves a simplifying effect because the developer using the class only needs to understand the class's interface and not the class's implementation. However, because a class binds the interface of the class to the class's single implementation, a class can have neither interchangeable implementation nor can a class satisfy several interfaces. Since these later advantages of separation are useful in building sophisticated software systems, a structure in addition to a class is needed.
An abstract interface is a programming structure that realizes more completely than does a class the advantages of separation. Similar to a class, an abstract interface captures the behavior of some abstraction by defining a collection of methods. Unlike a class, the abstract interface does not provide an implementation of the behavior; the implementation is provided by any class that defines and implements the behavior specified by the abstract interface. These properties of an abstract interface lead to the following definition:
Abstract Interface a named software representation for an abstraction's behavior that is intended to be implemented by one or more classes.
Notice that an abstract interface captures the behavior of an abstraction and not is attributes. Because an interface contains no implementation it has no hidden, private section; all of the methods in an interface are public, visible methods.
An abstract interface can be used to manipulate objects. As shown in Figure 1.4.5, the object must be an instance of a class that implements the abstract interface. The term implements means that the behavior defined by the abstract interface is a subset (or the same as) the behavior defined and implemented by the class. This arrangement is type-safe since any method defined in the abstract interface is guaranteed to be implemented by the object. This type-safety can be checked at compile-time.
Figure 1.4.5: Interchangeability of Objects Using an Interface | |
An abstract interface achieves the full advantages of separation. First, as shown in Figure 1.4.5, interchangeability is achieved because several classes can implement the same abstract interface. The objects of these classes can be manipulated through the same, single abstract interface. It is even possible, and often the case, that a single abstract interface will be bound to objects of different classes at different times during the program's execution. Notice that the manipulator of the object does not need to know the class of the object - only the abstract interface need be known by the manipulator. Second, as shown in Figure 1.4.6, a single class can implement multiple abstract interfaces. It is only required that the class implement all of the methods defined in each abstract interface. In this way an object of a class can be accessed via different interfaces. Here again, the manipulator of the object only needs to know the abstract interface and not the actual class of the object being manipulated.
Figure 1.4.6: Manipulating and Object via Different Interfaces | |
Exercises. 1. Answer True or False to each of the following statements: * Many objects can be created from the same class. * A single object may belong to several classes. * Two objects of the same class must have the same data and methods. * Two objects of the same class must have the same values for their data. * The data of a class is typically in the public part because it is a known attribute of the entity being modelled. 2. Define relevant attributes and behavior for each of the following entities: * a telephone * a calculator * an automobile * a patient in a hospital * a vending machine 3. For each of the entities in the previous question, show the general organization (the name, public part, and private part) of a class.
1.5 Composition
1.5.1 Concept of Composition
Composition deals with a single, complex system as an organization of more numerous but simpler systems. Composition is often used to study and explain complex human organizations (the government is divided into three major branches), complex biological systems (a human being consists of a respiratory system, a circulatory system, an immune system, a nervous systems, a skeletal system, etc.), complex machines (an aircraft consists of a propulsion system, a control system, a navigation system, etc), and complex programs (an operating systems consists of a user interface, a file system, a network system, a memory management system, etc). With composition it is important to understand both the individual (simpler) parts and the relationships or collaborations among them.
As a constructive activity, composition refers to the assembly of interacting parts to form a whole. The part-whole relationship is a fundamental one in object-oriented programming. One of the major goals of object-oriented programming, software reuse, is accomplished by composing existing objects (the parts) in new and different ways to form new or objects or systems (the whole). Composition might be viewed as the "Lego" approach to software development - using standardized, specialized parts to construct a wide range of interesting artifacts.
Composition can be defined as:
Composition an organized collection of components interacting to achieve a coherent, common behavior
The "part-whole" relationship is often expressed as a "has-a" relationship. For example, in referring to the relationship between an automobile and the automobile's windshield, the statement "the automobile has a windshield" suggests why the the term "has-a" is used to describe part-whole compositions.
There are two forms of composition named association (also acquaintance) and aggregation (also containment). These two forms of composition are similar in that they are both part-whole constructions. What distinguishes aggregation from association is the visibility of the parts. In an aggregation only the whole is visible and accessible. In an association the interacting parts are externally visible and, in fact, may be shared between different compositions. A soda machine is an example of aggregation. The machine is a whole composed of several internal parts (a cooling system, a coin acceptor, a change maker, a soda supply). These internal parts are not visible or accessible to the normal user of the soda machine. A computer workstation is an example of composition using association. The workstation consists of a keyboard, a mouse, a monitor, a printer, a modem and a processor. Each of these interacting parts are visible to the user and can be directly manipulated by the user.
In some cases the more generic term "composition" will be used in favor of the more precise terms "association" or "aggregation". This occurs when the statement applies to both forms, when the difference between the two forms is much less important than the general idea of forming a whole from parts, or when the precise form of composition can be inferred from context.
Both forms of composition are useful. Aggregation offers greater security because its structure is usually defined in advance and cannot be altered at run-time. The implementor of the aggregation is secure in the knowledge that the integrity of the aggregation and its proper functioning cannot be adversely affected by direct interference with its internal mechanisms. Association offers greater flexibility because the relationships among the visible parts can be redefined at run-time. An association can, therefore, be made to adapt to changing conditions in its execution environment by replacing one or more of its components. Interesting design decisions can revolve around which form of composition to use, balancing a need for security against a need for greater flexibility at run-time.
The two forms of composition are frequently used together and in combinations. The computer workstation was given as an example of an association among a mouse, keyboard, processor, modem and monitor. However, as shown in Figure 1.5.1, the processor of the computer workstation is itself an aggregation that consists of hidden parts including a CPU (processor chip), memory, and a disk. More detailed examination of the processor chip would show it to be an association of smaller elements some of which might be other associations or aggregations of parts. Objects may also exhibit a complex structure formed by layers of associations and aggregations. The subsections below on association and aggregation will give examples of such structures.
Figure 1.5.1: Association and Aggregation | |
1.5.2 Composition Using Association
Association is a part-whole organization in which the "whole" is exactly defined by the "parts" and the relationships among the parts. The parts of the composition maintain their identity, their external visibility, and their autonomy in the composition. The parts are often viewed as peers, collaborators or acquaintances, such terms reflecting the primacy of the parts in the part-whole composition. In some sense, the whole is the sum of its parts. This leads to the following definition of association.
Association a composition of independently constructed and externally visible parts.
The organization of a computer workstation, depicted in Figure 1.5.2, is a typical example of a real-world association. Each of the parts shown in this figure is externally visible and can be manipulated in its own right. The notion of "computer workstation" refers to the particular assembly of these parts in a way that gives rise to the functionality expected of a computer workstation. The expectations of a computer workstation would not be met by an assembly of fewer parts (i.e., no monitor), extraneous parts (i.e., two keyboards), or the correct parts associated differently (i.e., the mouse connected to the modem).
Figure 1.5.2: A Real-World Association | |
An association among software objects is created when an object contains references to other objects. An example of an association among objects is shown in Figure 1.5.3. This association creates a simple one-second timer that is displayed in a graphical user interface window and which is controlled by "Start" and "Stop" buttons. The Clock object is responsible for determining the end of each one second interval of time. At the end of each such interval the Clock object invokes an operation on the Counter object to increment its interval value.
Incrementing its value causes the Counter object to send a string representation of its value to the Message object. The Message object is responsible for displaying its current value in a drawable Canvas object. The Canvas is responsible for maintaining the consistency between the images or text drawn in the Canvas and what is displayed in the Canvas's portion of the user interface. In addition to providing an area on the user's display for the Canvas, the Frame also provides an area for a Panel object to display the "Start" and "Stop" button controls that it manages.
Figure 1.5.3: An Association of Objects | |
One advantage of an association is that the parts may be shared between different compositions. This is easily accomplished by having the same object be connected to (referred to) from two objects each of which is in a different composition. Using the computer workstation example mentioned above, it is possible to have a single printer shared between two different workstations. In the one second timer example, it is possible to have multiple timers displayed in the same window through a shared Frame object.
A shared Frame object is shown in Figure 1.5.4. The dashed line indicates the logical partition between the two distinct one-second timers that are displayed in the same (shared) user interface window (Frame).
Figure 1.5.4: Shared Objects in an Association | |
A second advantage of an association is that the parts in an association can be dynamically changed. This can be accomplished simply by having a member in the composition connect to (point to) a different object. This change is dynamic in that it can be done at run-time. Again using the computer workstation example, it is possible to change the keyboard or mouse or to change the printer connected to the system. In the one second timer example, it is possible to replace the one second clock by a faster clock allowing more accurate timings to be made.
1.5.3 Composition Using Aggregation
Aggregation is a composition in which the "whole" subsumes and conceals its constituent "parts". This relationship is suggested by Figure 1.5.5. In contrast to an association, the parts are not visible externally, they do not have an identity as far as a user of the composition is concerned, and they do not possess autonomy to the same degree as parts of an association. The "whole" is the single visible entity. This suggests the following definition.
Aggregation a composition that encapsulates (hides) the parts of the composition.
Figure 1.5.5 shows the general model of an object defined by aggregation. The outer objects contain inner (encapsulated) sub-objects which themselves may have hidden internal objects.
Figure 1.5.5: Composition via Aggregation | |
Composition using aggregation occurs in many familiar natural and man-made systems. Table 1.5.1 shows how several familiar systems can be mapped to the general structure of an aggregation shown in Figure 1.5.5. Notice that the sub-objects and the sub-sub-objects are generally considered to be internal components of the object.
Table 1.5.1: | Object | Sub-Object | Sub-Sub-Object | automobile | engine | pistons | computer | mother board | processor chip | molecules | atoms | quarks |
An aggregation of objects is created when one object (the "whole") contains in its encapsulated data one or more other objects (the "parts"). The simple one-second timer created above using association can also be implemented via aggregation as shown in Figure 1.5.6. In this figure the basic objects that form the timer (Clock, Counter, Message, Frame, Canvas, Panel, Start, and Stop) are contained within other objects (TimedCounter, Display and ControlButtons) that are, in turn, contained in the single encapsulating object, SimpleTimer. Notice that from the outside, only the SimpleTimer is visible; all other objects are concealed within the encapsulating boundary of the SimpleTimer object.
Figure 1.5.6: Composition via Aggregation | |
The first advantage of aggregation is that the outer object may be used without much, if any, concern for the operation, or even the existence, of the internal sub-objects. When driving a car we are rarely concerned about the thousands of parts which are composed together to realize the car. The ability to ignore the finer structure of an object greatly simplifies the task of understanding how a system works or building a system that works in a particular way. The second advantage of aggregation is that internal parts may be changed without affecting the user's view of the external whole. The internal structure of the parts may be completely changed or only individual parts may be replaced. Improvement in efficiency, reliability, or cost may motivate the replacement of parts. Exercises.
1. Consider a merchandising company that accepts phone orders and relays the orders to a warehouse where the merchandise is packaged and shipped via a package delivery service. Draw a diagram showing the associations and/or aggregations in this system.
2. Consider a home entertainment system with a satellite dish, a television, and a remote control. Draw a diagram showing the associations and/or aggregations in this system.
3. Consider an air traffic control system with radars, controllers, runways, and aircraft.. Draw a diagram showing the associations and/or aggregations in this system.
1.6 Generalization
Generalization identifies commonalities among a set of entities. The commonality may be of attributes, behavior, or both. For example, a statement such as "All graphical user interface windows have a title" expresses a common attribute among all entities that are considered windows in a graphical user interface. Similarly, the statement, "All graphical user interface windows can be resized." expresses a common behavior that all windows provide. Generalizations are usually easy to recognize as they contain words like "all" and "every".
Generalization may be defined as:
Generalization the identification, and possible organization, of common properties of abstractions.
This definition shows that generalization is not abstraction although the two are often confused. Abstraction aims at simplifying the description of an entity while generalization looks for common properties among these abstractions.
Generalizations are clearly important and prevalent in many disciplines of study. In science and mathematics, for example, the statements of "laws" and "theorems" are often generalizations - they state some property that holds over a group of things: the more powerful the generalization, the more things to which the generalization applies. The search for the basic forms of matter represents the physicists' quest for a generalization that applies to everything in the physical universe. The biologist's use of generalization is reflected in the organization of plants and animals into the taxonomy whose divisions are kingdom, phylum, class, order, etc.
Generalizations are equally important to software. Much of the effort in building software systems is to allow parts of the system to operate in the most general way possible. In some cases this might mean designing the system so that it can handle any number of things of the same kind. For example, the system might be expected to process any number of lines of input. In other cases the major design problem is how to handle things of different kinds or types. For example, the system might be expect to process input that comes from files of different formats or from both local as well as remote locations. An approach to solving some of these problems is to be able to capture more completely in software the idea of generalization.
One of the three forms of generalization is hierarchy. In the case of hierarchy, the commonalities are organized into a tree structured form. At the root of any subtree are found all the attributes and behavior common to all of the descendents of that root. This particular kind of tree structure is referred to as a generalization/specialization hierarchy because the root provides more general properties shared by all its descendents while the descendents typically add specializing properties which make them distinct among their siblings and their siblings' descendents.
The second form of generalization is polymorphism. Polymorphism captures commonality in algorithms. An algorithm may have a nested if-then-else (or case statement) logic which tests for the exact type of an object which it is manipulating. the algorithm performs some operations on the object based on the exact type of the object. However, in many algorithms the operations to be performed are the same, only the type of the object on which they are performed varies.
Polymorphism allows this nested logic (or case statement) to be collapsed to a single case in which the different object types are treated in a uniform manner. Through a mechanism called dynamic binding, the algorithm allows the object to determine which of its operations to perform in response to the algorithms invocation. Thus, the algorithm need not know the exact type of the object. The algorithm only needs to know that the object can respond to the invocation in an appropriate manner.
The third form of generalization is patterns. A pattern expresses a general solution (the key components and relationships) to a commonly occurring design problem. The attributes and behavior of the individual components are only partially defined to allow the pattern to be interpreted and applied to a wide range of situations. For example, a "wheeled vehicle" pattern might be defined in terms of the components "wheel", "axle", "frame", "body" and "power source". The pattern would also show how these components would be arranged in relation to each other (e.g., the axle must connect two wheels). The pattern could be interpreted in many different ways to solve particular problems that differ in their requirements for speed, durability, payload, fuel source, available materials, and other factors. Examples of the wheeled vehicle pattern are "automobile", "horse-drawn carriage", "ox cart", "moon buggy" and many others. 1.6.1 Hierarchy
A hierarchical organization of components based on a relationship of generalization/specialization is an important device in object-oriented programming. While the power of such an organization can only be fully appreciated after more study, it is useful to at least hint at the role it will play. A generalization and its specializations are often said to be related by an "is-a" relationship. The relationship has this name because it is suggested by phrases such as "an aircraft is a vehicle" and "a lion is a carnivore". In these phrases the characteristics of a specialized entity, the aircraft and lion, are related to the characteristics of a more general category of entities, the vehicle and carnivore categories, respectively. The "is-a" terminology reflects that the specialization has all of the attributes and behavior of the generalization. Thus, an aircraft has all of the properties of vehicle plus additional one that discriminate it as an aircraft apart from other types of vehicles. Similarly, a lion has all of the properties of a carnivore in addition to those that make is uniquely a lion.
A more comprehensive intuitive example of a generalization/specialization hierarchy is one that organizes and relates the characteristics of different types of vehicles. The diagram in Figure 1.6.1 illustrates a possible hierarchical structure for vehicles. The arrows in this diagram represent the "is-a" relation. For example, the arrow connecting the categories Aircraft and Vehicle is read as "Aircraft is a kind of Vehicle." The general concept of Vehicle is the root of the hierarchy. Properties shared by all vehicles might include the manufacturer's name, the type of fuel required, and the maximum speed. Two distinct groups of vehicles are those intended for use on land, LandVehicles, and those designed to fly, Aircraft. Properties common to all land vehicles are the number of axles, and the vehicle identification number. While these properties make sense for LandVehicles, they are not relevant to Aircraft. Aircraft properties, however, might include a transponder signature, and a tail number. Both LandVehicles and Aircraft can be divided into Private and Commercial categories. Things in the Private category have a person-owner attribute while those in the Commerical category have a company-owner attribute. Various specific types of vehicles are shown for the LandVehicles categories and for the commerical subcategory of Aircraft. Figure 1.6.1: A Generalization/Specialization Hierarchy for Vehicles | |
Another example of a generalization/specialization hierarchy, this time from the domain of graphical user interfaces, is shown in Figure 1.6.2. The most general element, Component, has attributes of location (coordinates denoting where the component appears on the screen) and shape (height and width), and a behavior that allows the component to be repositioned and resized. Specialized kinds of Components include an area for drawing graphical shapes (Canvas), an interactive element for user control (Button), and a general collection of components (Container). Two specialized kinds of Containers are a Window, a Container that has a visible representation on the screen, and a Panel, a Container that maintains an arrangement of other Components. The Frame is a Window with a border and a Dialog is a Window specialized for the purposes of creating a text-oriented interaction with the user.
Figure 1.6.2: A Generalization/Specialization Hierarchy | |
The notion of hierarchy in object-oriented programming can be defined as follows:
Hierarchy a generalization in which abstractions are organized into a directed acyclic graph whose arcs denote an "is-a" relation between a more generalized abstraction and the one or more derived specializations.
The most common case of hierarchy uses a tree structure to organize the abstractions, although more general organizations are possible.
A generalization/specialization hierarchy serves at least four major purposes. First, it provides a form of knowledge representation. A higher (more generalized) level in the hierarchy encodes an understanding of the general attributes and behavior possessed by all of its specialized descendents. Thus, it is possible to make statements such as: "All windows can be resized." and "All windows can be repositioned.". Second, the names of the intermediate levels in the hierarchy provide a vocabulary that can be used among developers and between developers and domain experts (those knowledgeable about the application domain but not necessarily about computing). This vocabulary allows discussions to be less ambiguous because the terms in the vocabulary identify specific, clearly defined concepts. Third, the hierarchy can be extended by adding new specializations at any level. These additions are easier to make because they occur within an existing framework that defines some, perhaps, many of their attributes and behavior. For example, to add a new specialized kind of Panel, it is not necessary to redefine all of the attributes and behavior already defined in Container, Component, and Panel; these are assumed to be part of the more generalized nature of the specialized Panel being added. Fourth, new attributes and behavior can be added easily to the proper subset of specializations. For example, any new attribute or behavior that might be needed for all containers can be added to the Container class. These additional attributes or behavior are then "automatically" part of all specialized kinds of Containers, but not of anything else. 1.6.2 Polymorphism
Polymorphism is a means of generalizing algorithms. The generality is achieved by allowing the algorithm to manipulate uniformly objects of different classes provided that the algorithm uses only common properties of the different classes. Any object known to possess the required common properties may be manipulated by the algorithm. Some object-oriented languages require that the compiler be able to verify at compile-time that an object possesses the required common properties. Other languages allow this verification to be deferred until run-time, risking a possible run-time error if, during execution, the object is discovered to be lacking one of the required common properties.
Polymorphism can be defined as follows:
Polymorphism the ability to manipulate objects of distinct classes using only knowledge of their common properties without regard for their exact class.
The meaning of polymorphism is reflected in the root phrases from which the term is derived. The phrase "poly" denotes "many" or "several" and "morph" refers to "shape", "form", or "appearance". Thus, polymorphism refers to things of many shapes or many forms. In the object-oriented programming sense, the "shape, form, or appearance" is taken to mean the interface or properties of an object. The "poly" aspect implies that the interfaces or properties are different or varied across the objects being considered. The challenge is how to manipulate these varied and different objects in a uniform manner.
Humans use polymorphism in many activities. One example is a store check-out clerk who determines the total amount of a customer's purchases. Each item being purchased has a bar code label that the clerk scans by passing it over a bar code reader. The bar code reader reads the identifying information on the bar code label, consults a database of merchandise and reports the information to the cash register for totalling. The clerk follows a generalized algorithm which might be written as: while ( more storeItems ) { pick up next storeItem; scan storeItem; put scanned storeItem in bag; };
This algorithm does not refer to any of the many specific types of merchandise sold by the store. The algorithm only refers to a general "storeItem". The storeItem is only required to have a bar code label so that the item can be scanned. Any item of merchandise having such a label can be handled by the clerk. Conversely, any item not possessing a bar code label cannot be handled by the clerk. In this example, compile-time checking is equivalent to making sure that items have a bar code label when they are put on the shelf (i.e., when the "store" is "programmed"). Run-time checking is equivalent to having the clerk check for the presence of a bar code label and, if the item does not have such a label, the clerk calls a manager to report the problem. There are many every-day examples similar to the store clerk in which people act in a polymorphic way: shelving books in a library, driving different kinds of cars, using different kinds of computers are a few of these.
Polymorphism enables open-ended software. New classes possessing the required common properties can be incorporated into a polymorphic structure; the structure need not be changed in any way to accommodate the additional classes. The same is true for the store clerk algorithm. The store clerk does not need any additional training (reprogramming) when new kinds of merchandise are added to the store's inventory provided that the new merchandise possesses the required bar code label.
A mechanism called dynamic binding is needed to implement polymorphism in object-oriented systems. In this context, the term "binding" refers to the linking of an invocation made by the polymorphic algorithm with the code of a method implemented by the receiving object. Because the polymorphic algorithm is unaware of the exact class of the receiver, the binding must be done dynamically because the same invocation may be bound to methods in different classes on different executions depending on what object is presented to the algorithm. Figure 1.6.3 depicts the act of dynamic binding. In this example the polymorphic algorithm invokes a method f() that is one of the common properties shared among a set of classes. The algorithm is unaware of the exact type of the object to which the invocation is directed. In the example, it is not known whether the object is of class X or class Y. The dynamic binding mechanism determines the type of the object and maps the invocation to the correct method in class X, if the object is of class X, or to the correct method in class Y, if the object is of class Y.
Figure 1.6.3: Dynamic Binding | |
1.6.3 Patterns
A pattern is a generalized solution for a commonly occurring kind problem. A pattern does not give the detailed code for a particular solution but instead gives the general form of a solution for any problem of the particular kind. The user of the pattern must adapt the pattern to the particular case at hand and supply the missing details not given in the pattern. Experienced designers are believed to possess, perhaps even at an intuitive or subconscious level, a rich repertoire of patterns and the ability to recognize when a current problem can be solved by adapting a pattern used successfully as a solution for one or more previous problems. Lacking the collection of previous designs, novice designers are forced to solve each problem that is new to them as if it were a completely unsolved problem, often reinventing what previous generations of designers have already created.
Patterns are recognized at many levels of scale and in many disciplines. In computer science, a large scale pattern is often presented as an architecture or a model. Examples of such large scale patterns are the client-server model, layered architecture, and micro-kernel architecture. Small scale patterns in computing are often called plans or idioms because they represent a common arrangement of programming language constructs. An example of a small scale pattern is the "counted loop with early exit" plan. This plan might be used to scan an array of fixed length and terminate when the end of the array is reached or earlier if a given search criterion is satisfied. The plan specifies the initial conditions, the arrangement of the elements of the loop construct, and the termination conditions. The plan may be specified in a graphical or pseudo-code form so that it can be mapped by the user to different programming languages. Patterns are also common in other disciplines. The authors of the book Design Patterns cite as part of their inspiration the role of patterns in the architecture of buildings and in literary forms.
A design pattern can be defined as follows:
Design Pattern a named generalization describing the elements and relationships of a solution for a commonly occurring design problem.
A pattern contains four essential parts: a name, a problem, a solution, and the consequences. The four parts of a pattern are illustrated by a simple client-server pattern. The name of the pattern is intended to convey briefly and succinctly the subject matter of the pattern. For this example the name client-server is appropriate. The problem portion of the pattern identifies the conditions under which the pattern is applicable (i.e., for what problem is this pattern a solution). The client-server problem is one of providing a service to possibly multiple clients in a loosely-coupled manner.
The solution specifies the elements that comprise the solution, their individual responsibilities, and the manner in which they collaborate to achieve the solution. The elements of the client-server pattern could be given in pictorial form as shown in Figure 1.6.4.
Figure 1.6.4: Client-Server Pattern | |
The client-server pattern identifies five elements in the pattern: Client, Server, request, reply, and Connection. The client is responsible for generating a request that is sent to the server which, in turn, performs its service and delivers a response in the form a reply. The responsibility for conveying the requests and replies between the client and server is assigned to an intermediary knows as the connection. The client and server each collaborate directly only with the connection (but only indirectly with each other). The collaboration between the elements is defined by the sequence of events beginning with the generation of a request by the client and its transmission to the server followed by the generation at the server of a reply and its transmission to the client.
Notice that the pattern does not specify the nature of the service provided; it could be a name service, a time service, a location service, a file service, a security service or any other. Also the pattern does not specify how the connection is to be implemented. The connection could be a memory buffer connecting two procedures within the same process, a memory buffer connecting two different processes on the same machine, or a network link between two processes on different machines. While these details vary, the pattern remains the same.
A pattern also specifies the positive and negative consequences of using the pattern. Some of the positive consequences of the client-server pattern are that the client and server may be implemented on different machines allowing each to take advantage of local specialized hardware or software resources, the client and server may be totally or largely unaware of and insensitive to the actual location of each other, and the server may be made available to many clients at the same time. Two negative consequences of the client-server pattern are that the client may be left hanging if its request or reply is lost or if the server crashes, and the client cannot demand or control the service from the server - it can only request such service.
A pattern for an object-oriented design is expressed in terms of classes and objects. The elements of the pattern are represented by classes. The relationships among the elements are defined by association, aggregation, and/or hierarchy. The responsibilities and collaborations are understood in terms of the behavior of each class and the interactions among the classes as defined by their associations.
Exercises.
1. Define a hierarchy for "Printed Material" that would include such specializations as books, magazines, textbooks, newspapers, and others. 2. Define a hierarchy for "Consumer Electronics" that would include such specializations as televisions, VCRs, radios, and others. 3. Define a hierarchy for "Athletic Games" that would include such specializations as soccer, basketball, football, tennis, track, and others. 4. Consider a librarian who is putting books back on the shelves after they have been returned. Each book has a catalog number printed on the spine of the book. Describe the actions of the librarian from the standpoint of polymorphism. 5. Consider a person working at a tollbooth who charges vehicles different amounts based on the number of axles: two axles passenger vehicles are charged one amount, a track towing a trailer is charged a higher amount, and an 1-wheeler is charged much more. Describe the actions of the tollbooth worker from the standpoint of polymorphism. 6. Define a design pattern for a "mail system". This pattern should be general enough to cover both a postal mail system, a package delivery service or courier service, and an electronic mail system. Identify the elements of the pattern, their responsibilities in the pattern, and their relationships to each other. 7. Define a "receptionist" design pattern. A receptionist holds arriving work for a boss, and passes the highest priority work to the boss whenever the boss is not occupied. This pattern should be general enough to cover both a real office environment and a computer-based scheduling system. Identify the elements of the pattern, their responsibilities in the pattern, and their relationships to each other.
1.7 Putting it All Together
To master object-oriented programming one must understand the connections among the design strategies, the software structures supporting the strategies, and the software engineering goals that the strategies and structures are meant to achieve. Some of the principal connections are shown in Figure 1.7.1. Understanding these connections enables the construction of useful and well-designed systems that solve important problems. This section identifies the key relationships among the many terms and concepts about object oriented programming, Java, design, and software engineering that have been introduced in the first chapter. The relationships discussed here will be understood more deeply as the exploration of object-oriented programming unfolds. For ease of reference, the definitions introduced throughout the chapter are collected in summary form at the end of this section.
Figure 1.7.1: Connections Among Strategies, Structures and Goals | |
While there is much to be learned about the design strategies, object-oriented software structures, and software engineering goals, two observations can be made: * the structures of objects and classes related to three of the four design strategies and one of the most important software engineering goals, reusability. * interfaces and inheritance relate to all of the software engineering goals, indicating the importance of understanding how to exploit these structures in creating quality software systems.
Other aspects of these relationships are discussed below.
Relationships to Design Strategies
Classes and objects are related to the strategies of abstraction and separation as well as the strategies for composition. The definitions of objects and classes makes evident the strong relationship between these basic concepts and the design strategies of abstraction and separation. An important task in system development is the identification of the core concepts in the application domain. The people performing this task are called system analysts, requirements engineers, or domain engineers. These people use the strategy of abstraction to identify the attributes and behavior essential to building useful application in the domain; complex entities are reduced to their essence. These abstractions, combining attributes and behavior, are translated directly into classes. The design strategy of separation is evident in the structure of class and objects which employ the notion of encapsulation to separate the public, external aspects of a class or object from it private, internal implementation. This separation confines the effects of changes to a class's implementation; improvements can be made in the implementation without creating "ripple effects" that require changes to other parts of the system that use the modified class.
Classes and objects are most often created so as to be used in conjunction with other classes and objects through composition. The notion of a "system" is one of differentiated parts interacting to achieve an overall behavior. Thus, to build an object-oriented system means to define classes and objects that are interrelated: they know about each other, they are designed to use each other's services, they form organized structures. The two forms of composition, association and aggregation, have natural counterparts in object-oriented languages. Associations are created by classes whose objects can refer to each other and invoke each other’s methods. Aggregations are created when the implementation of one class encapsulates objects of other classes. Thus, classes and objects provide structures that support the design strategy of composition.
Abstract interfaces also support abstraction, separation, and composition. Like classes, interfaces are a means of expressing the behavior of some abstraction because both classes and interfaces allow a related collection of methods to be defined as a named entity. Abstract interfaces support separation even more strongly than classes because, unlike a class, there is no implementation associated with the definition of an abstract interface. With abstract interfaces, the definition of the abstraction's behavior is completely divorced from an implementation that is encapsulated in a class conforming to the requirements of the abstract interface. Abstract interfaces support the association form of composition because the methods defined in one abstract interface may refer to objects whose type is defined by another interface.
Inheritance and design patterns support generalization. Inheritance allows the definition of a hierarchical organization of classes so as to express the commonalities among them. Expressing these common attributes and behavior makes it easier to understand the entire collection of classes because the shared aspects of a generalization can be understood once for each specialization. The generalization/specialization hierarchy conforms to common ways of organizing information about complex, structured phenomena. Design patterns are a way of codifying the knowledge of experienced designers. A pattern presents a generalized solution to a frequently encountered design problem. The design pattern is a generalization in the sense that it is not a solution to a specific problem but a general guide that shows how to solve and specific instance of the design problem. Design patterns for object-oriented software are presented as a set of relationships among a collection of classes. Thus, a design pattern is a structure that must be adapted to the specifics of a particular problem.
Relationships to Software Engineering
Object-oriented programming is an evolutionary development in software engineering that addresses (at least) the three major software engineering goals shown in Figure 1.7.1: reusability, extensibility, and flexibility. The language features that address these issues are those of objects, classes, abstract interfaces, inheritance, and design patterns. The foundation for these language features were established by decades of software engineering experience. Also important were the widely recognized value of such software engineering techniques as information hiding, encapsulation, strict enforcement of interfaces, and layering.
Reusability is an important issue in software engineering for at least two major reasons. First reusability is one means to cope with the pressures of producing ever larger and more functional systems in an ever decreasing development cycle (time to market). Reusability allows developers to be more efficient because the same code can be developed once and used in many different applications. Second, reliability can be improved by reusing previously developed, and previously tested, components. The development of new code entails the additional costs in time and money of testing, validation, and verification of the new code. Much of these expenses can be avoided by using "off-the-shelf" components.
Software reuse is certainly not a goal unique to object-oriented programming. While libraries of procedures proved this approach to be useful, in practice procedures were too primitive a unit to promote extensive reuse. Objects and classes are more sophisticated mechanisms for achieving software reuse because they bind together more completely all the aspects of an entire abstraction. Therefore, the abstraction can more easily be transported across applications. Interfaces promote software reuse because the software on either side of the interface may be reused independently of each other. That is, the classes implementing an interface can be used in any situation where the interface appears and, conversely, the code using an interface can be reused with a completely different set of classes implementing the interface. Any of the forms of generalization also contribute to reuse. A class in an inheritance hierarchy can be reused directly when it serves as a generalized base class from which a new class is derived by specialization. Design patterns allow design experience and success to be reused across designers.
Extensibility in software is important because software systems are long-lived and are subject to user's demands for new features and added capability. Object-oriented programming can help to satisfy this need through inheritance. Recall that inheritance is a generalization/specialization hierarchy. Referring to the Component hierarchy discussed earlier, extensibility is possible in two ways. The first way in which a generalization/specialization hierarchy supports extensibility is that any new attributes or behavior that is added to a more generalized concept (e.g., Component) will automatically become part of the attributes and behavior of its specializations (e.g., Container, Window, Button). For example, as shown in Figure 1.7.2, if the Component abstraction is enhanced to include a color with which the Component is displayed on the screen, then the attribute "currentColor" and the behavior "setColor" might be added to Component. It would then be possible to manipulate the color of a Window as well as all of the other specializations of Component.
Figure 1.7.2: Adding new Attributes and Behavior | |
The second way in which a generalization/specialization hierarchy supports extensibility is that the hierarchy itself can be extended. New additions can be made under any existing node. For example, as shown in Figure 1.7.3, the Frame might be specialized to a HyperTextFrame by including additional attributes and additional behavior that distinguishes ordinary words or images in a Frame from those words or images that are hyperlinks and can be clicked-on to cause the HyperTextFrame to display a new content.
Figure 1.7.3: Adding new Specialized Classes | |
Flexibility in software systems means, in part, that additions, variations or modification can be made without the need to modify numerous places in the system's code. Historically, many software systems were very brittle; the addition of a small change could only be accommodated by making modifications in many, and often apparently unrelated, parts of the existing system. This brittleness stood in marked contrast to the prevailing notion that, unlike hardware systems, software system were supposed to be extremely malleable and changes should be made easily.
Object-oriented programming contributes to flexibility in two ways. First, the separation of an interface from its implementation allows the user of the interface to remain unaffected by changes in the implementation. Thus, a modification can be made to the implementation (e.g., to improve its efficiency or reliability) without requiring any changes in the code that uses the interface. Second, polymorphism allows variations and additions to be made to the set of classes over which the polymorphism applies. For example, referring to the Component hierarchy, consider adding a new kind of button, a RadioButton. It would seem necessary to modify the Panel to allow a Panel to manipulate the newly created RadioButton. However, the Panel can use polymorphism so that the Panel's algorithms only rely on the more general attributes and behavior of an object (i.e., that it is a kind of Component) and does not need to be aware of the exact "type" (i.e., RadioButton) of the object. Using this approach, the Panel can be designed to operate on Components. Any newly created Component, even one - like the RadioButton - that is created after the Panel is already written, automatically can be manipulated by a Panel without changing the code of the Panel.
Summary of Definitions
The term defined in this chapter are summarized below for ease of reference. The section number in which a term appeared is given in parenthesis after each term. Term | Definition | Abstraction (1.2) | a named collection of attributes and behavior relevant to modelling a given entity for some particular purpose. | Software Interface (1.3) | the external, visible aspects of a software artifact by which the behavior of the artifact is elicited. | Software Implementation (1.3) | the programmed mechanism that realizes the behavior implied by an interface. | Separation (1.3) | in software systems, the independent specification of a software interface and one or more software implementations of that interface. | Class (1.4) | a named software representation for an abstraction that separates the implementation of the representation from the interface of the representation. | Encapsulation (1.4) | in object-oriented programming, the restriction of access to data within an object to only those methods defined by the object's class. | Object (1.4) | a distinct instance of a given class that encapsulates its implementation details and is structurally identical to all other instances of that class. | Abstract Interface (1.4) | a named software representation for an abstraction's behavior that is intended to be implemented by one or more classes. | Composition (1.5) | an organized collection of components interacting to achieve a coherent, common behavior | Association (1.5) | a composition of independently constructed and externally visible parts. | Aggregation (1.5) | a composition that encapsulates (hides) the parts of the composition. | Generalization (1.6) | the identification, and possible organization, of common properties of abstractions. | Hierarchy (1.6) | a generalization in which abstractions are organized into a directed graph whose arcs denote an "is-a" relation between a more generalized abstraction and the one or more specializations derived from it. | Polymorphism (1.6) | the ability to manipulate objects of distinct classes using only knowledge of their common properties without regard for their exact class. | Design Pattern (1.6) | a named generalization describing the elements and relationships of a solution for a commonly occurring design problem. |
2. Using Objects of a Single Class
2.1 Introduction
In this section the basic aspects of classes and objects are presented. The outermost structure of a class definition is given along with several examples of classes that capture abstractions in a graphical user interface domain as well as an example from an ecological simulation domain. The basic mechanism for creating object is described and the role of variables in providing a way to refer to objects is examined. The close relationship between the concepts of a variable's type and an object's class is seen, lending credence to the idea that a class is a "user defined type." Also described is the use of the import statement to guarantee that the compiler is able to see the definition of classes before use is made of these classes.
Giving a Name to a Class
In Java the keywords "class" and "public" are used to define a new class that is freely (publicly) available to other classes without restriction. Each class is given a distinctive name that conveys the meaning of the abstraction represented by the class. The following code defines the outer structure of a class named Frame that represents a "window" in a graphical user interface system. public class Frame { // represent a graphical user interface window /* the body of the class definition goes in here between the curly braces */ }
Similarly, a class that defines an abstraction of a "prey" in a simulation of an ecological environment has the following general structure: public class Prey { // represent a "prey" in an ecological simulation /* the body of the class definition goes in here between the curly braces */ }
The body of each classes' definition lies between the open and close braces. The body of the Frame class and the Prey class will be developed in the following sections.
Two different forms of comments are illustrated in the Frame class definition above. An adjacent pair of slash marks "//" introduces a comment that ends at the end of the current line. A multi-line comment begins with the pair of characters "/*" and end with the matching pair of characters "*/".
An object-oriented program typically involves several, perhaps many, different classes. Other classes related to a windowing system might be: public class Message {...} // an unchanging line of text public class TextBox {...} // editable lines of text public class Button {...} // a selector that can be "pushed"
Other classes related to the ecological simulation might be: public class Predator {...} // a Predator that hunts Prey public class Herd {...} // a group of Prey public class Pack {...} // a group of Predators
These classes will be seen and used in this and subsequent chapters.
Creating Objects of a Class
Creating an object involves three steps: importing the definition of a class, declaring a variable (identifier) that will refer to an object of that class, and constructing the object. As in C, C++, and many other languages, Java requires that the definition of a class must precede the use of that class in the program text as seen by the compiler. The requirement is needed so that the Java compiler can more easily check that the definition of a class is consistent with its use in declarations and other statements. This is part of the character of Java as a statically-typed object oriented language. The programmer must insure that the "declaration before use" rule is met by using the import statement to direct the compiler to search for and examine the definition of a given class. More details are in Section 4.9 on how th compiler knows where to search for the classes that are named in the import statement. The following code fragment illustrates how objects are created. The fragment's first two statement import the definitions of the Frame and Prey classes: import Frame; // find definition of Frame class import Prey; // find definiton of Prey class
Frame display; // declaration of a variable Prey aPrey; // declaration of a variable ... display = new Frame(...); // construct Frame object aPrey = new Prey(...); // construct Prey object
The third statement declares that the variable display will refer to an object of the Frame class. Similarly, the fourth statement declares that the variable aPrey will refer to an object of the Prey class. These declarations do not create the objects. All objects in Java are created by the new operator as shown in the last two statements. The ellipsis (i.e., the "...") is not part of Java syntax. This notation is used to denote the values that are typically required to initialize the object being declared. The values that should be given here will be seen when the complete definition of these classes is presented. The object created by the new operator is associated with an identifier (variable) by an assignment statement as shown in the last two statements. Defining a variable and creating an object can also be done in a single statement as follows: Frame display = new Frame(...); Prey aPrey = new Prey(...);
In these statements the object created by the new operator is assigned to a variable that is declared on the left-hand side of the assignment operator.
Many variables can be declared that refer to objects from the same class. For example, several variables that can refer to Frame objects and Prey objects can be created as follows: Frame display, viewer; Frame editor; Prey rabbit, mouse; Prey deer;
These two declarations create three variables that refer to Frame objects and three variables that refer to Prey objects. Notice that several identifiers (variables) can be created with one declaration, as is done with display and viewer. A comma must separate adjacent names in the same declaration. Also notice that, as in this example, the same class can be used in different declarations.
Two different variables may refer to the same object. For example, consider the following code that declares three variables: Frame display, viewer; Frame editor;
...
display = new Frame(...); editor = new Frame(...); viewer = display;
Two objects are created that are referred to by the variables display and editor. The last assignment statement means that the variable viewer will refer to the same object as that referred to by the variable display. In a complete program, some variables always refer to the same object while other variables refer to different objects at different times.
While the variable and the object to which it refers are technically distinct, common terminology often emphasizes their close relationship. In discussing a program, programmers often say "the display object" instead of the technically correct "the object referred to by the display variable." Not only is the shorter phrase easier to say but it also emphasizes the fact that variables are created for the purpose of naming objects; manipulating an object is done by performing operations on a variable that refers to the object. It is occasionally necessary to use the more precise terminology when the distinction needs to be made between a variable and the object to which the variable refers.
The declaraction of an identifier (variable) that refers to an object illustrates the strong connection between the concept of a "type" and the concept of a "class". Compare, for example the following three declarations: int counter; Frame display; Prey aPrey;
The first declaration creates a variable whose type is "int" and which is named by the identifier "counter". The type "int" determines what operations can be applied to the variable. For example, "+", "-", "<" and "=" are some of the valid operations. The compiler will issue warnings or error messages if invalid operations are attempted. Similarly, the second declaration creates a variable whose type is "Frame" and which is named by the indentifier "display" and the third declaration creates a variable whose type is "Prey" and which named by the identifier "aPrey." As with all types, the compiler will check that the operations applied to "display" and "aPrey" are appropriate for their types. Since Frame and Prey are programmer-defined types, the valid operations on objects of these types are exactly those given in the definition of the Frame class and the Prey class. The rules of type checking apply to assignment of objects just as they do to assignment of predefined types. For example, one object may be assigned to another object only (at least for now) if they are of exactly the same class. This rule is illustrated by the following code: Frame display, viewer; Prey aPrey, hunted; ... display = viewer; // correct types aPrey = hunted; // correct types viewer = aPrey; // incorrect types hunted = display; // incorrect types
It is permissible to assign viewer to display because these two variables refer to objects of the same class. However, aPrey cannot be assigned to viewer and display can not be assigned to hunted - in each case the two variables refer to objects of different classes, equivalently of different types.
UML Notation
The Unified Modeling Language (UML) provides graphical notations for representing classes and objects. Since only a very small part of the class and object concepts has been presented in this section, only a small part of the UML notation is described here. As more of the structure of classes and objects is revealed, more of the UML notation will be seen. The basic UML notation for a class is shown in Figure 2.1.1. UML allows a class to be represented at two different levels of detail. A class is drawn as a rectangular box inside of which is written the name of the class. The left part of Figure 2.1.1 shows the Frame class using only this level of detail. The right part of Figure 2.1.1 shows the outline of the more detailed representation of the Frame class. The name of the class is written in the top-most compartment. The middle and bottom compartments are empty, the information that can be put in these compartments will be shown in following sections. Figure 2.1.1: UML Class Notation | |
The UML representation for an object is similar to that for a class. An object of the Frame class is shown in Figure 2.1.2. The name of the object in this figure is "display." The name of the class is separated from the class of the object (in this case, the Frame class) by a colon. Both the name of the object and the name of the class are underlined to further distinguish the object notation from the class notation. The left part of Figure 2.1.2 shows only the basic information about an object. The right part of the figure shows that the object diagram may also have a second compartment. The information that can be placed in his additional compartment will be shown in the next section. Figure 2.1.2: UML Object Notation | |
Exercises. 1. Is it correct to have many classes with different names? 2. Is it correct have many objects of the same class? 3. Is it correct to define class int {...}; ? 4. Is it correct to declare "Frame Frame;"? That is, can an object and a class have the same name? 5. Is it correct to declare "Frame frame;" ? 6. Is it correct to declare "Frame aFrame;" ? 7. Write three declarations that create four Frame objects and two Message objects. Use the ellipsis notation to indicate where the initializing values would appear. 8. Write a different answer to the last question. 9. Write two declarations that create four Prey objects. Use the ellipsis notation to indicate where the initializing values would appear. 10. Write a different answer to the last question. 11. Name five other classes that might be part of a graphical user interface system. Present your answer in the form public class ClassName {...} where "ClassName" is the name of the class that have chosen. 12. Name five other classes that might be part of an ecological simulation system. Present your answer in the form public class ClassName {...} where "ClassName" is the name of the class that have chosen. 13. Give the declarations for the system described below in what you consider the best style.
An application has two windows, one for receiving user commands and one for displaying status information. Each window has a message that identifies the window. The command window has two areas where editable text can be displayed, one area for a command and one for command options. The command window has two buttons, one used to execute the command and one to stop the command's execution. The status window has a second message that is used to display any error messages that result from a command's execution. 14. Compare your answer to the last question with someone who has a different style. Identify the ways in which they styles are different. 15. Draw UML diagrams for all of the classes mentioned in this section. 16. Draw UML diagrams for all of the objects mentioned in this section.