CSci 581: Obj.-Oriented Design & Programming
Spring Semester 1999
Lecture Notes


Introduction to Object Orientation

Software Development Lifecycle

  .__________________.
  |                  |
  |     Analysis     |<--:
  |__________________|   |
           |             |
           |             ^
  .________V_________.   |
  |                  |-->:
  |      Design      |   |
  |__________________|<--.
           |             |
           |             ^
  .________V_________.   |
  |                  |-->:
  |  Implementation  |   |
  |__________________|<--:
           |             |
           |             |
  .________V_________.   ^
  |                  |   |
  |   Maintenance    |-->:
  |__________________|

Analysis

In the analysis phase we move from a vague description of the problem to be solved to a precise and unambiguous requirements specification.

The requirements specification might be a precise, but informal description written in careful natural language text and diagrams. Alternatively, the specification might be a formal description written in a mathematically precise language. Or the requirements specification might be something in between these.

The requirements specification should be:

Design

In the design phase, we move from a requirements specification to a design specification. The design specification gives the system structure. The design tasks are to:

Implementation

In the implementation phase, we move from a design specification to a tested executable system. The implementation tasks are to:

Maintenance

In the maintenance phase, we move from a complete "working" system to a modified system. The maintenance tasks are to:

Observations:

Conclusions:

"Programming in the Small" and "Programming in the Large"

The type of software development projects familiar to most students can be described as programming in the small. Such projects have the following attributes:

Programming in the large characterizes projects with the following attributes:

The techniques of object-oriented design and programming are useful in both programming in the small and programming in the large situations. However, some of the techniques are best appreciated when the difficulties of programming in the large are understood.

Object Orientation

We will approach the design of programs from an object-oriented perspective.

Key idea (notion?) in object orientation:
The real world can be accurately described as a collection of objects that interact.

Assumptions:

  1. Describing large, complex systems as interacting objects make them easier to understand than otherwise.
  2. The behaviors of real world objects tend to be stable over time.
  3. The different kinds of real world objects tend to be stable. (That is, new kinds appear slowly; old kinds disappear slowly.)
  4. Changes tend to be localized to a few objects.

Assumption 1 simplifies analysis, design, and implementation--makes them more reliable.

Assumptions 2 and 3 support reuse of code, prototyping, and incremental development.

Assumption 4 supports design for change.

The object-oriented approach:

An Object Model

Our object model includes four components:

  1. objects (i.e., abstract data structures)
  2. classes (i.e., abstract data types)
  3. inheritance (hierarchical relationships among ADTs)
  4. polymorphism by inheritance

Objects

Objects are characterized by:

An object is a separately identifiable entity that has a set of operations and a state that records the effects of the operations. That is, an object is essentially the same as an abstract data structure as we have discussed previously.

state:
the collection of information held (i.e., stored) by the object. The state is encapsulated within the object--is not directly visible.

operation:
a procedure that takes the state of the object and zero or more arguments and changes the state and/or returns one or more values. Objects permit certain operations and not others.

identity:
a way to distinguish between two distinct objects (even if they have the same state and operations).

As an example, consider an object for a student desk in a simulation of a classroom. The relevant state might be attributes like location, orientation, person using, items in basket, items on top, etc. The relevant operations might be state-changing operations (mutators) such as "move" the desk, "seat student", or "remove from basket" or might be state-observing operations (accessors) such as "is occupied" or "report items on desktop".

A language is object-based if it supports objects as a language feature.

Object-based languages include Ada, Modula, Clu, C++, Java, and Smalltalk. Pascal (without module extensions), Algol, Fortran. and C are not inherently object-based.

Classes

A class is a template for creating objects.

A class description includes definitions of

As an example, again consider a simulation of a classroom. There might be a class of "student desks" from which specific instances (objects) can be created as needed.

An object-based language is class-based if the concept of class occurs as a language feature and every object has a class.

Class-based languages include Clu, C++, Java, Smalltalk, and Ada 95. Ada 83 and Modula are not class-based.

