5.8. The Scope of Variables

 
[Page 146 ( continued )]

5.11. Method Abstraction and Stepwise Refinement

The key to developing software is to apply the concept of abstraction. You will learn many levels of abstraction from this book. Method abstraction is achieved by separating the use of a method from its implementation. The client can use a method without knowing how it is implemented. The details of the implementation are encapsulated in the method and hidden from the client who invokes the method. This is known as information hiding or encapsulation . If you decide to change the implementation, the client program will not be affected, provided that you do not change the method signature. The implementation of the method is hidden from the client in a "black box," as shown in Figure 5.11.


[Page 147]
Figure 5.11. The method body can be thought of as a black box that contains the detailed implementation for the method.


You have already used the System.out.print method to display a string, the JOptionPane.showInputDialog method to read a string from a dialog box, and the max method to find the maximum number. You know how to write the code to invoke these methods in your program, but as a user of these methods, you are not required to know how they are implemented.

The concept of method abstraction can be applied to the process of developing programs. When writing a large program, you can use the "divide and conquer" strategy, also known as stepwise refinement , to decompose it into subproblems. The subproblems can be further decomposed into smaller, more manageable problems.

Suppose you write a program that displays the calendar for a given month of the year. The program prompts the user to enter the year and the month, and then displays the entire calendar for the month, as shown in Figure 5.12.

Figure 5.12. After prompting the user to enter the year and the month, the program displays the calendar for that month.

Let us use this example to demonstrate the divide-and-conquer approach.


[Page 148]

5.11.1. Top-Down Design

How would you get started on such a program? Would you immediately start coding? Beginning programmers often start by trying to work out the solution to every detail. Although details are important in the final program, concern for detail in the early stages may block the problem-solving process. To make problem-solving flow as smoothly as possible, this example begins by using method abstraction to isolate details from design and only later implements the details.

For this example, the problem is first broken into two subproblems: get input from the user, and print the calendar for the month. At this stage, the creator of the program should be concerned with what the subproblems will achieve, not with how to get input and print the calendar for the month. You can draw a structure chart to help visualize the decomposition of the problem (see Figure 5.13(a)).

Figure 5.13. The structure chart shows that the printCalendar problem is divided into two subproblems, readInput and printMonth , and that printMonth is divided into two smaller subproblems, printMonthTitle and printMonthBody .

Use the JOptionPane.showInputDialog method to display input dialog boxes that prompt the user to enter the year and the month.

The problem of printing the calendar for a given month can be broken into two subproblems: print the month title, and print the month body, as shown in Figure 5.13(b). The month title consists of three lines: month and year, a dash line, and the names of the seven days of the week. You need to get the month name (e.g., January) from the numeric month (e.g., 1). This is accomplished in getMonthName (see Figure 5.14(a)).

Figure 5.14. (a) To printMonthTitle , you need getMonthName . (b) The printMonthBody problem is refined into several smaller problems.


In order to print the month body, you need to know which day of the week is the first day of the month ( getStartDay ) and how many days the month has ( getNumberOfDaysInMonth ), as shown in Figure 5.14(b). For example, December 2005 has thirty-one days, and the first of the month is Thursday, as shown in Figure 5.12.

How would you get the start day for the first date in a month? There are several ways to find the start day. The simplest approach is to use the Calendar class in §9.3, "The Calendar and GregorianCalendar Classes." For now, an alternative approach is used. Assume that you know that the start day ( startDay1800 = 3 ) for January 1, 1800 was Wednesday. You could compute the total number of days ( totalNumberOfDays ) between January 1, 1800 and the first date of the calendar month. The start day for the calendar month is (totalNumberOfDays + startDay1800) % 7 , since every week has seven days. So the getStartDay problem can be further refined as getTotalNumberOfDays , as shown in Figure 5.15(a).


[Page 149]
Figure 5.15. (a) To getStartDay , you need getTotalNumberOfDays . (b) The getTotalNumberOfDays problem is refined into two smaller problems.


To get the total number of days, you need to know whether the year is a leap year and the number of days in each month. So getTotalNumberOfDays is further refined into two subproblems: isLeapYear and getNumberOfDaysInMonth , as shown in Figure 5.15(b). The complete structure chart is shown in Figure 5.16.

Figure 5.16. The structure chart shows the hierarchical relationship of the subproblems in the program.


5.11.2. Top-Down or Bottom-Up Implementation

Now we turn our attention to implementation. In general, a subproblem corresponds to a method in the implementation, although some are so simple that this is unnecessary. You would need to decide which modules to implement as methods and which to combine in other methods. Decisions of this kind should be based on whether the overall program will be easier to read as a result of your choice. In this example, the subproblem readInput can be simply implemented in the main method.

