How To Design and Develop a Java Program

Some over your shoulder advice on how to start transforming a problem description into a design and then a program.

©2008 by Wayne Pollock, Tampa Florida USA.  All Rights Reserved.

 

(See also How to develop your first Java program.)

cartoon
(From www.abberley.co.uk/asap/)

One way to solve a problem is to break it down into simpler steps, simple enough to implement directly.  This technique was illustrated previously.  Another problem solving method is to simplify a task by looking for patterns.  Find the right way to look at a problem and the design becomes straight forward.  (By the same token, if you can't see how to implement some program, try to look at it differently.)  By generalizing you may come up with a simpler solution than if you had tried to solve the problem directly.

The Task:

Write a non-GUI program that produces a diamond as in the following output:

(not including the row and column numbers, shown for your convenience):

   12345678901234567890
     *
    ***
   *****
  *******
   *****
    ***
     *

You might quickly create a design similar to this:

Write the first line of output
Write the second line of output
Write the third line of output
Write the fourth line of output
Write the fifth line of output
Write the sixth line of output
Write the seventh line of output

This is hardly an elegant design!  Although it is simple and fast, it is very inflexible.  In the real world project requirements are often vague, ambiguous, and change throughout the life of the project.  A better design should be more flexible.  That is it should be general enough to accommodate the likely potential changes.

When designing a program, whatever you do it will be wrong. — Vonder Haar's Law of software design

In this case you should be asking yourself what might change in the requirements?  You might consider the customer saying the diamond is too big, the diamond is too small, or the diamond is just right, but needs to be moved a few more columns to the right.

Try to find the pattern here, a generalization of the problem that can easily be changed to accommodate the changes you can anticipate.  I think my program should be general enough to draw a diamond of any given size, and any given indent from the left margin.

It is also a mistake to make the requirements too general.  For example you might decide that the customer may want a different shape and try to design a program that can draw any shape.  Finding the right level of generalization requires software design and programming experience, as well as knowledge of the big picture, that is, the customer's line of business.

Hmm, This looks like a diamond, or a tilted square.  Is it easy to draw a diamond?  If so I can't think of how.  I should ask the instructor if there is a built-in Java method or class that will draw a diamond...  The answer is not for a non-GUI program.  Oh well.  Is there another way to look at this?  Well, the number of the stars seems to be increasing for each row, and then reducing at the same rate until the last line looks the same as the first one.  Also the indent from the left decreases as the rows get longer, and then decreases.

Outputting a certain number of blanks, then a certain number of stars, then start a new line and repeat.  Ah-ha!  This pattern sounds like something familiar.  I can do this with a bunch of for loops!

Let's see: the first row has four spaces and then one star.  The next row has three spaces (one less) and three stars (2 more).  The next row has two spaces (one less) and five stars (2 more).  The next row has one space (one less) and seven stars).  Then we draw the same lines, but in reverse.

So one pattern I can use to generalize is that I draw rows starting with 1 star up to 7 stars, and increase the number of stars by 2 each time.  And this is followed by another loop, starting with 5 stars and decreasing the number of stars each time by 2, and ending with 1 star.  I'll use numStars for the variable name:

for ( int numStars = 1; numStars <= 7; numStars += 2 )
   print correct number of leading spaces
   print numStars number of stars
   print a newline
for ( int numStars = 5; numStars >= 1; numStars -= 2 )
   print correct number of leading spaces
   print numStars number of stars
   print a newline

This program could be generalized if I used variables for the minimum number of stars, the maximum number of stars, and the amount of increase to the number of stars (commonly known as a STEP or INCREMENT value).  (To make a good-looking diamond the STEP should always be 2.  This really should be a constant!)  But how do we actually print the correct number of stars each time?  One way would be another loop (this would be a nested loop).

The harder part is to print the correct number of leading spaces, given a specific indent such as 4.  Actually printing a certain number of spaces can be done the same way as printing a certain number of stars, but how many spaces should be printed for each row?  In the requirements use case (the provided picture of the output) that starts as 4 spaces and decreases by 1 for each row until the middle row (with seven stars) is printed, then increases by one for each remaining row.  This sounds like I'll need another variable, let's call it numSpaces.  Let's try a design with another set of loops:

for ( int numStars = 1; numStars <= 7; numStars += 2 )
   for ( int numSpaces = 4; numSpaces >= 1; --numSpaces )
      print numSpaces number of leading spaces
   print numStars number of stars
   print a newline
for ( int numStars = 5; numStars >= 1; numStars -= 2 )
   for ( int numSpaces = 2; numSpaces <= 4; ++numSpaces )
      print numSpaces number of leading spaces
   print numStars number of stars
   print a newline

This so-called elegant solution is turning quite messy.  Look at all the for loops, and I didn't even show them all!  Nasty, messy code like this may mean I didn't get it right.  I'll try to simulate this code...

Oops!  This doesn't work at all!  For the first row numStars is 1.  But then the next for loop prints 4, then 3, then 2, then 1 space, a total of 10 spaces!  Clearly I have one too many for loops!  What I really want is a single loop that runs once for each row, setting both the numSpaces and numStars variables each pass through the loop.  Let's see if that works:

set numSpaces to 4;
for ( int numStars = 1; numStars <= 7; numStars += 2, --numSpaces )
   print numSpaces number of leading spaces
   print numStars number of stars
   print a newline
for ( int numStars = 5; numStars >= 1; numStars -= 2, ++numSpaces )
   print numSpaces number of leading spaces
   print numStars number of stars
   print a newline

This seems more elegant; numSpaces goes from 4, decreases by one each pass of the first loop, then increases each pass of the second loop.  Hey, I wonder if I can simplify the loops to deal with numStars that same way?  It would be nice to get rid of the magic number 5 in there>  The design becomes:

set numSpaces to 4, numStars to 1;
for ( ; numStars <= 7; numStars += 2, --numSpaces )
   print numSpaces number of leading spaces
   print numStars number of stars
   print a newline
for ( ; numStars >= 1; numStars -= 2, ++numSpaces )
   print numSpaces number of leading spaces
   print numStars number of stars
   print a newline

Not bad!  This should work for any diamond as long as I get the initial values for the variables right.  I will use constants INDENT (set to 4), MINIMUM (set to 1, for one star to start), MAXIMUM (set to 7), and STEP (set to 2).  This design not only draws the diamond required, but accommodates the potential changes we identified:  To change the size or indent, I need only change one constant!

The only question I have left is, should the code to print numSpaces spaces and numStars stars be for loops, and if so should they be separate methods?  Hmm, I'm not sure what is the best way to go.  The program is complex but not very long, so maybe a single method for everything will be good enough.  (Another good question for the instructor, before I spend a lot of time writing the code — I don't want to have to change the design later!)

