Version 4.31
Handouts:
Four basic activities:
The four activities are not necessarily sequential steps.
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.
So far, we know:
Now we meet:
Apricot.color
Math.PI, which is a constantApricot.fall(). Cannot refer to instance variables, because they "do not operate in the context of a particular object."Listings 7.1–7.2: SloganCounter, Slogan: static variable count, static method getCount
Three kinds of relationships between classes: dependency, aggregation, and inheritance. (Inheritance is deferred to a later chapter.)
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.
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.
thisWhen 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);
Represent dependency of A on B as a dashed line with a V-shaped arrow head:
Represent A is an aggregate having a B as a part by using a solid line with a diamond on the aggregate (whole) end:
(If you want to think of the diamond as a funny kind of arrow head, then read the arrow as "B (the part) is aggregated into A (the whole)".)
See Figure 7.2. Here is a simplified version of it:
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.
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
UML diagrams: an interface is drawn in a box, like a class, but with <<interface>> before the interface name; if class C implements interface I, then draw a dashed line ending in an empty triangular arrow from C to I:
Example (simplification of Figure 7.3):
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.)
java.util.Iterator declares the methods
public boolean hasNext();
public Object next();
We know what these do.
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.
Could we override the toString method, e.g.,
public String toString () {
if (this == Season.winter)
return "THE COLD SEASON";
else
...
}
Or using instance data as in Listing 7.11
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:
translate(sentence) calls translateWord(word), for each word in sentence.translateWord calls beginsWithVowel and beginsWithBlend.privateMethod 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.
Discuss some problems—in each of these, develop pseudocode first (in a text editor), then convert to comments and code:
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:
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.
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:
Methods of defect testing with I/O:
Test inputs for black box testing fall into equivalence categories; within an equivalence category, we expect all inputs to produce similar outputs. For example, negative and non-negative numbers are two equivalence categories for a square root function.
Try to include test cases at and near (off by 1 from) the boundaries between the equivalence categories, as well as a few from inside each category.
White box (glass box) testing is driven by knowledge of the code; we seek to exercise every statement by crafting inputs that will take each possible branch of an if statement, or a loop (maximize statement coverage).
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.
Principles:
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.
Be consistent: the look, feel, organization, etc. should not be changed without good reason.
Layout managers arrange the components within a container. We can select from several kinds of layout manager (see Figure 7.6):
BorderLayoutBoxLayout (vertical or horizontal)CardLayout (like a deck of cards—HyperCard?—only the top card is visible—no example given)FlowLayoutGridLayout (two-dimensional)GridBagLayout—like GridLayout, but a component can occupy more than one row or column; very complex.Each kind of container has a default layout manager; we can call the setLayout method to override the default:
JFrame—default = BorderLayoutJPanel—default = FlowLayoutListings 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.
The borders of a component can be decorated in various ways.
Listing 7.24 BorderDemo
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.
From Programming Projects:
Lockable interface: the regular methods of a Lockable object are protected, in the sense that when the object is locked, they cannot be invoked. But what can this mean?Recommended: