How to write complex computer programs

  • The key to developing complex programs is:

    • Divide and conquer

    • Abstraction of methods

  • Abstraction of a method:

    • Separating the function/purpose of a method from its implementation

      I.e.: you specify what a method must do/accomplish without worrying about how to do it (you figure this out later !)

  • The divide-and-conquer strategy, a.k.a. stepwise refinement:

      • You decompose a problem into smaller subproblems and write methods to solve each subproblem

  • Key to develop programs to solve problems:

    • Knowing what information is needed to solve the problem

Program to print a calendar

  • We will write a program that:

    • Reads in a year          and
    • Reads in a month
    • Then prints out the calendar for that month of that year

    Example:

    Enter year (e.g., 2001): 2023
    Enter month in number between 1 and 12: 2
    
    Output:
    
             February 2023
    -----------------------------
     Sun Mon Tue Wed Thu Fri Sat
                   1   2   3   4
       5   6   7   8   9  10  11
      12  13  14  15  16  17  18
      19  20  21  22  23  24  25
      26  27  28
      

Start constructing an high-level program from the description

  • A high-level program is a computer program with very little details

  • From the description, we know the program must perform these actions:

      public static void main(String[] args) 
      {
        Scanner input = new Scanner(System.in);
    
        // (1) Prompt the user to enter the year
        System.out.print("Enter year (e.g., 2001): ");
        int year = input.nextInt();
    
        // (2) Prompt the user to enter the month
        System.out.print("Enter month in number between 1 and 12: ");
        int month = input.nextInt();
    
        // (3) Print calendar for the month of the year
        printMonth(year, month);  // This method must print the calender 
                                  // for the monthof the year
      } 
    
      public static void printMonth(int year, int month) 
      {
         // We need to work this out....
      }
    

DEMO: demo/06-methods/08-stepwise-refinement/Demo.java

Flesh out the incomplete methods:    what does it take to print a calendar month ?

  • Printing the calendar of a month consists of printing out 2 parts:

             February 2023
    -----------------------------       <--- Month Title
     Sun Mon Tue Wed Thu Fri Sat
                   1   2   3   4
       5   6   7   8   9  10  11        <--- Month Body
      12  13  14  15  16  17  18
      19  20  21  22  23  24  25
      26  27  28

  • Therefore:

      public static void printMonth(int year, int month) 
      {
         printMonthTitle(year, month);  // Need to work this out
         printMonthBody(year, month);   // Need to work this out
      } 

    Note:   these newer incomplete methods are less complicated than the original one !

DEMO:   write dummy methods for printMonthTitle( ) and printMonthBody( )

Flesh out the incomplete methods:    what does it take to print a month title ?

  • How to print the month title:

    printMonthTitle(2023, 2 ):
                 February 2023   <-- need to translate month 2 to February
        -----------------------------     
         Sun Mon Tue Wed Thu Fri Sat
    
    printMonthTitle(2018, 5 ):
                 May 2018        <-- need to translate month 5 to May
        -----------------------------     
         Sun Mon Tue Wed Thu Fri Sat

  • Therefore: printMonthTitle( ) can be written as follows:

      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");
      } 

    We still need to flesh out getMonthName(month)

DEMO:   test functionality using a dummy getMonthName(month)

Flesh out the incomplete methods:    what does it take to translate month number to month name ?

  • We then flesh out the getMonthName( month ) method:

      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;
      }

DEMO:   test functionality using different year and month

Flesh out the incomplete methods:    what does it take to print a month body ?

  • Next, we flesh out the printMonthBody( ) method:

    printMonthBody(2023, 2):
    
                       1   2   3   4
           5   6   7   8   9  10  11
          12  13  14  15  16  17  18
          19  20  21  22  23  24  25
          26  27  28
    
    printMonthBody(2018, 5):
    
                   1   2   3   4   5
           6   7   8   9  10  11  12
          13  14  15  16  17  18  19
          20  21  22  23  24  25  26
          27  28  29  30  31
    

  • We need to figure out:

    • How to accomplish this task ?    ( Hint: what information do you need ?)

Flesh out the incomplete methods:    what does it take to print a month body ?

  • The information that we need in order to write printMonthBody(year, month) are:

     Days: 0   1   2   3   4   5   6
    
                       1   2   3   4   (1) startDay = 3
           5   6   7   8   9  10  11
          12  13  14  15  16  17  18  
          19  20  21  22  23  24  25  
          26  27  28                   (2) numberOfDaysInMonth = 28
    
                   1   2   3   4   5   (1) startDay = 2
           6   7   8   9  10  11  12
          13  14  15  16  17  18  19  
          20  21  22  23  24  25  26  
          27  28  29  30  31           (2) numberOfDaysInMonth = 31
    

  • Suppose we know the startDay and the numberOfDays:

    • How can we write the printMonthBody(year, month) method ?

Flesh out the incomplete methods:    what does it take to print a month body ?

  • How to print a month body:

    If:  (1) startDay = 3
         (2) numberOfDaysInMonth = 28
    
     Days: 0   1   2   3   4   5   6
    
                       1   2   3   4   (1) Print 3 "empty" days
           5   6   7   8   9  10  11   (2) Print days 1 - 28 
          12  13  14  15  16  17  18   
          19  20  21  22  23  24  25   Note: start a new line 
          26  27  28                         after printing 7 days                              
    
    
    If: (1) startDay = 2 (2) numberOfDaysInMonth = 31 Days: 0 1 2 3 4 5 6 1 2 3 4 5 (1) Print 2 "empty" days 6 7 8 9 10 11 12 (2) Print days 1 - 31 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Note: start a new line 27 28 29 30 31 after printing 7 days