Inheritance

A class D inherits from class B if D's objects form a subset of B's objects.

The importance of inheritance is that it encourages sharing and reuse of both design information and program code. The shared state and operations can be described and implemented in base classes and shared among the subclasses.

As an example, again consider the student desks in a simulation of a classroom. The "student desk" class might be derived (i.e., inherit) from a class "desk", which in term might be derived from a class "furniture".

    furniture <-- desk <-- student_desk

The simulation might also include a "computer desk" class that also derives from "desk".

    furniture <-- desk <-- computer_desk

In Java, we can express the above inheritance relationships using the extends keyword as follows:

    class Furniture  // extends Object by default
    {   ...
    }

    class Desk extends Furniture
    {   ...
    }

    class StudentDesk extends Desk
    {   ...
    }

    class ComputerDesk extends Desk
    {   ...
    }

Both "student desks" and "computer desks" will need operations to simulate a "move" of the entity in physical space. The "move" operation can thus be implemented in the "desk" class and shared by objects of both classes. Invocation of operations to "move" either a "student desk" or a "computer desk" will be bound to the general "move" in the "desk" class.

The "student desk" class might inherit from a "chair" class as well as the "desk" class.

    furniture <-- chair <-- student_desk

Some languages support multiple inheritance as shown above for "student desk" (e.g., C++, Eiffel). Other languages only support a single inheritance hierarchy.

Because multiple inheritance is both difficult to use correctly and to implement in a compiler, the designers of Java did not include a multiple inheritance of classes as feature. Java has a single inheritance hierarchy with a top-level class named Object from which all other classes derive (directly or indirectly).

    class StudentDesk extends Desk, Chair  // NOT VALID in Java
    {   ...
    }

To see some of the problems in implementing multiple inheritance, consider the above example. Class StudentDesk inherits from class Furniture through two different paths. Do the data fields of the class Furniture occur once or twice? What happens if the intermediate classes Desk and Chair have conflicting definitions for a data field or operation with the same name?

The difficulties with multiple inheritance are greatly decreased if we restrict ourselves to inheritance of class interfaces (i.e., the signatures of a set of operations) rather than a supporting the inheritance of the class implementations (i.e., the instance data fields and operation implementations). Since interface inheritance can be very useful in design and programming, the Java designers introduced a separate mechanism for that type of inheritance.

The Java interface construct can be used to define an interface for classes separately from the classes themselves. A Java interface may inherit from (i.e., extend) zero or more other interfaces.

    interface Location3D
    {   ...
    }

    interface HumanHolder
    {   ...
    }

    interface Seat extends Location3D, HumanHolder
    {   ...
    }

A Java class may inherit from (i.e., implement) zero or more interfaces as well as inherit from (i.e., extend) exactly one other class.

    interface BookHolder
    {   ...
    }

    interface BookBasket extends Location3D, BookHolder
    {   ...
    }

    class StudentDesk extends Desk implements Seat, BookBasket
    { ...  
    }

This definition requires the StudentDesk class to provide actual implementations for all the operations from the Location3D, HumanHolder, BookHolder, Seat, and BookBasket interfaces. The Location3D operations will, of course, need to be implemented in such a way that they make sense as part of both the HumanHolder and BookHolder abstractions.

Polymorphism

The concept of polymorphism (literally "many forms") means the ability to hide different implementations behind a common interface.

Polymorphism appears in several forms.

Overloading (or ad hoc polymorphism)
means using the same name (or symbol) for several different procedures or functions (i.e., operations) and allowing the compiler to choose the correct one based on the signatures (i.e., on the number, types, and order of the parameters).
    void move () { ... }

    void move (Location l) { ... }

    void move (Desk d) { ... }

The choice among the alternatives can be done statically at the time the program is compiled.

Java supports overloading of method calls, but does not support user-defined overloading of operator symbols. C++ supports both.

Parametric polymorphism
denotes the use of parameterized type (or class) definitions.

