1. Introduction

Object-Oriented Development

A software project usually involves three stakeholders: the client commissions the software, the developer builds the software, and the user uses the software. These roles may be played by individuals or organizations. In fact, all three roles may be played by the same actor, but it will still be important for the actor to remember which role he is playing at any given moment. Just as a play is divided into carefully scripted acts and scenes, a software project follows a carefully scripted development process, which is usually divided into five phases:

Analysis: All three participants create a specification document that describes the application's requirements as well as the application domain's important terms, concepts, and relationships.

Design: Using the specification document as a guide, the developer creates an architectural document that describes the application's important classes, together with their responsibilities and collaborations.

Implementation: Using the architectural document as a guide, the developer implements the classes it describes. The developer may need to introduce supporting classes.

Testing: Of course the developer tests the structure and function of each class he implements (unit testing), but the application must also be tested as a whole, first by the developer (integration testing), then by the users (acceptance testing). Integration testing is the riskiest phase.

Maintenance: Maintenance involves fixing bugs (corrective maintenance), porting the application to new platforms (adaptive maintenance), and adding new features (perfective maintenance). Maintenance is the longest and costliest phase.

Iterative-incremental development processes mitigate the risk of integration testing by allowing the developer to iterate through these phases many times. Each iteration produces an increment of the specification document, architectural document, and a tested implementation. Typically, high priority and high risk requirements are attacked during the early iterations. Thus, a decision to abort a project can be made early, before too much money has been spent:

In theory, a developer may be working on the specification near the end of the project and the implementation near the beginning. In practice, most of the specification is completed during the early iterations through the specification phase. Only finishing touches might be added late in the project. Administrative issues such as resolving file dependencies and implementing constructors and destructors are typical activities during the early iterations through the implementation phase, while implementing low-level supporting functions is more common during the late iterations. The following graph gives a rough idea of the maturity rates of the specification, architecture, implementation, and test plan as time passes:

Analysis Overview

Requirements Modeling
Domain Modeling
Scheduling and Cost Estimation

Design Overview

The input to the design phase, the specification document, can be understood as a description of the behavior of the system to be built. By behavior, we of course mean externally observable behavior, the input-output behavior. Input-output behavior is implicitly or explicitly understood as a collection of functions that realize system requirements. A function might be described in a specification document as one or more use cases:

The output of the design phase, the architectural document, is also a specification; it specifies the internal structure of the system, and therefore serves as a roadmap for the programmers who will implement the system. Design is use-case driven. Every element in a design document must trace back to a use case that it supports.

The main processes that occur in the design phase are decomposition and abstraction. The system is decomposed into modules. These modules are decomposed into sub-modules, and so on. Each module is an abstraction: the description of its purpose or behavior is independent of its implementation.

What is a module? To some degree the answer to this question depends on the design phase iteration. During the early iterations, module might mean subsystem. During late iterations, module might mean function or data structure. During the intense middle iterations, the definition of module depends on the design strategy.

Functional strategies decompose systems into hierarchies of functions and their supporting functions, with system state maintained by a centralized global data structure:

Wirth's Structured Programming is the simplest example of a functional strategy. It combines top-down decomposition with stepwise refinement. Other examples include:

Structured Design (Constantine and Yourdon)
Jackson Structured Programming (Jackson)
Wainer-Orr (Wainer)

Object-oriented strategies are data-driven. Instead of a centralized repository, system state is distributed among objects that encapsulate some part of the system state. These objects drive the application by passing messages to each other to request some modification of the system state. Naturally, an object is only authorized to modify the portion of the system state that it encapsulates.

Examples of object-oriented design strategies include:

OMT (Rumbaugh)
Booch Method (Booch)
The Unified Process (Booch, Rumbaugh, and Jacobson)

The focus of these notes will be object-oriented design. Although the development process described earlier is based on the Unified Process, our approach to design will be pattern-oriented. We will build designs by combining design patterns-- reusable groups of reusable classes and objects that collaborate with each other in canonical ways.

Extreme Programming

The goal of design is to make the inevitable system modifications less traumatic. A good design is easy to modify. That is the accepted wisdom. A few developers are beginning to challenge this view. Extreme Programming or X Programming is based on refactoring. Refactoring is a collection of techniques for repairing defective designs (also called anti-patterns). Accepting that all designs are inherently defective, X Programmers only devote enough time to the design phase to produce a design that supports the current set of requirements. Later, when more requirements are added to the specification, refactoring techniques are used to extend the design to the new requirements. A carefully designed suite of regression tests ensures that the modified design still supports the original requirements.

Implementation Overview

Programming Team Organization
   Chief Programmer Teams
   Democratic Programming
Frameworks, Toolkits, and Libraries
Programming
   Programming Paradigms
   Problem Solving Strategies
      Top Down
      Bottom Up
   Algorithms and Data Structures
   Style Conventions
   Error Handling
Source Code Control
Configuration Management
Bug Reporting Systems
Deployment

Testing Overview

Unit Testing
System Testing
Acceptance Testing

Functional versus Structural Testing
Static versus Dynamic Testing

Maintenance Overview

Corrective Maintanance
Adaptive Maintenance
Perfective Maintenance

Object-Oriented Modeling

The physical, natural, and social sciences are built on the idea of systems modeling. There are several popular modeling paradigms:

functional modeling: model = function (calculus, LISP, data flow)

relational modeling: model = relation (logic, SQL, Prolog)

imperative modeling: model = finite state machine (DDS, C)

In object-oriented modeling models are collections of objects. Object-oriented models can be used to describe real world systems (domain models) or software systems (architectural models). Not only are object-oriented domain models more intuitive than other types of models (because the real world is populated with real objects), but also, an object-oriented domain model can often be refined into an object-oriented architectural model, thus narrowing the semantic gap that exists between application domains and solution domains (= functions + data structures + instructions + files + etc.)

UML

Not surprisingly, object-oriented models appear prominently in specification and architectural documents. Today, most object-oriented models are expressed using the Universal Modeling Language (UML). UML was developed by Rational Software corporation [WWW-6] and was subsequently chosen by OMG, the Object Management Group [WWW-14], as the "industry standard" object-oriented modeling language. As such, UML replaces or incorporates several competing languages that preceded it.

Although UML describes many types of diagrams, we will only introduce and use a restricted subsets of class, package, object, and interaction (sequence) diagrams. For a more thorough treatment, the interested reader should consult [FOW-1] or any of the dozens of other books on UML that are currently available.