Flesh out the incomplete methods:    what does it take to print a month body ?

  • The printMonthBody(year, month) method:

      public static void printMonthBody(int year, int month) 
      {
          int i, numDaysOnLine = 0;
        
          int startDay = getStartDay(year, month);
          int numberOfDaysInMonth = getNumberOfDaysInMonth(year, month);
    
          // Print startDay number of "empty" days
          for (i = 0; i < startDay; i++)
          {
             System.out.print("    ");  // Use 4 spaces for an "empty" day
             numDaysOnLine++; // Keep track of # days printed on current line 
          }
    
          for (i = 1; i <= numberOfDaysInMonth; i++) 
          {
             System.out.printf("%4d", i); // Print i in 4 spaces
             numDaysOnLine++; // Keep track of # days printed on current line
    
             if ( numDaysOnLine % 7 == 0 )// Use next line after 7 days
                System.out.println();
          }
          System.out.println();
      }

DEMO:   test functionality using dummy getStartDay( ) and getNumberOfDaysInMonth( )

Flesh out the incomplete methods:    how to find the number of days in a given month

  • How to find the # days in a month ( numberOfDaysOnMonth(year, month)):

      public static int getNumberOfDaysInMonth(int year, int month) 
      {
         int numDays = 0;
         switch (month)
         {
             case 1: numDays = 31;  break;
             case 2: numDays = isLeapYear(year) ? 29 : 28;  break;
             case 3: numDays = 31;  break;
             case 4: numDays = 30;  break;
             case 5: numDays = 31;  break;
             case 6: numDays = 30;  break;
             case 7: numDays = 31;  break;
             case 8: numDays = 31;  break;
             case 9: numDays = 30;  break;
             case 10: numDays = 31; break;
             case 11: numDays = 30; break;
             case 12: numDays = 31; break;
         }
         return numDays;
      }

Flesh out the incomplete methods:    how to determine if a year is a leap year

  • We have studied the logic to cheap for leap year previously:

      public static boolean isLeapYear(int year)
      {
         if ( year % 400 == 0 || (year % 4 == 0 && year % 100 != 0 ) )
            return true;
         else
            return false;
      }

DEMO:   test functionality using dummy getStartDay( )

Flesh out the incomplete methods:    how to find the start day of a month

  • We only need find the start day of a month:

     Days: 0   1   2   3   4   5   6
    
                       1   2   3   4      startDay = 3
           5   6   7   8   9  10  11
          12  13  14  15  16  17  18  
          19  20  21  22  23  24  25  
          26  27  28                
    

  • In order to find the start day of a month, we need the following information:

      • We must know what day a certain calendar day is...

        It does not matter which calendar day

  • The text book chose this day:

      January 1 1800  was a Wednesday
    

Flesh out the incomplete methods:    how to find the start day of a month

  • How to compute the startDay of a month known that Jan 1 1800 is a Wednesday ?

    1. Find the # days between Jan 1 1800 to Month 1 Year:

                 # days in the years       # days in the months
       Jan 1 1800  <--------------> Jan 1 year <------> Month 1 year
                           x                       y
      

    2. startDay = (x + y + 3 (= Wednesday)) % 7

    Example: getStartDay(2023, 5):

       
      Jan 1 1800 <----------------> Jan 1 2023 <-------------> May 1 2023
      (Wednesday)  # days between              # days between
                   Jan 1 1800 to               Jan 1 2023 to
    	       Jan 1 2023		   May 1 2023
                   = x                         = y
    
      totalNumberOfDays since Jan 1 1800 = x + y
    
      getStartDay(2023, 5) = (totalNumberOfDays + 3) % 7 
    

Flesh out the incomplete methods:    how to find the start day of a month

  • We can write getStartDay( ) as follows:

      public static int getStartDay(int year, int month) 
      {
          final int START_DAY_FOR_JAN_1_1800 = 3; // Jan 1 1800 is Wednesday
    
          // 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;
      }
    

  • The method getTotalNumberOfDays(month, year) will find the # days between:

       Jan 1 1800   ---   month 1, year
    

We will flesh out the getTotalNumberOfDays(year, month) next.

Flesh out the incomplete methods:    how to find the start day of a month

  • We find the total number of days since Jan 1, 1800 as follows:

      Calculation:
    
                # days in the years       # days in the months
      Jan 1 1800  <--------------> Jan 1 year <------> Month 1 year
                         x                       y
    
      public static int getTotalNumberOfDays(int year, int month) 
      {
          int total = 0;
    
          // Get the total # days from 1/1/1800 to 1/1/year
          for (int i = 1800; i < year; i++)
             if (isLeapYear(i))
                total = total + 366;  // Leap year
             else
                total = total + 365;  // Normal year
    
          // Get the total # days from Jan/1/year to month/1/year
          for (int i = 1; i < month; i++)
             total = total + getNumberOfDaysInMonth(year, i);
    
          return total;
      }
    

DEMO: demo/06-methods/08-stepwise-refinement/Calendar.java

Advantages of the divide-and-conquer technique

  • Divide-and-conquer breaks up a large problem into smaller more manageable subproblems.

    • Each subproblem can then be implemented using a method.

  • Individual methods can be tested on its own     (shown in BlueJ !)

  • Methods that you write during the divide-and-conquer/stepwise refinement can often be re-used in the problem solving process:

      • The isLeapYear( ) method was used in:

        • getNumberOfDaysInMonth(int year, int month)
        • getTotalNumberOfDays(int year, int month)

  • This way of write code will often result in clear and understandable programs

  • 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