Skip to content
aipatterns.dev

Refactoring techniqueSimplifying Method Calls

Preserve Whole Object

Pass the object itself instead of several values pulled out of it, so the method signature stays stable as needs change.

Problem

A caller reads several fields from one object and passes them as separate arguments. When the method needs one more field, every call site changes. The repeated x.a, x.b, x.c pattern appears wherever the method is called.

Solution

Pass the whole object and let the method read the fields it needs.

Before

TypeScript
function isWithinBusinessHours(
  openHour: number,
  closeHour: number,
  timezone: string,
  at: Date,
): boolean {
  const hour = Number(
    new Intl.DateTimeFormat("en-US", { hour: "numeric", hourCycle: "h23", timeZone: timezone }).format(at),
  );
  return hour >= openHour && hour < closeHour;
}

if (isWithinBusinessHours(clinic.openHour, clinic.closeHour, clinic.timezone, slot.start)) {
  book(slot);
}

After

TypeScript
function isWithinBusinessHours(clinic: Clinic, at: Date): boolean {
  const hour = Number(
    new Intl.DateTimeFormat("en-US", { hour: "numeric", hourCycle: "h23", timeZone: clinic.timezone }).format(at),
  );
  return hour >= clinic.openHour && hour < clinic.closeHour;
}

if (isWithinBusinessHours(clinic, slot.start)) {
  book(slot);
}

Why

  • Shorter call sites with no repeated field extraction.
  • When the rule later needs clinic.holidays, only the method changes.
  • The method now shows it is about a clinic, which often hints it belongs on Clinic itself.

Steps

  1. Add a parameter for the whole object, alongside the existing ones. Run the tests.
  2. Inside the method, replace one extracted-value parameter with a read from the object. Run the tests. Repeat for each.
  3. Remove the now unused value parameters, and the code at call sites that extracted them. Run the tests.
  4. If the method only uses the object's data, consider Move Method to put it on the object's class.

Watch out

  • Do not pass the whole object when the method lives in a lower layer that should not depend on the object's type. A pricing library should not import your ORM entity. Pass values, or define a narrow interface (Pick<Clinic, "openHour" | "closeHour" | "timezone">).
  • Passing a mutable object hands the method write access to all of it. Use a readonly type if it must not change anything.
  • If callers sometimes build the values from different sources (not always from one object), the separate parameters are honest. Consider Introduce Parameter Object instead.
  • No named inverse exists, but splitting a whole object back into values is the right move when you need to remove a dependency.

Agent checklist

  • Every call site took the passed values from the same object.
  • The method's module may depend on that object's type, or you used a narrow structural type.
  • The method cannot accidentally mutate the object it receives.
  • Extraction code at call sites was removed.

Treats these smells

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.