• No se han encontrado resultados

Like functions, methods must sometimes distinguish different situations and compute results according to the given situation. How to Design Pro- gramsposes a simple example with the problem of computing the interest earned for a bank certificate of deposit:

. . . Develop a method that computes the yearly interest forcer- tificates of deposit(CD) for banks. The interest rate for a CD de- pends on the amount of deposited money. Currently, the bank pays 2% for amounts up to $5,000, 2.25% for amounts between $5,000 and $10,000, and 2.5% for everything beyond that. . . .

Since the problem statement contains descriptions of intervals, the problem analysis includes a graphical rendering of these intervals:

0 5000 10000

[

)[

)[

---

This picture is a good foundation for the construction of both the example step as well as the template step later.

First, however, we must represent the major problem information— bank certificates of deposit—in our chosen language, i.e., as a class:

//represent a certificate of deposit classCD{

String owner; intamount;//cents

CD(String owner,intamount){

this.owner=owner; this.amount=amount; }

}

In reality, an account would contain many fields representing the owner(s), tax information about the owner(s), account type, and many other details. For our little problem, we use just two fields: the owner’s name and the deposited amount.

Translating the intervals from the problem analysis into tests yields three “interior” examples:

check newCD("Kathy",250000).interest()expect5000.0

check newCD("Matthew",510000).interest()expect11475.0

check newCD("Shriram",1100000).interest()expect27500.0

For each example, we multiply the percentage points with the amount and divided by 100. (Why?) Add examples that determine howrateworks for borderline examples. If the problem statement isn’t clear to you, make up your mind for the two borderline cases and proceed.

Working through the examples clarifies that this method needs to dis- tinguish three situations. More precisely, the computations in the method body depend on the deposited amount of money. To express this kind of conditional computation, Java provides the so-calledIF-STATEMENT, which can distinguish two possibilities:

if(condition){ statement1}

else{

statement2}

As the notation suggests, an if-statement tests a condition—an expression that produces a boolean value—and selects one of two statements, depend- ing on whetherconditionevaluates totrueorfalse. The only statement you know so far is a return statement, so the simplest ifstatement looks like this:

if(condition){ returnexpression1;}

else{

returnexpression2;}

Of course, as their name suggestsifstatements are also statements, so re- placingstatement1orstatement2in the schematicifis legitimate:

if(condition){ returnexpression1;} else{ if(condition2){ returnexpression2;} else{ returnexpression3;} }

Here we replacedstatement2with the gray-shadedifstatement. The com- plete statement thus distinguishes three situation:

1. ifconditionholds, the computation proceeds withreturnexpression1;

2. if condition doesn’t hold but condition2 holds; then the computation proceeds withreturnexpression2;

3. and if neithercondition1norcondition2evaluates totrue, then the com- putation proceeds withreturnexpression3.

The analysis and the examples distinguish three situations so we do need twoifstatements as shown above:

inside ofCD:

//compute the interest rate forthisaccount doubleinterest(){

if(0<=this.amount &&this.amount<500000){ . . . this.owner. . . this.amount. . . }

else{if(500000<=this.amount &&this.amount<1000000){ . . . this.owner. . . this.amount. . . }

else{

. . . this.owner. . . this.amount. . . } }

}

From the first design recipe we know to usethis.amount; the tests come from the pictorial analysis of the problem.

The ownership data naturally plays no role for the computation of the interest rate. So finishing the definition from the template is easy:

inside ofCD:

//compute the interest rate forthisaccount doubleinterest(){

if(0<=this.amount &&this.amount<500000){

return2.00this.amount;}

else{if(500000<=this.amount &&this.amount<1000000){ return2.25this.amount;}

else{

return2.50this.amount;} }

}

Your task now is to formulate an examples class and the method examples as tests in the same class. When you are done you may also ponder the

ProfessorJ: Testing with doubles following challenge:

. . . The bank has decided that keeping track of fractional cents no longer makes any sense. They would like for interestto re- turn anint. . . .

Find the documentation for Java’s Mathclass and read up onMath.round. Then modify the design ofinterestappropriately, including the tests.

Let’s look at a second example. Suppose your manager asks you for some exploratory programming:

//represent a falling //star on a 100 x 100 canvas classStar{ intx=20; inty; intDELTA=5; Star(inty){ this.y=y; } }

. . . Develop a game based on the Grimms brothers’ fairy tale called “Star Thaler.” . . . Your first task is to simulate the movement of the falling stars. Experiment with a single star that is falling straight to the ground at a fixed number of pixels per time unit on a 100× 100 canvas. Once the star has landed on the ground, it doesn’t move anymore. . . .

For good measure, your manager has already designed a data represen- tation according to the design recipe. A falling star has a location, which means x and y coordinates, and it moves downward at some fixed rate. Hence the class has three fields. Thexcoordinate and the rate of descend

DELTAare always the same for now; theycoordinate increases14continu- ously.

Your task is to develop the methoddrop, which creates a new star at a different position. Following the design recipe you extract a concise pur- pose statement for the method from the problem statement:

inside ofStar:

//dropthisStar byDELTApixels, //unless it is on (or close) to the ground Star drop(){

. . . this.y. . . this.DELTA. . . }