The answer is, to look for more patterns.  (What a typical instructor answer! :-)  But I can see I use the same code, in two places each.  That's a clue to use methods.  But hold on a minute!  I see another pattern here.  All those statements to print a number of stars or spaces are doing the same thing except for the number and the character to show.  Maybe a single method to draw a run of some given character, of a some given length, can be used in all four cases?  Now that would be elegant!  Let's see...  Such a method will need two arguments, the character to draw and the number of times to draw it.  I'll call the method drawChar.

Should the method return a String for the main method to draw, or should it return nothing and do the drawing itself?  I suppose it would be more flexible for the future to have it return a String, but that seems like it would make the program even more complex so I won't bother for now.  (If the program needs changing, or stays useful long enough to have a version 2, I can consider the change then.)

The first draft of the code (with line numbers added):

// Non-GUI program to display a diamond pattern of stars.
// Written 2/2008 by Wayne Pollock, Tampa Florida USA.

class Diamond
{
   static final int MINIMUM = 1; // How many stars in first and last rows.
   static final int MAXIMUM = 7; // How many stars in middle row.
   static final int STEP = 2;    // How many stars to add to each row.
   static final int INDENT = 4;  // How many leading spaces for first row.

   public static void main ( String [] args )
   {
      int numSpaces = INDENT, numStars = MINIMUM;
      for ( ; numStars <= MAXIMUM; numStars += STEP, --numSpaces )
      {
         drawChar( ' ', numSpaces );  // Draw leading spaces.
         drawChar( '*', numStars );   // Draw the row of stars.
         System.out.println();  // add a newline.
      }
      for ( ; numStars >= MINIMUM; numStars -= STEP, ++numSpaces )
      {
         drawChar( ' ', numSpaces );  // Draw leading spaces.
         drawChar( '*', numStars );   // Draw the row of stars.
         System.out.println();  // add a newline.
      }
   }

   // Implementation method to display a "run" of some character:
   private static void drawChar ( char ch, int numToDraw )
   {
      for ( int i = 0; i < numToDraw; ++i )
        System.out.print( ch );  // Not println!
   }
}

