• No results found

a design pattern: composite (pair)

objects and classes

3.9 a design pattern: composite (pair)

Although software design and programming are often difficult chal-lenges, many experienced software engineers will argue that software engineering really has only a relatively small set of basic problems. Per-haps this is an understatement, but it is true that many basic problems are seen over and over in software projects. Software engineers who are familiar with these problems, and in particular, the efforts of other pro-grammers in solving these problems, have the advantage of not needing to “reinvent the wheel.”

The idea of a design pattern is to document a problem and its solution so that others can take advantage of the collective experience of the entire

3. This applies to top-level classes shown so far; later we will see nested and inner classes, which may be declared private.

A design pattern applicable in a wide variety of contexts.

software engineering community. Writing a pattern is much like writing a recipe for a cookbook; many common patterns have been written and, rather than expending energy reinventing the wheel, these patterns can be used to write better programs. Thus a design pattern describes a problem that occurs over and over in software engineering, and then describes the solution in a sufficiently generic manner as to be applicable in a wide vari-ety of contexts.

Throughout the text we will discuss several problems that often arise in a design, and a typical solution that is employed to solve the problem. We start with the following simple problem.

A common design pattern is to return two objects as a pair.

In most languages, a function can return only a single object. What do we do if we need to return two or more things? The easiest way to do this is to combine the objects into a single object using either an array or a class. The most common situation in which multiple objects need to be returned is the case of two objects. So a common design pattern is to return the two objects as a pair. This is the composite pattern.

Pairs are useful for implementing key-value pairs in maps and dictionaries.

In addition to the situation described above, pairs are useful for imple-menting maps and dictionaries. In both of these abstractions, we maintain key-value pairs: The pairs are added into the map or dictionary, and then we search for a key, returning its value. One common way to implement a map is to use a set. In a set, we have a collection of items, and search for a match. If the items are pairs, and the match criterion is based exclusively on the key component of the pair, then it is easy to write a class that con-structs a map on the basis of a set. We will see this idea explored in more detail in Chapter 19.

summary

This chapter described the Java class and package constructs. The class is the Java mechanism that is used to create new reference types; the package is used to group related classes. For each class, we can

n Define the construction of objects

n Provide for information hiding and atomicity n Define methods to manipulate the objects

The class consists of two parts: the specification and the implementation.

The specification tells the user of the class what the class does; the implemen-tation does it. The implemenimplemen-tation frequently contains proprietary code and in some cases is distributed only as a .classfile. The specification, however, is

public knowledge. In Java, a specification that lists the class methods can be generated from the implementation by using javadoc.

Information-hiding can be enforced by using the privatekeyword. Initial-ization of objects is controlled by the constructors, and the components of the object can be examined and changed by accessor and mutator methods, respectively. Figure 3.17 illustrates many of these concepts, as applied to a simplified version of ArrayList. This class, StringArrayList, supports add,get, and size. A more complete version that includes set,remove, and clear is in the online code.

The features discussed in this chapter implement the fundamental aspects of object-based programming. The next chapter discusses inheritance, which is central to object-oriented programming.

key concepts

accessorA method that examines an object but does not change its state. (76)

aliasingA special case that occurs when the same object appears in more than one role. (81)

atomic unitIn reference to an object, its parts cannot be dissected by the gen-eral users of the object. (70)

classConsists of fields and methods that are applied to instances of the class.

(71)

class specification Describes the functionality, but not the implementation. (73)

CLASSPATH variableSpecifies directories and files that should be searched to find classes. (94)

composite pattern The pattern in which we store two or more objects in one entity. (96)

constructorTells how an object is declared and initialized. The default con-structor is a member-by-member default initialization, with primitive fields initialized to zero and reference fields initialized to null. (76)

design patternDescribes a problem that occurs over and over in software engineering, and then describes the solution in a sufficiently generic man-ner as to be applicable in a wide variety of contexts. (96)

encapsulationThe grouping of data and the operations that apply to them to form an aggregate while hiding the implementation of the aggregate.

(70)

equals methodCan be implemented to test if two objects represent the same value. The formal parameter is always of type Object. (78)

fieldA class member that stores data. (72)

figure 3.17 Simplified

StringArrayList with add,get, and size

1 /**

2 * The StringArrayList implements a growable array of Strings.

3 * Insertions are always done at the end.

4 */

19 * @throws ArrayIndexOutOfBoundsException if index is bad.

20 */

