Refactoring: Composing Methods
Extract, inline, and name — the moves that turn long functions into short ones that read like their own documentation.
Big idea
Most refactoring happens inside functions. Composing methods means pulling meaningful chunks out into well-named functions (and occasionally folding pointless ones back in), so each function does one thing at one level of detail and its name tells you what that thing is.
The techniques
| Technique | Move | When |
|---|---|---|
| Extract Method | Turn a fragment into a function named for what it does | A block needs a comment to explain it; a function is too long |
| Inline Method | Replace calls with the body, delete the function | The body is as clear as the name; too much indirection |
| Extract Variable | Name a sub-expression | A complex condition or formula |
| Inline Temp | Replace a variable with its expression | The variable adds nothing and blocks other refactorings |
| Replace Temp with Query | Move a computed temp into a method | The value is needed in several methods |
| Split Temporary Variable | One variable per meaning | A variable is reassigned for different purposes |
| Remove Assignments to Parameters | Use a local instead | Reassigning a parameter confuses readers |
| Substitute Algorithm | Replace the body with a clearer algorithm | A simpler way exists (tests first!) |
Think of it like
Editing a long, rambling paragraph into a list of headed sections. The words barely change; you add headings (function names), split where the topic changes, and delete headings that just repeat their only sentence. Suddenly a reader can skim.
Before and after
def statement_before(customer, rentals):
out = f"Rentals for {customer}\n"
total, points = 0, 0
for title, days, kind in rentals:
amount = 2 + max(0, days - 2) * 1.5 if kind == "regular" else days * 3
points += 2 if kind == "new" and days > 1 else 1
out += f" {title:10} {amount:5.1f}\n"
total += amount
out += f"Owed {total:.1f}, earned {points} points"
return out
# --- after: same output, each piece named ---
def charge(days, kind):
return 2 + max(0, days - 2) * 1.5 if kind == "regular" else days * 3
def points_for(days, kind):
is_new_long_rental = kind == "new" and days > 1 # Extract Variable
return 2 if is_new_long_rental else 1
def statement(customer, rentals):
lines = [f" {title:10} {charge(d, k):5.1f}" for title, d, k in rentals]
total = sum(charge(d, k) for _, d, k in rentals) # Replace Temp with Query
points = sum(points_for(d, k) for _, d, k in rentals)
return "\n".join([f"Rentals for {customer}", *lines, f"Owed {total:.1f}, earned {points} points"])
rentals = [("Dune", 3, "regular"), ("Arrival", 2, "new"), ("Up", 1, "kids")]
print(statement(customer="Ana", rentals=rentals))
print("same as before:", statement("Ana", rentals) == statement_before("Ana", rentals))output
Rentals for Ana
Dune 3.5
Arrival 6.0
Up 3.0
Owed 12.5, earned 4 points
same as before: TrueRun and edit it in the app.
When extracting a method, what should its name describe?
Watch it
Hands on
A temporary variable is reused for two meanings, and a later edit reads the wrong one. Split the temp (one name per meaning) so the output is correct.
def box_report(w, h, d):
temp = 2 * (w * h + w * d + h * d) # surface area
print(f"surface area: {temp}")
temp = w * h * d # volume
print(f"volume: {temp}")
print(f"paint needed: {temp / 10:.1f} litres") # meant: surface area
box_report(2, 3, 4)output
surface area: 52
volume: 24
paint needed: 5.2 litresRun and edit it in the app.
When is Inline Method the right move?
How it relates
- The main cure for Long Method and for comment-as-smell (Dispensables).
- Extracted methods often turn out to belong elsewhere — the next step is Moving Features.
- Start from Refactoring Safely: tests green before and after each extract.
Key terms
- Extract Method — Moving a code fragment into its own named function.
- Inline Method — Replacing calls to a function with its body when the function adds nothing.
- Extract Variable — Naming a sub-expression to make its meaning clear.
- Split Temporary Variable — Giving each meaning of a reused variable its own name.