Method Abstraction and Stepwise Refinement
Tags: java, programming, learning, beginners
Published: 2024-05-10 17:57:19

The key to developing software is to apply the concept of abstraction. _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 also 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 below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijwj5bcbvw2znjn904cl.png)

You have already used the **System.out.print** method to display a string 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, then displays the entire calendar for the month, as shown in the following sample run.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jswqmlahnxxftw2fm4az.png)

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

## 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, you 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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sy2suwueshidqupnrzww.png)

The structure chart shows that the **printCalendar** problem is divided into two subproblems, **readInput** and **printMonth** in (a), and that **printMonth** is divided into two smaller subproblems, **printMonthTitle** and **printMonthBody** in (b).

You can use **Scanner** to read input for 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 b above. The month title consists of three lines: month and year, a dashed 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**, as shown below in a:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94fsmcccb3ns3e31ldyb.png)

(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 b. For example, December 2013 has 31 days, and December 1, 2013, is a Sunday.

How would you get the start day for the first date in a month? There are several ways to do so. For now, we’ll use an alternative approach. Assume you know that the start day for January 1, 1800, was a Wednesday (**START_DAY_FOR_JAN_1_1800 = 3**). 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 + START_DAY_FOR_JAN_1_1800) % 7**, since every week has seven days. Thus, the **getStartDay** problem can be further refined as **getTotalNumberOfDays**, as shown in below in a:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tmmsik7ep05vl2x02nr.png)

(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. Thus, **getTotalNumberOfDays** can be further refined into two subproblems: **isLeapYear** and **getNumberOfDaysInMonth**, as shown in b. The complete structure chart is shown:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qurzc900rplql5bs4k4.png)

## Top-Down and/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 with 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” 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_—a simple but incomplete version of a method—can be used for the methods waiting to be implemented. The use of stubs enables you to quickly build the framework of the program. 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:

```
public class PrintCalendar {
 /** Main method */
public static void main(String[] args) {
 Scanner input = new Scanner(System.in);
 // Prompt the user to enter year
 System.out.print("Enter full year (e.g., 2012): ");
int year = input.nextInt();
 // Prompt the user to enter month
 System.out.print("Enter month as a number between 1 and 12: ");
int month = input.nextInt();
 // 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 getMonthBody may look like this */
public static void printMonthBody(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 getStartDay may look like this */
public static int getStartDay(int year, int month) {
return 1; // 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 getNumberOfDaysInMonth may look like this */
public static int getNumberOfDaysInMonth(int year, int month) {
return 31; // A dummy value
 }
 /** A stub for isLeapYear 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.

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, known as the _driver_, to test it. The top-down and bottom-up approaches are equally good: Both approaches implement methods incrementally, help to isolate programming errors, and make debugging easy. They can be used together.

## Implementation Details

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

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

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

- January, March, May, July, August, October, and December have 31 days.
- April, June, September, and November have 30 days.
- February has 28 days during a regular year and 29 days during a leap year. A regular year, therefore, has 365 days, a leap year 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.
The complete program is given:

```
package demo;
import java.util.Scanner;

public class PrintCalendar {

	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		// Prompt the user to enter year
		System.out.print("Enter full year (e.g., 2012): ");
		int year = input.nextInt();
		
		// Prompt the user to enter month
		System.out.print("Enter month as a number between 1 and 12: ");
		int month = input.nextInt();
		