Here the purpose statement just reformulates two sentences from the prob- lem statement. It naturally suggests that there are two distinct kinds of stars: one that is falling and one that has landed. This, in turn, means that we need at least two kinds of examples:

Star s=newStar(10) Star t=newStar(100)

checks.drop()expect newStar(15) checkt.drop()expect newStar(100)

The first example,s, represents a free-falling star in the middle of the can- vas. The second one, t, is a star on the ground; this kind of star doesn’t move anymore. Of course, the two examples also point out that we don’t know what happens when the star is close to the ground. Since the prob- lem statement seems to imply that stars just land on the ground and then stop, we should add one more test case that clarifies this behavior:

check newStar(98).drop()expect newStar(100)

That is, once the star isclose enough,dropjust creates a star that has landed. Using anif-statement, we can finish the template fordrop:

14Remember that on a computer, the origin of the Cartesian grid is in the upper left. Going to the right increases thexvalue, going down increases theyvalue.

//represent a falling star //on a 100 x 100 canvas classStar{ intx=20; inty; intDELTA=5; Star(inty){ this.y=y; }

//dropthisStar byDELTApixels,

//unless it is on or close to the ground

Star drop(){

if(this.y+this.DELTA>=100){ return newStar(100);}

else{

return newStar(this.y+this.DELTA);}

} } Star Stringkind intx = 20 inty intDELTA = 5 intdrop()

Figure 40: A falling star

inside ofStar:

//dropthisStar byDELTApixels, unless it is on or near the ground Star drop(){

if(this.y+this.DELTA>=100) . . .

else//the star is in the middle of the canvas . . .

}

Remember that if you can distinguish different intervals in a numeric type or different cases in another atomic type of data, a template distinguishes as many situations as there are sub-intervals (cases).

Now take a look at figure 40, which contains the full method definition fordrop. If the condition holds, the method returns a star at height100(the ground level); otherwise, it actually creates a star that has dropped by a few pixels.

A bit of reflection suggests that we could have easily decided to distin- guish three different situations:

0

t

97 100

inside ofStar:

//dropthisStar byDELTApixels, //unless it is on or near the ground Star drop(){

if(this.y+this.DELTA<97){ return. . . ;}

else{if(this.y+this.DELTA<100){ return. . . ;}

else{//thisstar has landed return. . . ;

} } }

On the left you see a number line with the appropriate sub-intervals, just like inHow to Design Programs. There are three: from 0 to 97 (exclusive); from 97 (inclusive) to 100 (exclusive); and 100. On the right, you see a template that distinguishes the three situations. It uses twoifstatements, one followed by another. In principle, you can chain together as many of them as are necessary; the details are explained in the next intermezzo.

Exercises

Exercise 10.5 Modify the Coffeeclass from figure 38 so thatcosttakes into account bulk discounts:

. . . Develop a program that computes the cost of selling bulk coffee at a specialty coffee seller from a receipt that includes the kind of coffee, the unit price, and the total amount (weight) sold. If the sale is for less than 5,000 pounds, there is no dis- count. For sales of 5,000 pounds to 20,000 pounds, the seller grants a discount of 10%. For sales of 20,000 pounds or more, the discount is 25%. . . .

Don’t forget to adapt the examples, too.

//represent information about an image classImage{

intwidth; // in pixels intheight; // in pixels String source;

Image(intwidth,intheight,String source){ this.width=width;

this.height=height; this.source=source; }

}

Design the methodsizeStringfor this class. It produces one of three strings, depending on the number of pixels in the image:

1. "small"for images with 10,000 pixels or fewer;

2. "medium"for images with between 10,001 and 1,000,000 pixels;

3. "large"for images that are even larger than that.

Remember that the number of pixels in an image is determined by the area of the image.

Exercise 10.7 Your physics professor would like to simulate an experiment involving bouncing balls. Design a class that represents a ball that is falling on a 10 x 100 canvas at a rate ofDELTA. That is, each time the clock ticks, the ball drops byDELTApixels.

When the ball reaches the bottom of the canvas, it bounces, i.e., it re- verses course and travels upwards again. The bounce is perfect, meaning the ball travels the full distance during the bounce. Put different, if the ball is far enough away from a wall, it just travelsDELTApixels. If it is too close for that, it drops by whatever pixels are left and then reverses course for the remaining number of pixels. As it reverses course, it continues to travel at the same speed.

Design the methodmove, which simulates one step in the movement of the ball.

As you design conditional methods, don’t forget the design recipe from How to Design Programsfor just this kind of function. If it is complex, draw a number line to understand all the intervals (cases, enumerated items). Pick examples from the interior of each interval and for all the borderline cases.

//a certificate of deposit

classCD{ String owner; intamount; // cents

CD(String owner,intamount){

this.owner=owner;

this.amount=amount;

}

//compute the interest rate (in %) forthisaccount

doublerate(){

if(0<=this.amount

&&this.amount<500000){ return2.00;}

else{if(500000<=this.amount

&&this.amount<1000000){ return2.25;}

else{

return2.50;}

} }

//compute the interest to be paid forthisaccount

doublepayInterest(){

return(this.rate()∗this.amount)/100;

} } CD Stringowner int amount int rate() intpayInterest()

Figure 41: A CD with interest payment

Documento similar