And here's the output:

    *
   ***
  *****
 *******
*********
 *******
  *****
   ***
    *

Not bad!  I think I'll change INDENT to 6, so the output matches the requirements (two leading spaces on the middle row).  I could stop here, but the design with two for loops seems awkward and messy.  Could there be another way to look at this task, that results in a single loop?

The figure looks symmetrical both horizontally and vertically.  Is there a way to exploit that to come up with a simpler design?  Hmm.  (Many hours and false starts later:)  Suppose we think of the vertical center column to be the zero column, and you draw a line of stars centered at that column.  Then my loop goes from -3 to +3, and I just add that number to INDENT to determine the column of the first star on a line, or how many leading spaces to draw.  Also I add that number to MAXIMUM to determine how many stars to draw.  The two for loops then becomes a single, shorter loop:

...
int range = MAXIMUM / 2;
for ( int i = -range; i <= range; ++i )
{
   drawChar( ' ', INDENT + i );  // Draw leading spaces.
   drawChar( '*', MAXIMUM + i ); // Draw a row of stars.
   System.out.println();         // Add a newline.
}
...

The next draft looks like this:

// Non-GUI program to display a diamond pattern of stars.
// Written 2/2008 by Wayne Pollock, Tampa Florida USA.

class Diamond
{
   static final int MAXIMUM = 7; // How many stars in middle row.
   static final int INDENT = 6;  // How many leading spaces for first row.

   public static void main ( String [] args )
   {
      final int range = MAXIMUM / 2;
      for ( int i = -range; i <= range; ++i )
      {
         drawChar( ' ', INDENT + i );  // Draw leading spaces.
         drawChar( '*', MAXIMUM + i ); // Draw a row of stars.
         System.out.println();         // Add a newline.
      }
   }

   // Implementation method to display a "run" of some character:
   private static void drawChar ( char ch, int numToDraw )
   {
      for ( int i = 0; i < numToDraw; ++i )
        System.out.print( ch );  // Not println!
   }
}

And here's the output:

   ****
    *****
     ******
      *******
       ********
        *********
         **********

Well that's not right!  Perhaps I should have paid more attention in math class.  Let's see...  I think the answer is to use Math.abs method, and I think I added when I should have subtracted.  The number of stars in each row is also twice the correct value, I should divide by two.  And the number of leading spaces is completely wrong, it should be the index of the center column minus one-half the width of the line of stars.  After making these changes (plus renaming some variables), the new code becomes:

// Non-GUI program to display a diamond pattern of stars.
// Written 2/2008 by Wayne Pollock, Tampa Florida USA.

import static java.lang.Math.abs;

class Diamond
{
   static final int WIDTH = 7;  // How many stars in middle (fattest) row.
   static final int CENTER = 5; // Column index for vertical center axis.

   public static void main ( String [] args )
   {
      final int range = WIDTH / 2;
      for ( int i = -range; i <= range; ++i )
      {
         int numStars = WIDTH - abs(i*2);
         drawChar( ' ', CENTER - numStars/2 ); // Draw leading spaces.
         drawChar( '*', numStars );            // Draw a row of stars.
         System.out.println();                 // Add a newline.
      }
   }

   // Implementation method to display a "run" of some character:
   private static void drawChar ( char ch, int numToDraw )
   {
      for ( int i = 0; i < numToDraw; ++i )
        System.out.print( ch );  // Not println!
   }
}

And here's the output:

     *
    ***
   *****
  *******
   *****
    ***
     *

You can view/download Diamond.java to see the final version.  Whew!  This was hard.  Thank goodness I wasn't shy about asking the instructor for help!  (What do you suppose happens when WIDTH is an even number, say eight?  The first and last rows will have zero stars.  Not a problem for now, but we will need to run this design by the customer if the requirements for the size of the diamond changes.)

The code becomes even shorter if drawChar returns a String; the body of the loop becomes a single println statement (with the two drawChar method calls as arguments).  Now that's what I call elegant: simple, short, and flexible!  (The method drawChar can be replaced with standard Java methods which makes the code even shorter, but not simpler!  In general your design should not depend on clever hacks that are hard to read.)

Debugging:

So you've written your code carefully or copied it from a book, and compiled it without any error messages.  But it doesn't run, or it does run but does something you didn't intend or expect.  You've looked, but just can't see what is wrong with it.  You need to debug it.

See Patricia Shanahan's Debug Strategy for some good advice.