Skip to main content

Section 12.7 Top Down Design

There are two main approaches to program design: top-down and bottom-up .
As we discuss each, we will consider how they might be used to design a program that calculates the number of days between two dates like 3/4/2023 and 4/5/2024. The program will ask the user to enter two dates, then calculate the number of days between them, and finally display the result. In these samples of the process, we will not worry about leap years just to reduce the complexity and allow us to focus more on the process than the mathematical details.
The first approach we will consider is top-down design. This method involves starting with the highest-level function, which outlines the overall structure of the program, and then breaking it down into smaller functions that handle specific tasks. Each function should be designed to perform a single task or a closely related set of tasks.
As new functions are identified, we should try to clearly specify what inputs they take and what outputs they produce. If we are fairly certain a given function can be implemented with a short amount of code, we can consider that part of the design finalized. That function will be a base-level function that we can implement directly and then use to build higher-level functions. If it seems like a given function is too complex to implement easily, we should stop and identify parts of the task that can be further broken up.
The strength of top-down design is that we start from the problem we want to solve and use that to identify the necessary building blocks. This reduces the chances that we design functions that are not actually necessary for the program.
However, top-down design works best when the programmer clearly understands the problem being solved and can easily identify the necessary steps to produce a solution. It can be difficult to use this approach effectively if the problem is not well understood or the high level task is so complex that each piece must be broken down many times before we get to something simple enough to consider a base-level function.

Example 12.7.1. A Top-Down Design.

A top-down design for our date program would start by identifying the high-level tasks:
  • β˜… Get user input for two dates.
  • β˜… Calculate the number of days between two dates. Inputs: two strings (like "3/4/2023" and "4/5/2024"); Output: an integer representing the number of days between the two dates. int daysBetween(string date1, string date2)

Note 12.7.2.

We will use β˜… to mark new functions at each step of the process. Each time you see a list of function ideas, look for that symbol to find the new functions. Anything without that symbol is a function we already identified in a previous step.
That sounds too complicated to implement in a few lines of code. So let’s break it down.
If we can convert each date into a number of days since the start of year 0000, we can then find the difference between those two numbers. So we will need a function that converts a date string into a number of days. That function will take a string like "3/4/2023" and return an integer representing the number of days since 0/0/0. Let’s call it dateToDays. The existing daysBetween function will use dateToDays to help do its job.
  • Get user input for two dates.
  • Calculate the number of days between two dates. Inputs: two strings (like "3/4/2023" and "4/5/2024"); Output: an integer representing the number of days between the two dates. int daysBetween(string date1, string date2)
    • β˜… Convert each date string into a number of days since 0/0/0. Inputs: a string like "3/4/2023"; Output: an integer representing the number of days since 0/0/0. int dateToDays(string date)
The dateToDays function is going to need to work with the month, day and year as numbers. So maybe we should write functions to get those parts of the string as integers:
  • Get user input for two dates.
  • Calculate the number of days between two dates. Inputs: two strings (like "3/4/2023" and "4/5/2024"); Output: an integer representing the number of days between the two dates. int daysBetween(string date1, string date2)
    • Convert each date string into a number of days since 0/0/0. Inputs: a string like "3/4/2023"; Output: an integer representing the number of days since 0/0/0. int dateToDays(string date)
      • β˜… Get the month as an integer. Inputs: a string like "3/4/2023"; Output: an integer representing the month (3 in this case). int getMonth(string date)
      • β˜… Get the day as an integer. Inputs: a string like "3/4/2023"; Output: an integer representing the day (4 in this case). int getDay(string date)
      • β˜… Get the year as an integer. Inputs: a string like "3/4/2023"; Output: an integer representing the year (2023 in this case). int getYear(string date)
Those all sound easy enough to implement, they don’t need any more refinement.
However, calculating the total number of days still sounds a bit complex. Working with the days and years should be easy. We know each year is 365 days (recall that we are cheating a little and ignoring leap years) and that each day is 1 day. But the months will complicate things.
What would make the job easier? Well, what if I could ask someone β€œhow many days are there before month X starts?”. If I could ask β€œhow many days were there before the start of March” and get the answer 59 (31 for Jan + 28 for Feb), it would be easy to do the rest of dateToDays.
So let’s add a function daysBeforeMonth that answers that question. Our design now looks like:
  • Get user input for two dates.
  • Calculate the number of days between two dates. Inputs: two strings (like "3/4/2023" and "4/5/2024"); Output: an integer representing the number of days between the two dates. int daysBetween(string date1, string date2)
    • Convert each date string into a number of days since 0/0/0. Inputs: a string like "3/4/2023"; Output: an integer representing the number of days since 0/0/0. int dateToDays(string date)
      • Get the month as an integer. Inputs: a string like "3/4/2023"; Output: an integer representing the month (3 in this case). int getMonth(string date)
      • Get the day as an integer. Inputs: a string like "3/4/2023"; Output: an integer representing the day (4 in this case). int getDay(string date)
      • Get the year as an integer. Inputs: a string like "3/4/2023"; Output: an integer representing the year (2023 in this case). int getYear(string date)
      • β˜… Get the number of days before the given month. Inputs: an integer month; Output: an integer representing the number of days before the given month started. int daysBeforeMonth(int month)
At this point, each of the listed functions seems simple enough to implement on its own. (The new function could just be a big if/else chain (if the month is 0, return 0; if the month is 1, return 31; if the month is 2, return 59; etc...). So now we can start implementation.

Insight 12.7.3.

While reading the steps, you might have noticed that this method depends on β€œseeing” the solution clearly before we get down to the details. We need to have a strong sense of how we are going to solve each part of the problem to identify the functions we need to build.
Faced with a new problem, you may have to try to solve it by hand first to get a better sense of the path you want to take before you start designing a solution.
You have attempted of activities on this page.