47 private static final int INIT_CAPACITY = 10;

48

49 private int theSize = 0;

50 private String [ ] theItems = new String[ INIT_CAPACITY ];

51 }

implementationRepresents the internals of how the specifications are met. As far as the class user is concerned, the implementation is not important.

(73)

import directiveUsed to provide a shorthand for a fully qualified class name.

Java 5 adds the static import that allows a shorthand for a static member.

(91)

information hidingMakes implementation details, including components of an object, inaccessible. (70)

instance members Members declared without the static modifier. (83)

instanceof operatorTests if an expression is an instance of a class. (82)

javadocAutomatically generates documentation for classes. (73)

javadoc tag Includes @author, @param,@return, and @throws. Used inside of javadoc comments. (74)

methodA function supplied as a member that, if not static, operates on an instance of the class. (71)

mutatorA method that changes the state of the object. (76)

objectAn entity that has structure and state and defines operations that may access or manipulate that state. An instance of a class. (70)

object-based programmingUses the encapsulation and information-hiding features of objects but does not use inheritance. (71)

object-oriented programmingDistinguished from object-based programming by the use of inheritance to form hierarchies of classes. (69)

package Used to organize a collection of classes. (90)

package statement Indicates that a class is a member of a package. Must pre-cede the class definition. (93)

package-visible access Members that have no visibility modifiers are only accessible to methods in classes in the same package. (95)

package-visible classA class that is not public and is accessible only to other classes in the same package. (95)

pairThe composite pattern with two objects. (96)

privateA member that is not visible to nonclass methods. (72)

publicA member that is visible to nonclass methods. (72)

static fieldA field that is shared by all instances of a class. (83)

static initializerA block of code that is used to initialize static fields. (86)

static methodA method that has no implicit thisreference and thus can be invoked without a controlling object reference. (83)

this constructor callUsed to make a call to another constructor in the same class. (82)

this referenceA reference to the current object. It can be used to send the cur-rent object, as a unit, to some other method. (79)

toString methodReturns a String based on the object state. (78)

common errors

1. Private members cannot be accessed outside of the class. Remember that, by default, class members are package visible: They are visible only within the package.

2. Usepublic class instead of class unless you are writing a throw-away helper class.

3. The formal parameter to equals must be of type Object. Otherwise, although the program will compile, there are cases in which a default

equals (that simply mimics ==) will be used instead.

4. Static methods cannot access nonstatic members without a controlling object.

5. Classes that are part of a package must be placed in an identically named directory that is reachable from the CLASSPATH.

6. this is a final reference and may not be altered.

7. Constructors do not have return types. If you write a “constructor” with return type void, you have actually written a method with the same name as the class, but this is NOT a constructor.

on the internet

Following are the files that are available:

TestIntCell.java Contains a main that tests IntCell, shown in Figure 3.3.

IntCell.java Contains the IntCell class, shown in Figure 3.4.

The output of javadoc can also be found as IntCell.html.

Date.java Contains the Date class, shown in Figure 3.6.

BigRational.java Contains the BigRationalclass, shown in Section 3.7 and found in package weiss.math.

Ticket.java Contains the Ticket static member example in Figure 3.9.

Squares.java Contains the static initializer sample code in Figure 3.10.

StringArrayList.java Contains a more complete version of

StringArrayList code in Figure 3.17.

ReadStringsWithStringArrayList.java

Contains a test program for StringArrayList.

exercises

IN SHORT

3.1 What is information hiding? What is encapsulation? How does Java support these concepts?

3.2 Explain the public and private sections of the class.

3.3 Describe the role of the constructor.

3.4 If a class provides no constructor, what is the result?

3.5 Explain the uses of this in Java.

3.6 What happens if when attempting to write a constructor, a void return type is included?

3.7 What is package-visible access?

3.8 For a class ClassName, how is output performed?

3.9 Give the two types of importdirective forms that allow BigRationalto be used without providing the weiss.math package name.

3.10 What is the difference between an instance field and a static field?

3.11 Under what circumstances can a static method refer to an instance field in the same class?

3.12 What is a design pattern?

3.13 For the code in Figure 3.18, which resides entirely in one file, a. Line 17 is illegal, even though line 18 is legal. Explain why.

b. Which of lines 20 to 24 are legal and which are not? Explain why.

IN THEORY

3.14 A class provides a single private constructor. Why would this be useful?

3.15 Suppose that the main method in Figure 3.3 was part of the IntCell class.

