Why program design matters

Many beginning programmers jump straight into coding without planning. This approach works for trivial programs, but leads to problems as programs grow.

The "just start coding" approach problems:

  • Spaghetti code: Everything tangled together
  • Frequent rewrites: Realizing the structure doesn't work
  • Hard to debug: Can't isolate problems
  • Difficult to extend: Adding features breaks existing code
  • Collaboration problems: Others can't understand your code

Benefits of proper design:

  • Clear structure: Easy to understand and maintain
  • Modular code: Problems isolated to specific areas
  • Easier testing: Each component can be tested separately
  • Better collaboration: Team members can work on different parts
  • Efficient development: Less time spent fixing structural problems

The program design process

Here's a step-by-step approach for designing programs:

  1. Understand the problem
  2. Break it into smaller pieces
  3. Plan your functions
  4. Write code incrementally
  5. Test the program

Let's walk through each step with a simple example.

Example: Simple Grade Calculator

Let's design a simple program to calculate a student's final grade.

Step 1: Understand the problem

Initial requirement: "Calculate a student's final grade."

This is too vague! Let's ask clarifying questions:

  • What grades do we need? (tests, homework, final exam)
  • How are they weighted? (are some worth more than others)
  • What should we output? (percentage, letter grade, or both)

Refined requirement: A program that:

  • Takes 3 test scores (each worth 25%)
  • Takes 1 final exam score (worth 25%)
  • Calculates the final percentage
  • Shows the letter grade (A, B, C, D, or F)
Note: We could treat the final exam as a fourth test, but we'll keep it separate for demonstration.

Step 2: Break it into smaller pieces

Now we can identify the main parts:

Input needed:

  • 3 test scores
  • 1 final exam score

Processing needed:

  • Calculate weighted average
  • Determine letter grade from percentage

Output needed:

  • Final percentage
  • Letter grade

Step 3: Plan your functions

Based on our breakdown, we need these functions:

Input functions:

  • readTestScore() - Get one test score from user input
  • readFinalScore() - Get final exam score from user input

Processing functions:

  • calculateFinalGrade() - Calculate weighted average
  • getLetterGrade() - Convert percentage to letter grade

Output functions:

  • displayResults() - Show final grade and letter

Main program flow:

  1. Get the three test scores
  2. Get the final exam score
  3. Calculate the final percentage
  4. Determine the letter grade
  5. Display the results

Step 4: Write code incrementally

Don't try to write everything at once! Start with the most important parts:

Phase 1: Plan our functions and headers

First, let's create the header file with our function declarations:

grade_calculator.h

#pragma once

// Function declarations - what our program can do
double readTestScore(int testNumber);
double readFinalTestScore();
double calculateFinalGrade(double test1, double test2, double test3, double final);
char getLetterGrade(double percentage);
void displayResults(double finalGrade, char letterGrade);

Now let's implement these functions:

grade_calculator.cpp

#include "grade_calculator.h"
#include <iostream>

// Function to get a valid test score
double readTestScore(int testNumber)
{
    double score;
    std::cout << "Enter test " << testNumber << " score (0-100): ";
    std::cin >> score;

    return score;
}

// Function to get a valid final exam score
double readFinalTestScore()
{
    double score;
    std::cout << "Enter final exam score (0-100): ";
    std::cin >> score;

    return score;
}

// Function to calculate the final grade
double calculateFinalGrade(double test1, double test2, double test3, double final)
{
    // Each test is worth 25% of the total grade
    double testWeight = 0.25;

    return (test1 * testWeight)
         + (test2 * testWeight)
         + (test3 * testWeight)
         + (final * testWeight);
}

// Function to convert percentage to letter grade
char getLetterGrade(double percentage)
{
    // Notice early returns, only first match is returned.
    if (percentage >= 90.0) return 'A';
    if (percentage >= 80.0) return 'B';
    if (percentage >= 70.0) return 'C';
    if (percentage >= 60.0) return 'D';
    return 'F';
}

// Function to display the results
void displayResults(double finalGrade, char letterGrade)
{
    std::cout << "\n=== Final Grade Report ===" << std::endl;
    std::cout << "Final Grade: " << finalGrade << "%" << std::endl;
    std::cout << "Letter Grade: " << letterGrade << std::endl;

    if (letterGrade == 'F')
        std::cout << "Status: Not Passing" << std::endl;
    else
        std::cout << "Status: Passing" << std::endl;
}

Phase 2: Put it all together

Now let's create the main program:

main.cpp

#include "grade_calculator.h"
#include <iostream>

int main()
{
    std::cout << "=== Grade Calculator ===" << std::endl;
    std::cout << "This program calculates your final grade from 3 tests and 1 final exam." << std::endl;
    std::cout << "Each component is worth 25% of your final grade.\n" << std::endl;

    // Get all the scores
    double test1 = readTestScore(1);
    double test2 = readTestScore(2);
    double test3 = readTestScore(3);
    double final = readFinalTestScore();

    // Calculate final grade
    double finalGrade = calculateFinalGrade(test1, test2, test3, final);
    char letterGrade = getLetterGrade(finalGrade);

    // Display results
    displayResults(finalGrade, letterGrade);

    return 0;
}

Sample program run:

=== Grade Calculator ===
This program calculates your final grade from 3 tests and 1 final exam.
Each component is worth 25% of your final grade.

Enter test 1 score (0-100): 85
Enter test 2 score (0-100): 92
Enter test 3 score (0-100): 78
Enter final exam score (0-100): 88

=== Final Grade Report ===
Final Grade: 85.75%
Letter Grade: B
Status: Passing

Step 5: Test the program

Test your program with different inputs:

Test scenarios:

  • Try different valid scores (between 0-100)
  • Test different grade boundaries (89.9 vs 90.0)
  • Test edge cases (all 0s, all 100s)
Note: This version doesn't validate input (negative numbers, over 100, or text input that will cause problems).
Input validation is a more advanced topic we'll cover in later lessons.

Common improvements you could add:

  • Support for different weighting (not just 25% each)
  • More detailed grade breakdown
  • Option to calculate multiple students
  • Add input validation

Common program patterns

As you build more programs, you'll notice they often follow similar patterns:

Pattern 1: Calculators

  1. Get input from user
  2. Process the calculation
  3. Display the result

Pattern 2: Menu-driven programs

  1. Show menu options
  2. Get user choice
  3. Perform the chosen action
  4. Repeat until user exits

Pattern 3: Data processing

  1. Read or get data
  2. Process/analyze the data
  3. Output results or reports

Pattern 4: Simple games

  1. Setup the game
  2. Get player input
  3. Update game state
  4. Display current state
  5. Repeat until game ends

Summary

Program design is about thinking before coding:

The process:

  1. Understand the problem
  2. Break it into smaller pieces
  3. Plan your functions
  4. Write code step by step
  5. Test as you go

Key principles:

  • Break big problems into smaller functions
  • Each function should do one job well
  • Use clear, descriptive names
  • Build and test incrementally

Benefits of good design:

  • Code is easier to debug
  • Adding features is straightforward
  • Other people can understand your code
  • You spend less time fixing problems

The more you practice this approach, the easier programming becomes. Start with simple programs and gradually work on more complex projects as your skills improve.