Java does not currently support parameterized class definitions. The template facility in C++ and the generic facility in Ada are examples of parametric polymorphism. There are experimental extensions to Java that do support parametric polymorphism.

Polymorphism by inheritance (sometimes called pure polymorphism, but a subset of what Budd's textbook calls pure polymorphism)
means the association at runtime (or the dynamic binding) of an operation invocation (i.e., procedure or function call) with the appropriate operation implementation in an inheritance hierarchy.

This form of polymorphism is carried out at run time. Given an object (i.e., class instance) to which an operation is applied, the system will first search for an implementation of the operation associated with the object's class. If no implementation is found in that class, the system will check the superclass, and so forth up the hierarchy until an appropriate implementation is found. Implementations of the operation may appear at several levels of the hierarchy.

As an example, again consider the simulation of a classroom. As in our discussion of inheritance, suppose that the "student desk" and "computer desk" classes are derived from the "desk" class and that a general "move" operation is implemented as a part of the "desk" class. This could be expressed in Java as follows:

    class Desk extends Furniture
    {   ...
        public void move(...)
        ...
    }

    class StudentDesk extends Desk
    {   ...
        // no move(...) operation here
        ...
    }

    class ComputerDesk extends Desk
    {   ...
        // no move(...) operation here
        ...
    }

As we noted before, invocation of operations to "move" either a "student desk" or a "computer desk" will be bound to the general "move" in the "desk" class.

Extending the example, suppose that we need a special version of the "move" operation for "computer desks". For instance, we need to make sure that the computer is shut down and the power is disconnected before the entity is moved. We can define this special version of the "move" operation and associate it with the "computer desk" class. Now a call to "move" a "computer desk" will be bound to the special "move" operation, but a call to "move" a "student desk" will still be bound to the general "move" operation in the "desk" class. The definition of move in ComputerDesk overrides the definition in Desk.

In Java, this can be expressed as follows:

    class Desk extends Furniture
    {   ...
        public void move(...)
        ...
    }

    class StudentDesk extends Desk
    {   ...
        // no move(...) operation here
        ...
    }

    class ComputerDesk extends Desk
    {   ...
        public void move(...)
        ...
    }

A class-based language is object-oriented if class hierarchies can be incrementally defined by an inheritance mechanism and the language supports polymorphism by inheritance along these class hierarchies.

Object-oriented languages include C++, Java, Smalltalk, and Ada 95. The language Clu is class-based, but does not include an inheritance facility.

Other object-oriented languages include Objective C, Object Pascal, Eiffel, and Oberon 2.

Object-Oriented Design: Finding Classes and Operations

The goals of the design phase are to:

The actual design process is iterative. Elaboration of a class may lead to identification of additional classes or changes to those already identified.

Classes should be crisply defined. It should be as easy as possible to determine what class an object belongs in. Coming up with a good categorization is often difficult in complex situations.

Operations should be defined precisely. Ambiguity and imprecision will likely cause problems later in the design and implementation.

Relationships among classes should not be excessively complex.

The information gathered in the design phase is the basis for the implementation. A good design makes the implementation easier and faster and the resulting product more reliable.

Method to find objects and their classes:
Begin with the nouns in the requirements specification.
Method to find operations:
Begin with the verbs in the requirements specification.

Example: Computerized Telephone Book

As an example, consider a computerized telephone book for a university. The telephone book should contain entries for each person in the university community--student, professor, and staff member. Users of the directory can look up entries. In addition, the administrator of the telephone book can, after supplying a password, insert new entries, delete existing entries, modify existing entries, print the telephone book, and print a listing of all students or of all faculty.

  1. Identify the candidate classes. List the nouns and noun phrases from the specification:

    computerized telephone book, university, telephone book, entry, person, university community, student, professor, staff member, employee, user, administrator, password

    Several, but not necessarily all, of these will become classes in our design.

    Other classes may be be implicit in the specification or may emerge as the design of individual classes proceed. For example, the design will likely need name, address, and telephone number fields of the entries for each person, need components of the user interface (such as a menu), and need information structure for storing the collection of personal entries that make up the telephone book.

  2. Identify the candidate operations. List the verbs from the specification.

    lookup entry, supply password, insert new entry, delete existing entry, modify existing entry, print telephone book, print all students, print all employees, set telephone number field, get telephone number field, compare entries.

  3. Eliminate unnecessary and synonym classes and operations.

    For example, computerized telephone book, telephone book, and directory can be combined into a single PhoneBook class. Similarly, entry and person can be combined into a single Person class. In addition, all the print operations probably probably be combined into a single print operation with the differing functionalities specified by parameters.

  4. Associate the operations with the appropriate classes.

    For example, associate lookup, insert, delete, and modify entry operations with the PhoneBook class, associate compare, setPhoneNumber and getPhoneNumber with the Person class, etc.

    Sometimes there might be a choice on where to associate a operation. For example, the "insert person into telephone book" operation could be a operation of Person (that is, insert this person into the argument telephone book) or a operation of PhoneBook (that is, insert the argument person in this telephone book).

    Which is better? Associating the operation with Person would require the Person class to have access to the internal representation details of the PhoneBook. However, associating the operation with PhoneBook would not require the PhoneBook to know about the internal details of the Person class--except to be able to compare two entries. Thus making insert an operation of PhoneBook and compare an operation of Person would best maintain the encapsulation of data.

Principle:
An object cannot manipulate the internal data of another object directly; it must use an operation of that object.

During design we should not be concerned with the minute details of the implementation. However, it is appropriate to consider whether there is a "reasonable" implementation. In fact, it is better to make sure there are two different possible implementations to ensure flexibility and adaptability.

It is good to have more than one person involved in a design. A second designer can review the first's work more objectively and ask difficult questions -- and vice versa.

Object-Oriented Design: Finding Relationships Among Classes

Two classes in a design may be related in one of several ways. The three relationships that are common are:

Use Relationship

Class A uses B when:

If objects of A can carry out all operations without any awareness of B, then A does not use B.

From the telephone book example, PhoneBook uses Person objects (that is, it inserts, deletes, modifies, etc., the entries). Person objects do not use PhoneBook objects.

If class A uses class B, then a change to class B (particularly to its public interface) may necessitate changes to class A. The following principle will make modification of a design and implementation easier.

Principle:
Minimize the coupling between classes (that is, the number of classes used by a class.)

Aggregation Relationship

Class A uses class B for aggregation if objects of A contain objects of B.

Note: Object A contains (has an) object B if A has an instance variable that somehow designates object B--a Java reference to B, the index of B, the key of B, etc.

Aggregation is a one-to-N relationship; one object might contain several others.

Note: Aggregation is a special case of the use relationship. If asked to identify the aggregation and use relationships, identify an aggregation relationship as such rather than as a use relationship.

For example, objects of the Person class in the telephone book example contain (has) name, address, and phoneNumber objects.

The Pascal record and C structure are aggregations; they contain other data fields. They are not, however, objects.

Inheritance Relationship

Class D inherits from class B if all objects of D are also objects of B.

As noted previously,

In the telephone book example, we design Professor to inherit from Employee; similarly, we design Staff to inherit from Employee. We make Employee itself inherit from Person; similarly, we design Student to inherit from Person.

Inheritance can lead to powerful and extensible designs. However, use and aggregation are more common than inheritance.

Object-Oriented Implementation

The outputs of the object-oriented design phase:

The purpose of the implementation phase is to code, test, and integrate the classes into the desired application system.

Object-oriented development:

Note: If we seek to evolve a prototype into a full program, we should never hesitate to reopen the analysis or design if new insight is discovered.


UP to CSCI 581 Lecture Notes root document?


Copyright © 1999, H. Conrad Cunningham
Last modified: Mon Feb 1 22:45:12 CST