Refactoring techniqueSimplifying Method Calls
Replace Parameter with Explicit Methods
Split a method that branches on a flag or enum argument into one clearly named method per value.
- Also known as
- Remove Flag Argument
Problem
A method takes a parameter whose only job is to choose which branch runs. Call sites read like setStatus(account, true, false), and the reader must open the method to learn what true means. Each branch has little in common with the others.
Solution
Create a separate method for each value of the parameter, move the matching branch into it, and have callers call the explicit method.
Before
function exportReport(report: Report, format: "csv" | "pdf"): Buffer {
if (format === "csv") {
const header = report.columns.join(",");
const rows = report.rows.map((r) => r.map(csvEscape).join(","));
return Buffer.from([header, ...rows].join("\n"));
} else {
const doc = new PdfDocument({ size: "A4" });
doc.table(report.columns, report.rows);
return doc.render();
}
}
const file = exportReport(monthly, "pdf");After
function exportCsv(report: Report): Buffer {
const header = report.columns.join(",");
const rows = report.rows.map((r) => r.map(csvEscape).join(","));
return Buffer.from([header, ...rows].join("\n"));
}
function exportPdf(report: Report): Buffer {
const doc = new PdfDocument({ size: "A4" });
doc.table(report.columns, report.rows);
return doc.render();
}
const file = exportPdf(monthly);Why
- Each call states what it does without a flag to decode.
- Each method has one job and only the dependencies that job needs.
- The type checker catches a missing case at compile time, rather than a runtime
else.
Steps
- Confirm callers pass the parameter as a literal. If the value comes from data at runtime, stop; you would only move the branch to every caller.
- Create one explicit method per value, with the body of its branch. Run the tests.
- Make the original method delegate to the explicit methods. Run the tests.
- Replace each call that passes a literal with a call to the matching explicit method. Run the tests.
- Delete the original method when no callers remain.
Watch out
- Do not split when the value is decided at runtime (from a user setting, request header or database). Keep the dispatching method, or move the choice to a lookup map. That map is often the boundary where callers need a single entry point.
- Do not split when the branches share most of their code and differ by a value. That is the case for Parameterize Method, the inverse technique.
- Many values (ten export formats) make many methods. Consider Replace Conditional with Polymorphism with one exporter object per format.
Related
- Inverse: Parameterize Method.
- Treats Switch Statements driven by flag arguments and shortens Long Method.
- Escalate to Replace Conditional with Polymorphism as cases grow.
Agent checklist
- Most callers pass the parameter as a literal.
- The branches do different work, not the same work with different values.
- Any caller with a runtime value still has a single dispatch point.
- Boolean flag arguments were the first candidates.
Treats these smells
See also
- Parameterize MethodMerge several methods that do the same thing with different literal values into one method that takes the value as a parameter.
- Replace Conditional with PolymorphismReplace a switch on an object's type or kind with one class per case that overrides a shared method, so new cases add code instead of editing it.
- Rename MethodChange a method's name to say what it does, using a forwarding alias when callers cannot all be updated at once.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.