a. Would the program still work?

b. Could the commented-out line in main be uncommented without generating an error?

3.16 Is the following import directive, which attempts to import virtually the entire Java library, legal?

import java.*.*;

3.17 Suppose the code in Figures 3.3 (TestIntCell) and 3.4 (IntCell) are both compiled. Then, the IntCell class in Figure 3.4 is modified by adding a one-parameter constructor (thus removing the default zero-parameter constructor). Of course if TestIntCellis recompiled, there will be a compilation error. But if TestIntCellis not recompiled, and

IntCell is recompiled by itself, there is no error. What happens when

TestIntCell is then run?

IN PRACTICE

3.18 A combination lock has the following basic properties: the combina-tion (a sequence of three numbers) is hidden; the lock can be opened by providing the combination; and the combination can be changed, but only by someone who knows the current combination. Design a

1 class Person 2 {

3 public static final int NO_SSN = -1;

4

5 private int SSN = 0;

6 String name = null;

7 } 8

9 class TestPerson 10 {

11 private Person p = new Person( );

12

13 public static void main( String [ ] args ) 14 {

15 Person q = new Person( );

16

17 System.out.println( p ); // illegal 18 System.out.println( q ); // legal 19

20 System.out.println( q.NO_SSN ); // ? 21 System.out.println( q.SSN ); // ? 22 System.out.println( q.name ); // ? 23 System.out.println( Person.NO_SSN ); // ? 24 System.out.println( Person.SSN ); // ? 25 }

26 } figure 3.18

Code for Exercise 3.13

class with public methods open and changeCombo and private data fields that store the combination. The combination should be set in the constructor.

3.19 Wildcard import directives are dangerous because ambiguities and other surprises can be introduced. Recall that both java.awt.Listand

java.util.List are classes. Starting with the code in Figure 3.19:

a. Compile the code; you should get an ambiguity.

b. Add an import directive to explicitly use java.awt.List. The code should now compile and run.

c. Uncomment the local List class and remove the import directive you just added. The code should compile and run.

d. Recomment the local List, reverting back to the situation at the start. Recompile to see the surprising result. What happens if you add the explicit import directive from step (b)?

3.20 Move class IntCell (Figure 3.3) into package weiss.nonstandard, and revise TestIntCell (Figure 3.4) accordingly.

3.21 Add the following methods to the BigRationalclass, making sure to throw any appropriate exceptions:

BigRational pow( int exp ) // exception if exp<0 BigRational reciprocal( )

BigInteger toBigInteger( ) // exception if denominator is not 1 int toInteger( ) // exception if denominator is not 1 3.22 For the BigRational class, add an additional constructor that takes two

BigIntegers as parameters, making sure to throw any appropriate exceptions.

figure 3.19 Code for Exercise 3.19 illustrates why wildcard imports are bad.

1 import java.util.*;

2 import java.awt.*;

3

4 class List // COMMENT OUT THIS CLASS TO START EXPERIMENT 5 {

6 public String toString( ) { return "My List!!"; } 7 }

8

9 class WildCardIsBad 10 {

11 public static void main( String [ ] args ) 12 {

13 System.out.println( new List( ) );

14 } 15 }

3.23 Modify the BigRationalclass so that 0/0 is legal and is interpreted as

“Indeterminate” by toString.

3.24 Write a program that reads a data file containing rational numbers, one per line, stores the numbers in an ArrayList, removes any dupli-cates, and then outputs the sum, arithmetic mean, and harmonic mean of the remaining unique rational numbers.

3.25 Suppose you would like to print a two dimensional array in which all numbers are between 0 and 999. The normal way of outputting each number might leave the array misaligned. For instance:

54 4 12 366 512 756 192 18 27 4 14 18 99 300 18

Examine the documentation for the format method in the String class and write a routine that outputs the two-dimensional array in a nicer format, such as

54 4 12 366 512 756 192 18 27 4 14 18 99 300 18

3.26 Package java.math contains a class BigDecimal, used to represent an arbitrary-precision decimal number. Read the documentation for

BigDecimal and answer the following questions:

a. Is BigDecimal an immutable class?

b. Ifbd1.equals(bd2) is true, what is bd1.compareTo(bd2)? c. If bd1.compareTo(bd2) is 0, when is bd1.equals(bd2) false?

d. If bd1 represents 1.0 and bd2 represents 5.0, by default what is

bd1.divide(bd2)?

