Topic 7: Design and Testing

Version 4.31

FIRST DAY

Handouts:

Overview

(7.1) Software development activities

Four basic activities:

  1. Define the requirements
  2. Design
  3. Implementation
  4. Testing

The four activities are not necessarily sequential steps.

(7.2) Identifying classes and objects

A fundamental design question is, what are the classes?

Look for nouns in the problem statement. Nouns usually represent objects, hence may suggest classes. Generally, class names should be nouns, too.

Verbs may suggest methods.

Sometimes it's a tricky decision whether to implement something as an object or as primitive data. E.g., should salary be an integer? Does it have upper and lower bounds based on the job?

Strike a balance between classes that are too general and classes that are too specific. You may not need a separate class for every kind of appliance, maybe just a general appliance class that has a little bit of description like "toaster" or "dishwasher".

Besides the objects mentioned in the problem statement, other classes may be needed to get the job done.

Some of the classes we need may already exist; reuse them.

(7.3) Static class members

So far, we know:

Now we meet:

Listings 7.1–7.2: SloganCounter, Slogan: static variable count, static method getCount

(7.4) Class relationships

Three kinds of relationships between classes: dependency, aggregation, and inheritance. (Inheritance is deferred to a later chapter.)

  1. Dependency: class A depends on class B if A uses some of B's methods. This it may do either through references to instances of B, or through static methods.

    Too many dependencies between different classes can make the program revision complicated, since changing class B is likely to require changing class A.

    Sometimes a class is dependent on itself. This happens when we have a.foo(b), where a and b are both instances of the class.

    Listings 7.3–7.4 illustrate with a rational number class

    Look carefully at the constructor; the methods reciprocal, add, subtract, multiply, divide, equals; the private methods reduce and gcd. Explain with 4/5, 2/3.

  2. Aggregation: occurs when an object is made up (in part) of other objects. It has parts. This is a has-a relationship.

In OOP, an aggregate object is one that has instance variables referring to other objects.

"Aggregation is a special type of dependency" [really?] because the methods of the aggregate call the methods of the parts [usually or always?].

Listings 7.5–7.7 illustrate: Student is an aggregate object, containing Address objects as parts. It also contains Strings, but frequently we just pretend strings are a primitive data type.

this

When we want to refer to the object whose method is running (e.g., the referent of a in the call a.foo()), we can use the reserved word this. The word can also be used to select the instance variable that is shadowed by a local variable; we often do so in a constructor to avoid having to make up new variable names.

Examples of this:

this.x = x; // in a constructor with parameter x
this.x = (here.x + there.x) / 2.0;
deltaX = (other.x - this.x);
this.foo(); // same as just foo();
button.addActionListener(this);

UML Diagrams

See Figure 7.2. Here is a simplified version of it:

Figure 7.2 (simplified)

Design discussion

Programming Project 7.3 (Course, Student, CourseDriver).

Design only, not coding. Identify the classes and their principal attributes and methods. Sketch a UML diagram, which should show any relationships of dependency or aggregation between classes.

(7.5) Interfaces

A Java interface declares constants and abstract methods. Abstract methods are declared without an implementation, i.e., method body:

public interface Hittable // or just Hit
{
  public static final int LUCKY = 7;
  public int hit (int a, int b);
}

Classes that implement the interface must define implementations of all of its methods. A class may declare that it implements an interface, in which case it is treated as a subtype of the interface.

What's a subtype?

Listings 7.8–7.10: Complexity, Question, MiniQuiz

Two Important Interfaces in Java

  1. java.lang.Comparable declares a single method

    public int compareTo (Object obj); 
    

    such that a.compareTo(b) returns a negative number if a < b, zero if a = b, or a positive number if a > b.

    (Pronounce comparable, accenting the first syllable; many regard comparable as unacceptable.)

  2. java.util.Iterator declares the methods

    public boolean hasNext();
    public Object next();
    

    We know what these do.

SECOND DAY

(7.6) Enumerated types revisited

We know that an enumerated type is a special kind of class. Its constant values are instances of the class. For example, in

enum Size {small, medium, large}

we can declare and assign

Size shirtsize = Size.small;

Furthermore, the class can have a constructor that initializes instance data, as shown in Listings 7.11–7.12 (Season, SeasonTester). Very interesting!

We can iterate over the values of an enumerated type using the static method values(), which returns an array of the possible values of the enumerated type. (We'll cover arrays in Chapter 7; in the meantime, it's sufficient to know that an array is a kind of sequential collection, similar to a list.)

Finally, we can also define other methods for the enumerated type class. There is just a hint that this might be very exciting, with no elaboration.

(7.7) Method design

An algorithm is a step by step procedure for solving a problem.

Algorithms are often developed in pseudocode, before translating into a programming language. The pseudocode frees us from having to worry about the syntax of the language.

Design algorithms first, then code.

Method decomposition (also called top-down design is a way of handling complex problems by splitting them into subproblems. A top-level method solves the problem by calling on methods that solve the subproblems.

Example: Listings 7.13–7.14 PigLatin, PigLatinTranslator:

Method parameters: arguments are passed by value, i.e., the formal parameter receives a copy of the actual argument's value. However, in the case of reference types, what is copied is the reference, i.e., address, of the object. So the callee's mutating the object referenced affects the object reference in the caller, because it is the same object.

Listings 7.15, 16, 17 ParameterTester, ParameterModifier, Num illustrate by passing a primitive and two reference arguments.

There is very little in this section about actual algorithm design.

Discussion

Discuss some problems—in each of these, develop pseudocode first (in a text editor), then convert to comments and code:

  1. Start with two problems from chapter 5 (#5.11, 5.12) at the algorithmic level only.
  2. Other possible problems: 5.6, 5.13, 5.15, 5.18

(7.8) Method overloading

Method overloading occurs when we define two methods with the same name in the same class (usually—or occasionally in a superclass and subclass). The methods must have different signatures; i.e., the number, type, or order of their parameters must be different. The return type is not part of the signature, so if you change it, you must also change something else.

Example: println is overloaded several ways:

  1. 0-argument version
  2. 1-argument versions for each primitive type and for Object.

Constructors, like methods, can be overloaded, allowing flexibility in the creation of new objects.

Note that overloading does not increase the expressive power of the language, i.e., it does not enable us to compute functions that we could not computer otherwise. Without overloading, we'd just have to invent different names for each of the variants—e.g., println0, printlnInt, etc.

(7.9) Testing

Think about testing early in the design process. The aim of testing is to find errors; the earlier we find them, the better.

"Testing" in the broad sense can include:

  1. Running a program with inputs and comparing the outputs with the known correct result.
  2. Program verification (proof of correctness)—difficult for large-scale programs
  3. Reviews—let several people examine the design or code, looking for errors and other problems. Also called "walkthrough".

Methods of defect testing with I/O:

Remarks

  1. Testing is tedious work, but necessary.
  2. To avoid monotony and human errors, it is good to automate testing procedures as much as possible.
  3. It makes sense to test small pieces of codeunit testing—before putting them together to make larger pieces of code.
  4. JUnit is a popular framework for unit testing in Java. (There are similar "XUnit" packages for various programming languages X.)

    See my notes on JUnit.

(7.10) GUI design

Principles:

  1. Know the user's needs and possible activities
  2. Design software to avoid user input or command errors. E.g., disable commands that are not possible; use radio buttons rather than type-in to select from a small set of values.

  3. Optimize user abilities: in particular, provide shortcuts for the power user.
  4. Be consistent: the look, feel, organization, etc. should not be changed without good reason.

(7.11) Layout managers

Layout managers arrange the components within a container. We can select from several kinds of layout manager (see Figure 7.6):

Each kind of container has a default layout manager; we can call the setLayout method to override the default:

Listings 7.18–7.23 demonstrate various layout managers: LayoutDemo, IntroPanel, FlowPanel, BorderPanel, GridPanel, BoxPanel. Also demonstrates a JTabbedPane for the main "switch".

Some of the layout managers have alternate constructors and/or methods that allow us to specify gaps between regions or components, allowing the background color to be seen.

BoxLayout, instead, allows us to add invisible components (for spacing) using the methods createRigidArea and createGlue; glue is flexible (i.e., stretchable) invisible space.

(7.12) Borders

The borders of a component can be decorated in various ways.

Listing 7.24 BorderDemo

(7.13) Containment hierarchies

An application window can have components grouped into containers at multiple levels (boxes within boxes; also representable as a tree).

See Figures 7.12–7.13.

Possible problems for discussion

From Programming Projects:

Recommended:


  1. Version log:
    • Version 4.3, 2012 Feb 17. Updated for 7th edition.
    • Version 4.2, 2011 Apr 5. Fixed broken link to JUnit notes.
    • Version 4.1, 2011 Feb 19. Converted to markdown.
    • Version 4, 2010 Feb 18. Converted to RST.
    • Version 3, 2009 Feb 23. Revised for 6th edition.
    • Version 2, 2008 Feb 18. Revised for 5th edition.