Education digital-technologies year-9
Refactor with Functions
A follow-up lesson on turning repeated code into reusable functions inside the Pet Services project.
This page applies the functions idea directly to the existing project, showing how repeated logic can be broken into smaller reusable parts.
Project Navigation
LESSON • FUNCTIONS (REFRACTORING)
🐾 Refactoring the Pet Service Booking Program using functions
Today you will take the same working program and reorganise it into functions. The program should behave the exact same way. The only thing that changes is the structure.
✅ Today you will learn to…
• Identify “chunks” of code that belong in a function
• Write functions that return values to the main program
• Keep logic in one place (so rule changes are easy)
• Build a clean main program that reads like a plan
• Write functions that return values to the main program
• Keep logic in one place (so rule changes are easy)
• Build a clean main program that reads like a plan
🎯 Success criteria
Your program uses functions for validation, pricing, and age rules, and the main program becomes short and readable.
⚠️ One rule
No new features. No extra services. No extra fees. Same program behaviour — cleaner structure.
📌 Reminder: feature creep
Feature creep is when you keep adding extra features that were not part of the original plan.
Why it’s bad during planning
Your rules keep growing, so you can’t finish your decision tree properly. You end up guessing.
Why it’s bad during coding
You change rules mid-build and patch random fixes. The code becomes messy and hard to debug.
Today’s goal: same behaviour, cleaner structure.
1️⃣ What we are starting with (the original worked example)
This is the original “all-in-one” program. You are not changing what it does — you are reorganising it. You will use this as your reference for the rest of the lesson.
Original worked example code
# ------------------------------------------
# Pet Service Booking System
# ------------------------------------------
# This program collects dog information,
# calculates grooming cost based on size and service,
# applies age-based modifiers,
# and prints a clear booking summary.
# ------------------------------------------
# ---- Chunk A: Welcome and Instructions ----
print("Welcome to the Dog Grooming Booking System.")
print("This program is for DOGS only.")
print("Size categories: small (0–10kg), medium (10–25kg), large (25kg+).")
print("Services available: bath, haircut, full.")
print("--------------------------------------------")
# ---- Chunk B: Collect Basic Inputs ----
dog_name = input("Enter your dog's name: ")
dog_breed = input("Enter your dog's breed: ")
# ---- Chunk C: Validate Age Input Using a While Loop ----
# We keep asking until the user gives a sensible number.
while True:
dog_age_input = input("Enter your dog's age in years: ")
# Check if the input is a number
if dog_age_input.isdigit():
dog_age = int(dog_age_input)
# Age cannot be negative
if dog_age >= 0:
break
else:
print("Age cannot be negative. Try again.")
else:
print("Please enter a valid whole number for age.")
# ---- Validate Dog Size ----
# This while loop ensures size is one of the allowed options.
valid_sizes = ["small", "medium", "large"]
while True:
dog_size = input("Enter dog size (small / medium / large): ").lower()
if dog_size in valid_sizes:
break
else:
print("Invalid size. Please type small, medium, or large.")
# ---- Display Services Using a For Loop ----
# Instead of writing 3 separate print statements,
# we loop through a list.
services = ["bath", "haircut", "full"]
print("\nAvailable services:")
for service_option in services:
print("-", service_option)
# ---- Validate Service Choice ----
while True:
service = input("Choose a service: ").lower()
if service in services:
break
else:
print("Invalid service. Please choose from the list above.")
# ---- Chunk D: Calculate Base Cost ----
# Base cost depends on BOTH service and size.
if service == "bath":
if dog_size == "small":
base_cost = 20
elif dog_size == "medium":
base_cost = 25
else:
base_cost = 30
elif service == "haircut":
if dog_size == "small":
base_cost = 30
elif dog_size == "medium":
base_cost = 35
else:
base_cost = 40
else: # full grooming
if dog_size == "small":
base_cost = 45
elif dog_size == "medium":
base_cost = 50
else:
base_cost = 60
# ---- Chunk E: Apply Age Modifiers ----
total_cost = base_cost # Start with base cost
# Puppy discount
if dog_age < 1:
total_cost = total_cost * 0.9 # Reduce by 10%
# Senior dog fee
if dog_age >= 10:
total_cost = total_cost + 5
# ---- Chunk F: Output Booking Summary ----
# We store summary lines in a list and print them using a for loop.
summary_lines = [
"\n----- Booking Summary -----",
f"Dog name: {dog_name}",
f"Breed: {dog_breed}",
f"Age: {dog_age} years",
f"Size category: {dog_size}",
f"Service selected: {service}",
f"Base cost: ${base_cost:.2f}",
f"Final total cost: ${total_cost:.2f}"
]
for line in summary_lines:
print(line)
print("----------------------------")
2️⃣ What we are building today (same program, cleaner structure)
The idea
Take repeated or “chunk” logic and move it into functions, so your main program becomes short and easy to read.
You are not changing the code, just how the code is organised.
Target functions
•
•
•
•
•
•
get_valid_age()•
get_valid_size(valid_sizes)•
get_valid_service(services)•
calculate_base_cost(service, dog_size)•
apply_age_based_fee(base_cost, dog_age)•
print_booking_summary(...)3️⃣ Refactor steps (do these in order)
This is the safe way to refactor: move one chunk at a time, test it, then move the next chunk. If you try to do everything at once, you won’t know what broke.
Step A — Worked example:
apply_age_based_fee(base_cost, dog_age)This one is fully worked. Read it, then use it as your model for the next steps.
This function keeps all age rules in one place, which means if the rules change later, you change one function instead of changing code all over your program.
This function keeps all age rules in one place, which means if the rules change later, you change one function instead of changing code all over your program.
def apply_age_based_fee(base_cost, dog_age):
total = base_cost
if dog_age < 1:
total = total * 0.9
if dog_age >= 10:
total = total + 5
return total
Checkpoint
You should be able to point to:
• the parameters (
• the variable that changes (
• the returned value (
base_cost, dog_age)• the variable that changes (
total)• the returned value (
return total)Step B — Your turn:
get_valid_age()Look at the original program and find the age validation chunk. Your job is to move that chunk into a function that returns an integer.
What your function must do: keep asking until the user enters a whole number that is 0 or more, then return it.
What your function must do: keep asking until the user enters a whole number that is 0 or more, then return it.
Starter template (you fill it in)
def get_valid_age():
# TODO: copy the age validation loop from the original code
# TODO: change the break into a return
# TODO: return the final integer age
Test it (prove it works)
Run these lines after your function:
age = get_valid_age()
print("Age captured:", age)
If it never prints, your function is probably never returning.
Hint
In the original code, the loop uses
In a function, you can finish the loop by using
break to escape the loop. In a function, you can finish the loop by using
return dog_age once you have a valid age.Show worked solution (check after you try)
def get_valid_age():
while True:
dog_age_input = input("Enter your dog's age in years: ")
if dog_age_input.isdigit():
dog_age = int(dog_age_input)
if dog_age >= 0:
return dog_age
else:
print("Age cannot be negative. Try again.")
else:
print("Please enter a valid whole number for age.")
Step C — Your turn:
get_valid_size(valid_sizes)Find the size validation loop in the original program and turn it into a function.
What your function must do: keep asking until the user types a value inside
What your function must do: keep asking until the user types a value inside
valid_sizes, then return it.Starter template (you fill it in)
def get_valid_size(valid_sizes):
# TODO: copy the size validation loop from the original code
# TODO: return the final valid size (as lowercase text)
Test it (prove it works)
valid_sizes = ["small", "medium", "large"]
size = get_valid_size(valid_sizes)
print("Size captured:", size)
Hint
Use
Then check:
dog_size = input(...).lower() so the program accepts SMALL, Small, small, etc. Then check:
if dog_size in valid_sizesShow worked solution (check after you try)
def get_valid_size(valid_sizes):
while True:
dog_size = input("Enter dog size (small / medium / large): ").lower()
if dog_size in valid_sizes:
return dog_size
else:
print("Invalid size. Please type small, medium, or large.")
Step D — Your turn:
get_valid_service(services)This function has two jobs that belong together:
• print the service menu using a for loop
• validate the user’s choice until it is inside
• validate the user’s choice until it is inside
servicesStarter template (you fill it in)
def get_valid_service(services):
# TODO: print the menu (for loop)
# TODO: validate the input (while loop)
# TODO: return the chosen service (lowercase)
Test it (prove it works)
services = ["bath", "haircut", "full"]
choice = get_valid_service(services)
print("Service captured:", choice)
Hint
In the original program, the for loop prints the menu first, then the while loop validates the input. Keep that same order inside your function.
Show worked solution (check after you try)
def get_valid_service(services):
print("\nAvailable services:")
for service_option in services:
print("-", service_option)
while True:
service = input("Choose a service: ").lower()
if service in services:
return service
else:
print("Invalid service. Please choose from the list above.")
Step E — Your turn:
calculate_base_cost(service, dog_size)Copy the pricing chunk from the original program into a function. Your function must return the correct base cost for every service + size combo.
Rule reminder: base cost depends on service + size.
Rule reminder: base cost depends on service + size.
Starter template (you fill it in)
def calculate_base_cost(service, dog_size):
# TODO: copy the pricing if/elif/else from the original code
# TODO: return the correct base cost (number)
Test it (prove it works)
These are quick spot-checks:
print(calculate_base_cost("bath", "small")) # expect 20
print(calculate_base_cost("haircut", "large")) # expect 40
print(calculate_base_cost("full", "medium")) # expect 50
Hint
You can keep the exact same if/elif/else shape from the original program. The only difference is you should return numbers instead of assigning
base_cost in the main program.Show worked solution (check after you try)
def calculate_base_cost(service, dog_size):
if service == "bath":
if dog_size == "small":
return 20
elif dog_size == "medium":
return 25
else:
return 30
elif service == "haircut":
if dog_size == "small":
return 30
elif dog_size == "medium":
return 35
else:
return 40
else: # full grooming
if dog_size == "small":
return 45
elif dog_size == "medium":
return 50
else:
return 60
Notice: The base cost depends on service + size. That’s why we collect size in the first place.
4️⃣ Build the new main program (this is the payoff)
After you create the functions, your main program becomes short and readable. It should feel like a plan written in Python.
Important: This main program will NOT work until you have written the functions above it. That is normal.
# ---- Main Program (clean + readable) ----
print("Welcome to the Dog Grooming Booking System.")
print("This program is for DOGS only.")
print("Size categories: small (0–10kg), medium (10–25kg), large (25kg+).")
print("Services available: bath, haircut, full.")
print("--------------------------------------------")
dog_name = input("Enter your dog's name: ")
dog_breed = input("Enter your dog's breed: ")
dog_age = get_valid_age()
valid_sizes = ["small", "medium", "large"]
dog_size = get_valid_size(valid_sizes)
services = ["bath", "haircut", "full"]
service = get_valid_service(services)
base_cost = calculate_base_cost(service, dog_size)
total_cost = apply_age_based_fee(base_cost, dog_age)
# Booking summary (we keep the same output format as the original)
summary_lines = [
"\n----- Booking Summary -----",
f"Dog name: {dog_name}",
f"Breed: {dog_breed}",
f"Age: {dog_age} years",
f"Size category: {dog_size}",
f"Service selected: {service}",
f"Base cost: ${base_cost:.2f}",
f"Final total cost: ${total_cost:.2f}"
]
for line in summary_lines:
print(line)
print("----------------------------")
Checkpoint: What changed and what did NOT change?
Did NOT change: the rules, the questions asked, the prices, the summary output.
Did change: the structure. Chunks of logic are now stored in functions and reused.
Did change: the structure. Chunks of logic are now stored in functions and reused.
5️⃣ Optional challenge:
print_booking_summary(...)This is optional (do it if you finish early). Right now, the main program builds
What your function must do: take the booking variables as parameters, build the list, then print it using a for loop.
summary_lines and prints them. Your challenge is to move that into a function. What your function must do: take the booking variables as parameters, build the list, then print it using a for loop.
def print_booking_summary(dog_name, dog_breed, dog_age, dog_size, service, base_cost, total_cost):
# TODO: build the list of summary lines (same as the original)
# TODO: print each line using a for loop
Show worked solution (check after you try)
def print_booking_summary(dog_name, dog_breed, dog_age, dog_size, service, base_cost, total_cost):
summary_lines = [
"\n----- Booking Summary -----",
f"Dog name: {dog_name}",
f"Breed: {dog_breed}",
f"Age: {dog_age} years",
f"Size category: {dog_size}",
f"Service selected: {service}",
f"Base cost: ${base_cost:.2f}",
f"Final total cost: ${total_cost:.2f}"
]
for line in summary_lines:
print(line)
print("----------------------------")
🛠 Troubleshooting (read this if you get stuck)
My program runs but the total cost is wrong
Check these in order:
• Did
• Did you call
• Are you using
calculate_base_cost() return the correct number for your service + size?• Did you call
apply_age_based_fee(base_cost, dog_age) with the right variables?• Are you using
total_cost in the summary, not base_cost?I get a NameError (function not defined)
That means Python cannot see your function. Fix this by checking:
• Your function is written above the main program
• Your spelling matches exactly (capital letters matter)
• You didn’t rename it in one place but not the other
• Your spelling matches exactly (capital letters matter)
• You didn’t rename it in one place but not the other
My program asks questions forever (loop won’t stop)
This usually happens when your validation function never returns. Look inside your
while True loop and find the line where it should return the valid answer.✅ Exit check (before you pack up)
Answer these clearly
1) What does “refactor” mean?
2) Why is
3) If the age rules changed, where would you edit the code?
2) Why is
apply_age_based_fee() a good function name?3) If the age rules changed, where would you edit the code?
Quick self-check
You can point to your program and show:
• where each function starts (
• what each function returns (
• that your main program is shorter and clearer than the original
def)• what each function returns (
return)• that your main program is shorter and clearer than the original
Clean plan → clean code. Clean functions → easy debugging.