Ready – Aim – Fire – Finish

Example 1 – Contains
Comp 401

 

 

Our task is to write a method that searches an array, ‘x’, of integers . The method returns true if the array contains ‘value’. Otherwise it returns false.

 

1.  Ready

 

Before we can begin writing code, we must be clear about the data that is available to us and exactly what the method must do.  We specify the method header

 

            boolean contains( int value, int [ ] x )

 

At this point, we do not need to be concerned about Java details like static, public, etc.

 

Being precise about what the method must do can be surprisingly difficult. Our first attempt is not bad, for using English. Later in the course we will see how to use mathematical logic to be clearer and less ambiguous

 

     // Return true is there is an element of x equal to value

     boolean contains( int value, int [ ] x )

 

 

2.  Aim

 

How will we know when our code is correct? The Ready step explains to the programmer what the code must do, but we need Java code that we can execute to prove that our code works as expected.

 

First, we specify the test cases we will run.

 

Typical Case

                    x              value   return

            [ 1, 2, 3, 4 ]    2          T

 

Boundary Conditions

            [ 1, 2, 3, 4 ]    1          T

            [ 1, 2, 3, 4 ]    4          T

 

Next we would finish the Aim step by writing Java code for each of these test cases.  For example,

 

     void testContains( ) {

          int [ ] x = new int [ ] { 1, 2, 3, 4 };

          System.out.println( contains( 2, x ) );

     ...

     }

 

When testContains executes correctly, our code for the contains method will, by definition, be correct.

 

So far, so good. However, there is a big and glaring omission in our test cases.  After you write you test cases, consider them from the vantage point of an adversary: Could you write a  method that passes the test cases but does not do what was intended?  If so, revise your test cases. At least one needs to be added to our current three cases.

 

Can you supply the missing test cases(s)?

 

3.  Fire

 

Now it is time to write the code.

 

     // Return true is there is an element of x equal to value

     boolean contains( int value, int [ ] x ) {

          for( int index = 0; index < x.length; index++ ) {

              if( value == x[ index ] )

                   return true;

          }

          return false;

     }

 

Method testContains returns the expected results and our Java implementation of contains is correct.

 

4.  Finish

 

We are not yet done.  It is not enough that our code execute correctly.  It must also be clear, concise, and easy to understand. There is nothing wrong with the above implementation of contains, but we can do better.

 

Our loop potentially examines each element of the array x and the order in which we examine the elements is irrelevant.  We could have started at the end of the array and worked towards the first element.  This is an example of the iterator design pattern, and Java has a special form of loop, the for-each loop, for iterating.

 

     // Return true is there is an element of x equal to value

     boolean contains( int value, int [ ] x ) {

          for( int arrayValue : x ) {

              if( value == arrayValue )

                   return true;

          }

          return false;

     }

 

We rerun testContains to verify that our rewritten code is also correct.

 

What have we gained from this rewrite?  Our code is shorter and more direct. When the read sees the for-each loop, it is clear that we are iterating.  With the standard for loop that we started with, we might skip elements of the array or skip around in the array.  It would depend on how the value of index was modified.

 

Also, “off by one” errors are perhaps the most common bug we find. Is the index of the last element x.length or x.length-1 ?  Easy to go wrong here, but with the for-each loop, we leave such details to Java.

 

Rewriting code to make it clearer but without changing what is does is called refactoring.  The Finish step takes correct code and cleans it up to make it as easy to understand as possible.  See the link Handouts/JavaProgrammingStyleGuidelines for requirements for clear code.