		// Print calendar for the month of the year
		printMonth(year, month);
	}
	
	/** Print the calendar for a month in a year */
	public static void printMonth(int year, int month) {
		// Print the headings of the calendar
		printMonthTitle(year, month);
		
		// Print the body of the calendar
		printMonthBody(year, month);
	}
	
	/** Print the month title, e.g., March 2012 */
	public static void printMonthTitle(int year, int month) {
		System.out.println("         " + getMonthName(month) + " " + year);
		System.out.println("-----------------------------");
		System.out.println(" Sun Mon Tue Wed Thu Fri Sat");
	}
	
	/** Get the English name for the month */
	public static String getMonthName(int month) {
		String monthName = "";
		switch(month) {
		case 1: monthName = "January"; break;
		case 2: monthName = "February"; break;
		case 3: monthName = "March"; break;
		case 4: monthName = "April"; break;
		case 5: monthName = "May"; break;
		case 6: monthName = "June"; break;
		case 7: monthName = "July"; break;
		case 8: monthName = "August"; break;
		case 9: monthName = "September"; break;
		case 10: monthName = "October"; break;
		case 11: monthName = "November"; break;
		case 12: monthName = "December"; break;
		}
		
		return monthName;
	}
	
	/** Print month body */
	public static void printMonthBody(int year, int month) {
		// Get start day of the week for the first date in the month
		int startDay = getStartDay(year, month);
		
		// Get number of days in the month
		int numberOfDaysInMonth = getNumberOfDaysInMonth(year, month);
		
		// Pad space before the first day of the month
		int i = 0;
		for(i = 0; i < startDay; i++)
			System.out.print("    ");
		
		for(i = 1; i <= numberOfDaysInMonth; i++) {
			System.out.printf("%4d", i);
			
			if((i + startDay) % 7 == 0)
				System.out.println();
		}
		
		System.out.println();
	}
	
	/** Get the start day of month/1/year */
	public static int getStartDay(int year, int month) {
		final int START_DAY_FOR_JAN_1_1800 = 3;
		// Get total number of days from 1/1/1800 to month/1/year
		int totalNumberOfDays = getTotalNumberOfDays(year, month);
		
		// return the start day for month/1/year
		return(totalNumberOfDays + START_DAY_FOR_JAN_1_1800) % 7;
	}
	
	/** Get the total number of days since January 1, 1800 */
	public static int getTotalNumberOfDays(int year, int month) {
		int total = 0;
		
		// Get the total days from 1800 to 1/1/year
		for(int i = 1800; i < year; i++)
			if(isLeapYear(i))
				total = total + 366;
			else
				total = total + 365;
		
		// Add days from Jan to the month prior to the calendar month
		for(int i = 1; i < month; i++)
			total = total + getNumberOfDaysInMonth(year, i);
		
		return total;
	}
	
	/** Get the number of days in a month */
	public static int getNumberOfDaysInMonth(int year, int month) {
		if(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
			return 31;
		
		if(month == 4 || month == 6 || month == 9 || month == 11)
			return 30;
		
		if(month == 2) return isLeapYear(year) ? 29 : 28;
		
		return 0; // If month is incorrect
	}
	
	/** Determine if it is a leap year */
	public static boolean isLeapYear(int year) {
		return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
	}

}

```

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 displays 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 print months only after January **1800**, it could be modified to print months before **1800**.

## Benefits of Stepwise Refinement

Stepwise refinement breaks a large problem into smaller manageable subproblems. Each subproblem can be implemented using a method. This approach makes the program easier to write, reuse, debug, test, modify, and maintain.

**Simpler Program**

The print calendar program is long. Rather than writing a long sequence of statements in one method, stepwise refinement breaks it into smaller methods. This simplifies the program and makes the whole program easier to read and understand.

**Reusing Methods**

Stepwise refinement promotes code reuse within a program. The **isLeapYear** method is defined once and invoked from the **getTotalNumberOfDays** and **getNumberOfDayInMonth** methods. This reduces redundant code.

**Easier Developing, Debugging, and Testing**

Since each subproblem is solved in a method, a method can be developed, debugged, and tested individually. This isolates the errors and makes developing, debugging, and testing easier. When implementing a large program, use the top-down and/or bottom-up approach. Do not write the entire program at once. Using these approaches seems to take more development time (because you repeatedly compile and run the program), but it actually saves time and makes debugging easier.

**Better Facilitating Teamwork**

When a large problem is divided into subprograms, subproblems can be assigned to different programmers. This makes it easier for programmers to work in teams.