e. If bd1 represents 1.0 and bd2 represents 3.0, by default what is

bd1.divide(bd2) ?

f. What is MathContext.DECIMAL128?

g. Modify the BigRational class to store a MathContext that can be initialized from an additional BigRational constructor (or which defaults to MathContext.UNLIMITED). Then add a toBigDecimal

method to the BigRational class.

3.27 An Account class stores a current balance, and provides getBalance,

deposit, withdraw, and toString methods in addition to at least one constructor. Write and test an Accountclass. Make sure your withdraw method throws an exception if appropriate.

3.28 ABinaryArrayrepresents arbitrarily long sequences of binary variables.

The private data representation is an array of Boolean variables. For instance, the representation of the BinaryArray “TFTTF”would be an array of length five storing true, false, true, true, false in array indices 0, 1, 2, 3, and 4, respectively. The BinaryArrayclass has the following functionality:

n A one-parameter constructor that contains a String. Throw an IllegalArgumentException if there are illegal characters.

n AtoString method.

n Agetandsetmethod to access or change a variable at a particu-lar index.

n A size method that returns the number of binary variables in theBinaryArray.

Implement the BinaryArrayclass, placing it in a package of your choosing.

PROGRAMMING PROJECTS

3.29 Implement a simple Dateclass. You should be able to represent any date from January 1, 1800, to December 31, 2500; subtract two dates; incre-ment a date by a number of days; and compare two dates using both

equalsandcompareTo. A Dateis represented internally as the number of days since some starting time, which, here, is the start of 1800. This makes all methods except for construction and toString trivial.

The rule for leap years is a year is a leap year if it is divisible by 4 and not divisible by 100 unless it is also divisible by 400. Thus 1800, 1900, and 2100 are not leap years, but 2000 is. The constructor must check the validity of the date, as must toString. The Datecould be bad if an incre-ment or subtraction operator caused it to go out of range.

Once you have decided on the specifications, you can do an imple-mentation. The difficult part is converting between the internal and exter-nal representations of a date. What follows is a possible algorithm.

Set up two arrays that are static fields. The first array, daysTillFirst-OfMonth, will contain the number of days until the first of each month in a nonleap year. Thus it contains 0, 31, 59, 90, and so on. The second array, daysTillJan1, will contain the number of days until the first of each year, starting with firstYear. Thus it contains 0, 365, 730, 1095, 1460, 1826, and so on because 1800 is not a leap year, but 1804 is. You should have your program initialize this array once using a static initializer. You can then use the array to convert from the internal representation to the exter-nal representation.

3.30 APlayingCardrepresents a card used in games such as poker and black jack, and stores the suit value (hearts, diamonds, clubs, or spades) and the rank value (2 through 10, or jack, queen, king, or ace). A Deck rep-resents a complete 52-card collection of PlayingCards. A MultipleDeck

represents one or more Decksof cards (the exact number is specified in the constructor). Implement the three classes PlayingCard,Deck, and

MultipleDeck, providing reasonable functionality for PlayingCard, and for both DeckandMultipleDeck, minimally provide the ability to shuf-fle, deal a card, and check if there are remaining cards.

3.31 A complex number stores a real part and an imaginary part. Provide an implementation of a BigComplexclass, in which the data representa-tion is two BigDecimals representing the real and imaginary parts.

3.32 Sometimes a complex number is represented as a magnitude and an angle (in the half-open range of 0 to 360 degrees). Provide an implementation of a BigComplexclass, in which the data representation is one BigDecimal

representing the magnitude and a double representing the angle.

3.33 Implement a class, Polynomial, to represent single-variable polynomi-als and write a test program. The functionality of the Polynomialclass is as follows:

n Provide at least three constructors: a zero-parameter constructor that makes the polynomial zero, a constructor that makes a sepa-rate independent copy of an existing polynomial, and a construc-tor that creates a polynomial based on a Stringspecification. The last constructor can throw an exception if the Stringspecification is invalid, and you can make a design decision on what a valid specification is.

n negate returns the negative of this polynomial.

n add, subtract, and multiply return a new polynomial that is the sum, difference, or product, respectively, of this polynomial and another polynomial, rhs. None of these methods change either of the original polynomials.

n equals andtoStringfollow the standard contract for these func-tions. For toStringmake the String representation look as nice as you can.

n The polynomial is represented by two fields. One, degree,

n The polynomial is represented by two fields. One, degree,