C H A P T E R  8

Object-Oriented Analysis and Design–An Overview

Object-oriented programming is an exceptionally bad idea, which could only have originated in California.

—Edsger Dijkstra

The object has three properties, which makes it a simple, yet powerful model building block. It has state so it can model memory. It has behavior, so that it can model dynamic processes. And it is encapsulated, so that it can hide complexity.

—Trygve Reenskaug, Working With Objects

Well, yes, we've all learned about the object-oriented programming paradigm before. But it never hurts to go over some basic definitions so that we're all on the same page for our discussion about object-oriented analysis and design.

First of all, objects are things. They have an identity (i.e., a name), a state (i.e., a set of attributes that describes the current data stored inside the object), and a defined set of operations that operate on that state. A stack is an object, as is an Automobile, a Bank Account, a Window, or a Button in a graphical user interface. In an object-oriented program, a set of cooperating objects pass messages among themselves. The messages make requests of the destination objects to invoke methods that either perform operations on their data (thus changing the state of the object), or to report on the current state of the object. Eventually work gets done. Objects use encapsulation and information hiding (remember, they're different) to isolate data and operations from other objects in the program. Shared data areas are (usually) eliminated. Objects are members of classes that define attribute types and operations.

Classes are templates for objects. Classes can also be thought of as factories that generate objects. So an Automobile class will generate instances of autos, a Stack class will create a new stack object, and a Queue class will create a new queue. Classes may inherit attributes and behaviors from other classes. Classes may be arranged in a class hierarchy where one class (a super class or base class) is a generalization of one or more other classes (sub-classes). A sub-class inherits the attributes and operations from its super class and may add new methods or attributes of its own. In this sense a sub-class is more specific and detailed than its super class; hence, we say that a sub-class extends a super-class. For example, a priority queue is a more specific version of a queue; it has all the attributes and operations of a queue, but it adds the idea that some queue elements are more important that others. In Java this feature is called inheritance while in UML it's called generalization.1 Go figure.

There are a number of advantages to inheritance. It is an abstraction mechanism which may be used to classify entities. It is a reuse mechanism at both the design and the programming level. The inheritance graph is a source of organizational knowledge about domains and systems.

And, of course, there are problems with inheritance, as well. It makes object classes that are not self-contained; sub-classes cannot be understood without reference to their super classes. Inheritance introduces complexity and this is undesirable, especially in critical systems. Inheritance also usually allows overloading of operators (methods in Java) which can be good (polymorphism) or bad (screening useful methods in the superclass).

Object-oriented programming (OOP) has a number of advantages, among them easier maintenance, because objects can be understood as stand-alone entities. Objects are also appropriate as reusable components. But, for some problems there may be no mapping from real-world objects to system objects, meaning that OOP is not appropriate for all problems.

An Object-Oriented Analysis and Design Process

Object-oriented analysis (OOA), design (OOD) and programming (OOP) are related but distinct. OOA is concerned with developing an object model of the application domain. So, for example, you take the problem statement, generate a set of features and (possibly) use cases,2 tease out the objects and some of the methods within those objects that you'll need to satisfy the use case, and you put together an architecture of how the solution will hang together. That's object-oriented analysis.

OOD is concerned with developing an object-oriented system model to satisfy requirements. You take the objects generated from your OOA, figure out whether to use inheritance, aggregation, composition, abstract classes, interfaces, and so on, in order to create a coherent and efficient model, draw the class diagrams, and flesh out the details of what each attribute is and what each method does, and describe the interfaces. That's the design.

Some people like object-oriented analysis, design, and programming3 and some people don't.4

So object-oriented analysis allows you to take a problem model and re-cast it in terms of objects and classes and object-oriented design allows you to take your analyzed requirements and connect the dots between the objects you've proposed and to fill in the details with respect to object attributes and methods. But how do you really do all this? Well, here is a proposed process that starts to fill in some of the details.5 We'll figure out the rest as we go along.

_________

1 Fowler, M. UML Distilled. (Boston, MA: Addison-Wesley, 2000.)

2 Cockburn, A. Writing Effective Use Cases. (Boston, MA: Addison-Wesley, 2000.)

3 Beck, K., and B. Boehm. “Agility through Discipline: A Debate.” IEEE Computer 36 (6):44-46. (2003)

4 Graham, Paul. “Why Arc isn't Especially Object Oriented,” retrieved from www.paulgraham.com/noop.html on October 12, 2009.

5 McLaughlin, Brett D., et. al. Head First Object-Oriented Analysis & Design. (O'Reilly Media, Inc. Sebastopol, CA: 2007.)

  1. Write (or receive) the problem statement. Use this to generate an initial set of features.
  2. Create the feature list. The feature list is the set of program features that you derive from the problem statement; it contains your initial set of requirements.
  3. Write up use cases. This helps to refine the features and to dig out new requirements and to expose problems with the features you just created. We'll also see that we can use user stories for this step.
  4. Break the problem into subsystems or modules or whatever you want to call them as long as they're smaller, self-contained bits usually related to functionality.
  5. Map your features, subsystems, and use cases to domain objects; create abstractions.
  6. Identify the program's objects, methods, and algorithms.
  7. Implement this iteration.
  8. Test the iteration.
  9. If you've not finished the feature list and you still have time and/or money left, go back to step 4 and do another iteration, otherwise…
  10. Do final acceptance testing and release.

Note that this process leaves out a lot of details like the length of iteration. How many features end up in an iteration? How and when do we add new features to the feature list? How exactly do we identify objects and operations? How do we abstract objects into classes? Where do we fix bugs that are found in testing? Do we do reviews of code and other project work products?

Leaving out steps here is okay. We're mostly concerned with the analysis and design elements of the process. We'll discuss ideas on the rest of the process below and some of the answers are also in Chapter 3 on project management.

How do the process steps above fit into the software development life cycle? Well, I'm glad you asked. Recall that the basic development life cycle has four steps:

  1. Requirements Gathering and Analysis;
  2. Design;
  3. Implementation and Testing; and
  4. Release, Maintenance, and Evolution.

We can easily assign the previous ten steps into four buckets, as follows:

Requirements Gathering and Analysis

  1. Problem statement.
  2. Feature list creation.
  3. Use case generation.

Design

  1. Break up the problem.
  2. Map features and use cases to domain objects.
  3. Identify objects, methods, and algorithms.

Implementation and Testing

  1. Implement this iteration.
  2. Test the iteration.
  3. If you've not finished with the feature list or out of time, go back to step 4, otherwise…

Release/Maintenance/Evolution

  1. Do final acceptance testing and release.

Once again we can ignore the details of each process step for now. These details really depend on the process methodology you choose for your development project. The description of the process above uses an iterative methodology and can easily be fitted into an agile process, or a more traditional staged release process.

Note also, that you'll need to revisit the requirements whenever you get to step 4, because you're likely to have uncovered or generated new requirements during each iteration. Also, whenever your customer sees a new iteration, they'll ask for more stuff (yes, they will, trust me). This means you'll be updating the feature list (and re-prioritizing) at the beginning of each new iteration. BEWARE!

Doing the Process

Let's continue by working through an extended example, seeing where the problem statement leads us and how we can tease out requirements and begin our object oriented analysis.

The Problem Statement

Burt, the proud owner of Birds by Burt, has created the ultimate in bird feeders. Burt's Bird Buffet and Bath (B4), is an integrated bird feeder and bird bath. It comes in 12 different colors (including camo) and 1, 3, and 5 lb capacities. It will hold up to one gallon of water in the attached bird bath, it has a built-in hanger so you can hang it from a tree branch or from a pole, and the B4 is just flying off the shelves. Alice and Bob are desperate for a B4, but they'd like a few changes. Alice is a techno-nerd and a fanatic songbird watcher. She knows that her favorite songbirds only feed during the day, so she wants a custom B4 that allows the feeding doors to open automatically at sunrise and close automatically at sunset. Burt, ever the accommodating owner, has agreed and the hardware division of Birds by Burt is hard at work designing the B4++ for Alice. Your job is to write the software to make the hardware work.

The Feature List

The first thing we need to do is figure out what the B4++ will actually do. This version seems simple enough. We can almost immediately write down three requirements:

  • The feeding doors must all open and close simultaneously.
  • The feeding doors should open automatically at sunrise.
  • The feeding doors should close automatically at sunset.

So this doesn't seem so bad. The requirements are simple and there is no user interaction required. The next step is to create a use case so we can see just what the bird feeder is really going to do.

Use Cases

A use case is a description of what a program does in a particular situation. It's the detailed set of steps that the program executes when a user asks for something. Use cases always have an actor – some outside agent that gets the ball rolling, and a goal – what the use case is supposed to have done by the end. The use case describes what it takes to get from some initial state to the goal from the user's perspective.6 Here's a quick example of a use case for the B4++:

  1. The sensor detects sunlight at a 40% brightness level.
  2. The feeding doors open.
  3. Birds arrive, eat, and drink.
  4. Birds leave.
  5. The sensor detects a decrease in sunlight to a 25% brightness level.
  6. The feeding doors close.

Given the simplicity of the B4++, that's about all we can expect out of a use case. In fact, steps 3 and 4 aren't technically part of the use case, because they aren't part of the program – but they're good to have so that we can get a more complete picture of how the B4++ is operating. Use cases are very useful in requirements analysis because they give you an idea – in English – of what the program needs to do in a particular situation and because they nearly always will help you uncover new requirements. Note that in the use case we don't talk about how a program does something, we only concentrate on what the program has to do to reach the goal. Use cases are generated during the Requirements Gathering and Analysis phase of the software life cycle, so we're not so much concerned with the details yet, we just treat the program as a black box and let the use case talk about the external behavior of the program. Most times there will be several use cases for every program you write. We've only got one because this version of the B4++ is so simple.

Decompose the Problem

So now that we've got our use case we can probably just decompose the problem and identify the objects in the program. You go ahead, I'll wait....

__________

6 Cockburn, 2000.

Done? Okay. This problem is quite simple; if you look at the use case above and pick out the nouns, you see that we can identify several objects. Each of these objects has certain characteristics and contributes to reaching the goal of getting the birds fed. (Yes, I know, “birds” is a noun in the use case, but they are the actors in this little play so for the purposes of describing the objects we ignore them – they're not part of the program.) The other two nouns of interest are “sensor” and “doors.” These are the critical pieces of the B4++, because the use case indicates that they are the parts that accomplish the goal of opening and closing the feeding doors at sunrise and sunset. So it's logical that they are objects in our design. Here are the objects I came up with for this first version of the B4++ and a short description:

BirdFeeder: The top-level object. The bird feeder has one or more feeding doors at which the birds will gather, and a sensor to detect sunrise and sunset. The BirdFeeder class needs to control the querying of the light sensor and the opening and closing of the feeding doors.

Sensor: There will be a hardware light sensor that detects different light levels. We'll need to ask it about light levels.

FeedingDoor: There will be several feeding doors on the bird feeder. They have to open and close.

That's probably about it for classes at this point. Now what do they all do? To describe classes and their components we can use another UML feature, class diagrams.

Class Diagrams

A class diagram allows you to describe the attributes and the methods of a class. A set of class diagrams will describe all the objects in a program and the relationships between the objects. We draw arrows of different types between class diagrams to describe the relationships. Class diagrams give you a visual description of the object model that you've created for your program. We saw a set of class diagrams for the Fox and Rabbit program we described in Chapter 5.

Class diagrams have three sections:

  • Name: The name of the class
  • Attributes: The instance data fields and their types used by the class
  • Methods: The set of methods used by the class and their visibility.

We can see an example of a class diagram for our BirdFeeder class in Figure 8-1.

images

Figure 8-1. The BirdFeeder class

The diagram shows that the BirdFeeder class has a single integer attribute, lightLevel, and a single method, operate(). By themselves class diagrams aren't terribly interesting, but when you put several of them together and show the relationships between them, then you can get some interesting information about your program. So what else do we need in the way of class diagrams? In our program the BirdFeeder class uses the FeedingDoor and Sensor classes, but they don't know (or care) about each other. In fact, while BirdFeeder knows about FeedingDoor and Sensor and uses them, they don't know they are being used. Ah, the beauty of object-oriented programming. This relationship can be expressed in the class diagram of all three classes shown in Figure 8-2.

images

Figure 8-2. BirdFeeder uses FeedingDoor and Sensor

In UML, the dotted line with the open arrow at the end indicates that one class (in our case BirdFeeder) is associated with another class (in our case either FeedingDoor or Sensor) by using it.

Code Anyone?

Now that we've got the class diagrams and know the attributes, the methods, and the association between the classes it's time to flesh out our program with some code.

In the BirdFeeder object, the operate() method needs to check the light levels and open or close the feeding doors depending on the current light level reported by the Sensor object, and does nothing if the current light level is above or below the threshold values..

In the Sensor object, the getLevel() method just reports back the current level from the hardware sensor.

In the FeedingDoor object, the open() method checks to see if the doors are closed. If they are, it opens them and sets a boolean to indicate that they're open. The close() method does the reverse.

Here's the code for each of the classes described.

/**
 * class BirdFeeder
 *
 * @author John F. Dooley
 * @version 1.0
 */

import java.util.ArrayList;
import java.util.Iterator;

public class BirdFeeder
{
    /* instance variables */
    private static final int ON_THRESHOLD = 40;
    private static final int OFF_THRESHOLD = 25;
    private int lightLevel;
    private Sensor s1;
    private ArrayList<FeedingDoor> doors = null;

    /*
     * Default Constructor for objects of class BirdFeeder
     */
    public BirdFeeder()
    {
        doors = new ArrayList<FeedingDoor>();
        /* initialize lightLevel */
        lightLevel = 0;
         s1 = new Sensor();
        /* by default we have a feeder with just one door */
        doors.add(new FeedingDoor());
        
    }
    /*
     * The operate() method operates the birdfeeder.
     * It gets the current lightLevel from the Sensor and
     * checks to see if we should open or close the doors
     */
    public void operate()
    {
        lightLevel = s1.getLevel();
        
        if (lightLevel > ON_THRESHOLD) {
            Iterator door_iter = doors.iterator();
            while (door_iter.hasNext()) {
                FeedingDoor a = (FeedingDoor) door_iter.next();
                a.open();
                System.out.println("The door has opened.");
            }
        } else if (lightLevel < OFF_THRESHOLD) {
             Iterator door_iter = doors.iterator();
             while (door_iter.hasNext()) {
                FeedingDoor a = (FeedingDoor) door_iter.next();
                a.close();
                System.out.println("The door has closed.");
            }
        }
    }
}


/**
 * class FeedingDoor
 *
 * @author John Dooley
 * @version 1.0
 */
public class FeedingDoor
{
    /* instance variables */
    private boolean doorOpen;

    /*
     * Default constructor for objects of class FeedingDoors
     */
    public FeedingDoor()
    {
        /* initialize instance variables */
        doorOpen = false;
    }

    /*
     * open the feeding doors
     * if they are already open, do nothing
     */
    public void open( )
    {
        /** if the door is closed, open it */
        if (doorOpen == false) {
            doorOpen = true;
        }
    }
    
    /*
     * close the doors
     * if they are already closed, do nothing
     */
    public void close( )
    {
        /* if the door is open, close it */
        if (doorOpen == true) {
            doorOpen = false;
        }
    }
    /*
     * report whether the doors are open or not
     */
    public boolean isOpen()
    {
        return doorOpen;
    }
}

/**
 * class Sensor
 *
 * @author John Dooley
 * @version 1.0
 */
public class Sensor
{
    /* instance variables */
    private int lightLevel;

    /*
     * Default constructor for objects of class Senso
     */
    public Sensor(
    {
        /** initialize instance variable */
        lightLevel = 0;
    }

    /**
     * getLevel - return a light leve
     *
     * @return the value of the light level
     * that is returned by the hardware sensor
     */
    public int getLevel( )
    {
        /* till we get a hardware light sensor, we just fake it */
        lightLevel = (int) (Math.random() * 100)
        return lightLevel
    }
}

Finally, we have a BirdFeederTester class that operates the B4++.

/**
 * The class that tests the BirdFeeder, Sensor, and
 * FeedingDoor classes.
 *
 * @version 0.1
 */
public class BirdFeederTester
{
    private BirdFeeder feeder;

    /*
     * Constructor for objects of class BirdFeederTest
     */
    public BirdFeederTester()
    {
        this.feeder = new BirdFeeder();
    }
   
    public static void main(String [] args)
    {
        BirdFeederTester bfTest = new BirdFeederTester();
       
        for (int i = 0; i < 10; i++) {
            System.out.println("Testing the bird feeder");
            bfTest.feeder.operate();
            try {
                Thread.currentThread().sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Sleep interrupted" + e.getMessage());
                System.exit(1);
            }
        }
    }
}

When Alice and Bob take delivery of the B4++ they are thrilled. The doors automatically open and close, the birds arrive and eat their fill. Birdsong fills the air. What else could they possibly want?

Conclusion

Object-oriented design is a methodology that works for a very wide range of problems. The real world is easily characterized as groups of cooperating objects. This single simple idea promotes simplicity of design, reuse of both designs and code, and the ideas of encapsulation and information hiding that Parnas advocated in his paper on modular decomposition. It's not the right way to solve some problems, including problems like communications protocol implementations, but it opens up a world of new and better solutions for many others and it closes the “intellectual distance” between the real-world description of a problem and the resulting code. Onward!

References

Beck, K., and B. Boehm. “Agility through Discipline: A Debate.” IEEE Computer 36 (6):44-46. (2003)

Cockburn, A. Writing Effective Use Cases. (Boston, MA: Addison-Wesley, 2000.)

Fowler, M. UML Distilled. (Boston, MA: Addison-Wesley, 2000.)

Graham, Paul. “Why Arc isn't Especially Object Oriented,” retrieved from www.paulgraham.com/noop.html on October 12, 2009.

McLaughlin, Brett D., et. al. Head First Object-Oriented Analysis & Design. (O'Reilly Media, Inc. Sebastopol, CA: 2007.)

Wirfs-Brock, R. and A. McKean. Object Design: Roles Responsibilities, and Collaborations. (Boston, MA: Addison-Wesley, 2003.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.206.112