Two or more functions all take the same data shape as their first parameter and operate on its fields — passive data being acted on from outside.
Functions that all act on the same data live alongside it as methods; calls become method calls on a domain object.
Before the refactoring
function baseCharge(reading) {return reading.kwh * reading.tariff.baseRate;}function taxableCharge(reading) {return baseCharge(reading) + reading.kwh * reading.tariff.taxRate;}
After the refactoring
class Reading {constructor({ kwh, tariff }) { this.kwh = kwh; this.tariff = tariff; }baseCharge() { return this.kwh * this.tariff.baseRate; }taxableCharge() { return this.baseCharge() + this.kwh * this.tariff.taxRate; }}
The data's invariants aren't enforced anywhere; logic that belongs together scatters across modules; new operations have to re-import the data shape and re-derive its rules.
Wrapping passive data in a class that nobody else uses adds ceremony — only combine when 2+ functions take the same data and would benefit from co-located behavior.
Encapsulation tightens; tests target the class; new operations land in one obvious place.
Wrapping passive data in a class that nobody else needs to be a class — adds construction ceremony at every entry point with no encapsulation gain.