Unit 06 · lesson
Refactor the System Monitor
Core path: 35 minutes
The Week 5 monitor works. That does not mean its structure is good.
This lesson keeps the visible behavior but changes the architecture. That distinction matters: refactoring changes how the code is organized without intentionally changing what the program does.
Start from the working monitor and save a copy or Git checkpoint before touching it. Future you will appreciate the backup.
Find the responsibilities hiding in the script
The monolithic version is doing several jobs at once:
- collecting device data;
- deciding status;
- counting warnings;
- calculating totals;
- displaying one device;
- displaying the final report.
Those jobs are candidates for functions because each one can be named, reasoned about, and tested separately.
Extract the decision logic first
def determine_status(battery, temperature):
if battery <= 10:
return "CRITICAL BATTERY"
elif temperature >= 80:
return "OVERHEATING"
return "NORMAL"
Test it before reconnecting it to the whole application:
print(determine_status(5, 40))
print(determine_status(80, 90))
print(determine_status(80, 40))
Expected:
CRITICAL BATTERY
OVERHEATING
NORMAL
That is one reason decomposition matters. You can inspect one piece without dragging the rest of the program into the test.
Separate input helpers
def get_device_name():
return input("Name: ")
def get_battery():
return int(input("Battery: "))
def get_temperature():
return float(input("Temperature: "))
These functions hide repetitive input/conversion details behind names that explain the job.
Separate report output
def display_device_report(name, status):
print(f"{name}: {status}")
def display_final_report(device_count, warning_count, average_battery):
print("=== FINAL REPORT ===")
print(f"Devices: {device_count}")
print(f"Warnings: {warning_count}")
print(f"Average battery: {average_battery:.1f}%")
Notice that these functions mostly display. determine_status() mostly decides.
That separation becomes useful later when we test code automatically.
Rebuild the main flow
The main program should now read more like a coordinator:
ask how many devices
repeat for each device
get device data
determine status
update totals
display device report
calculate average
display final report
Your exact function boundaries may differ. That is fine if you can explain why each function has one coherent responsibility.
Break return on purpose
Change:
return "NORMAL"
to:
print("NORMAL")
Then inspect the caller.
The text may still appear on screen, which makes this bug interesting. But the function no longer gives the caller the string value it expects. The default return value is None.
That is the difference between:
print → display something now
return → send a value back to the caller
Repair it and verify the report again.
Add one function of your own
Examples:
def battery_category(battery):
...
or:
def is_warning(status):
...
Before writing it, state the contract in plain language:
function name:
input(s):
returned value:
what it should NOT do:
Then implement it.
Prove the refactor preserved behavior
Run the same small test data through the old and new versions.
Compare:
- statuses;
- warning count;
- average battery;
- visible report output.
If the architecture improved but the outputs changed unexpectedly, you introduced a behavior change while refactoring. Investigate it.
The goal is not "more functions." The goal is code where the boundaries help another developer understand and test the system.