You can use either a "top-down" approach or a "bottom-up" approach. The " top-down" approach implements one method in the structure chart at a time from the top to the bottom. Stubs can be used for the methods waiting to be implemented. A stub is a simple but incomplete version of a method. The use of stubs enables you to test invoking the method from a caller. Implement the main method first, and then use a stub for the printMonth method. For example, let printMonth display the year and the month in the stub. Thus, your program may begin like this:


[Page 150]
   public class   PrintCalendar {  /** Main method */    public static void   main(String[] args) {  // Prompt the user to enter year  String yearString = JOptionPane.showInputDialog(   "Enter full year (e.g., 2001):"   );  // Convert string into integer    int   year = Integer.parseInt(yearString);  // Prompt the user to enter month  String monthString = JOptionPane.showInputDialog(   "Enter month as number between 1 and 12:"   );  // Convert string into integer    int   month = Integer.parseInt(monthString);  // Print calendar for the month of the year   printMonth(year, month);  }  /** A stub for printMonth may look like this */     public static void   printMonth(   int   year,   int   month)  { System.out.print(month +   " "   + year); }  /** A stub for printMonthTitle may look like this */     public static void   printMonthTitle(   int   year,   int   month)  { }  /** A stub for getMonthName may look like this */     public static   String getMonthName(   int   month)  {   return "January"   ;  // a dummy value  }  /** A stub for getMonthNmae may look like this */     public static int   getStartDay(   int   year,   int   month)  {   return 1   ;  // a dummy value  }  /** A stub for getNumberOfDaysInMonth may look like this */     public static int   getNumberOfDaysInMonth(   int   year,   int   month)  {   return 31   ;  // a dummy value  }  /** A stub for getTotalNumberOfDays may look like this */     public static int   getTotalNumberOfDays(   int   year,   int   month)  {   return 10000   ;  // a dummy value  }  /** A stub for getTotalNumberOfDays may look like this */     public static boolean   isLeapYear(   int   year)  {   return true   ;  // a dummy value  } } 

Compile and test the program, and fix any errors. You can now implement the printMonth method. For methods invoked from the printMonth method, you can again use stubs.


[Page 151]

The bottom-up approach implements one method in the structure chart at a time from the bottom to the top. For each method implemented, write a test program to test it. The top-down and bottom-up approaches are both fine. Both approaches implement methods incrementally, help to isolate programming errors, and make debugging easy. Sometimes they can be used together.

5.11.3. Implementation Details

The isLeapYear(int year) method can be implemented using the following code:

   return   (year %   400   ==     (year %   4   ==     && year %   100   !=     )); 

Use the following facts to implement getTotalNumberOfDaysInMonth(int year, int month) :

  • January, March, May, July, August, October, and December have thirty-one days.

  • April, June, September, and November have thirty days.

  • February has twenty-eight days during a regular year and twenty-nine days during a leap year. A regular year, therefore, has 365 days, whereas a leap year has 366 days.

To implement getTotalNumberOfDays(int year, int month) , you need to compute the total number of days ( totalNumberOfDays ) between January 1, 1800 and the first day of the calendar month. You could find the total number of days between the year 1800 and the calendar year and then figure out the total number of days prior to the calendar month in the calendar year. The sum of these two totals is totalNumberOfDays .

To print a body, first pad some space before the start day and then print the lines for every week, as shown for December 2005 (see Figure 5.12).

The complete program is given in Listing 5.8.

Listing 5.8. PrintCalendar.java
(This item is displayed on pages 151 - 153 in the print version)
 1   import   javax.swing.JOptionPane; 2 3   public class   PrintCalendar { 4  /** Main method */  5   public static void   main(String[] args) { 6  // Prompt the user to enter year  7 String yearString = JOptionPane.showInputDialog( 8   "Enter full year (e.g., 2001):"   ); 9 10  // Convert string into integer  11   int   year = Integer.parseInt(yearString); 12 13  // Prompt the user to enter month  14 String monthString = JOptionPane.showInputDialog( 15   "Enter month in number between 1 and 12:"   ); 16 17  // Convert string into integer  18   int   month = Integer.parseInt(monthString); 19 20  // Print calendar for the month of the year  21  printMonth(year, month);  22 } 23 24  /** Print the calendar for a month in a year */  25    static void   printMonth(   int   year,   int   month)  { 26  // Print the headings of the calendar  27  printMonthTitle(year, month);  28 

[Page 152]
 29  // Print the body of the calendar  30  printMonthBody(year, month);  31 } 32 33  /** Print the month title, e.g., May, 1999 */  34    static void   printMonthTitle(   int   year,   int   month)  { 35 System.out.println(   " "   + getMonthName(month) 36 +   " "   + year); 37 System.out.println  (   " “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “ “"   ); 38 System.out.println(   " Sun Mon Tue Wed Thu Fri Sat"   ); 39 } 40 41  /** Get the English name for the month */  42    static   String getMonthName(   int   month)  { 43 String monthName =   null   ; 44   switch   (month) { 45   case   1: monthName =   "January"   ;   break   ; 46   case   2: monthName =   "February"   ;   break   ; 47   case   3: monthName =   "March"   ;   break   ; 48   case   4: monthName =   "April"   ;   break   ; 49   case   5: monthName =   "May"   ;   break   ; 50   case   6: monthName =   "June"   ;   break   ; 51   case   7: monthName =   "July"   ;   break   ; 52   case   8: monthName =   "August"   ;   break   ; 53   case   9: monthName =   "September"   ;   break   ; 54   case   10: monthName =   "October"   ;   break   ; 55   case   11: monthName =   "November"   ;   break   ; 56   case   12: monthName =   "December"   ; 57 } 58 59   return   monthName; 60 } 61 62  /** Print month body */  63    static void   printMonthBody(   int   year,   int   month)  { 64  // Get start day of the week for the first date in the month  65   int   startDay =  getStartDay(year, month)  ; 66 67  // Get number of days in the month  68   int   numberOfDaysInMonth =  getNumberOfDaysInMonth(year, month)  ; 69 70  // Pad space before the first day of the month  71   int   i =     ; 72   for   (i =     ; i < startDay; i++) 73 System.out.print(   " "   ); 74 75   for   (i =   1   ; i <= numberOfDaysInMonth; i++) { 76   if   (i <   10   ) 77 System.out.print(   " "   + i); 78   else   79 System.out.print(   " "   + i); 80 81   if   ((i + startDay) %   7   ==     ) 82 System.out.println(); 83 } 84 85 System.out.println(); 86 } 87 88  /** Get the start day of the first day in a month */  89    static int   getStartDay(   int   year,   int   month)  { 

[Page 153]
 90  // Get total number of days since 1/1/1800  91   int   startDay1800 =   3   ; 92   int   totalNumberOfDays = getTotalNumberOfDays(year, month); 93 94  // Return the start day  95   return   (totalNumberOfDays + startDay1800) %   7   ; 96 } 97 98  /** Get the total number of days since January 1, 1800 */  99    static int   getTotalNumberOfDays(   int   year,   int   month)  { 100   int   total =     ; 101 102  // Get the total days from 1800 to year - 1  103   for   (   int   i =   1800   ; i < year; i++) 104   if   (isLeapYear(i)) 105 total = total +   366   ; 106   else   107 total = total +   365   ; 108 109  // Add days from January to the month prior to the calendar month  110   for   (   int   i =   1   ; i < month; i++) 111 total = total + getNumberOfDaysInMonth(year, i); 112 113   return   total; 114 } 115 116  /** Get the number of days in a month */  117    static int   getNumberOfDaysInMonth(   int   year,   int   month)  { 118   if   (month ==   1   month ==   3   month ==   5   month ==   7   119 month ==   8   month ==   10   month ==   12   ) 120   return 31   ; 121 122   if   (month ==   4   month ==   6   month ==   9   month ==   11   ) 123   return 30   ; 124 125   if   (month ==   2   )   return   isLeapYear(year) ?   29   :   28   ; 126 127   return 0   ;  // If month is incorrect  128 } 129 130  /** Determine if it is a leap year */  131    static boolean   isLeapYear(   int   year)  { 132   return   year %   400   ==     (year %   4   ==     && year %   100   !=     ); 133 } 134 } 

The program does not validate user input. For instance, if the user enters either a month not in the range between 1 and 12 or a year before 1800 , the program would display an erroneous calendar. To avoid this error, add an if statement to check the input before printing the calendar.

This program prints calendars for a month but could easily be modified to print calendars for a whole year. Although it can only print months after January 1800, it could be modified to trace the day of a month before 1800 .

Note

Method abstraction modularizes programs in a neat, hierarchical manner. Programs written as collections of concise methods are easier to write, debug, maintain, and modify than would otherwise be the case. This writing style also promotes method reusability.



[Page 154]

Tip

When implementing a large program, use the top-down or bottom-up approach. Do not write the entire program at once. This approach seems to take more time for coding (because you are repeatedly compiling and running the program), but it actually saves time and makes debugging easier.


 


Introduction to Java Programming-Comprehensive Version
Introduction to Java Programming-Comprehensive Version (6th Edition)
ISBN: B000ONFLUM
EAN: N/A
Year: 2004
Pages: 503

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net