Example code for weekly refactoring sessions.
Slides: https://bit.ly/39xupAQ
- Before doing the refactorings try to
clear the decks
and see how much you can improve the code only by renaming things. - In the
Introduce Parameter Object
example,doesNotContain
is a stylistic choice, feel free to extractcontains
as per the book.- Pick one style and stick with it, either verbose names or using
!contains
, rememberwhen in rome
.
- Pick one style and stick with it, either verbose names or using
- Create NumberRange class, move to
number-range.ts
:
class NumberRange {
constructor(
readonly min: number,
readonly max: number,
){}
}
- Introduce range alongside existing parameters for
readingsOutsideRange
:
const range = new NumberRange(operatingPlan.temperatureFloor, operatingPlan.temperatureCeiling)
const alerts = readingsOutsideRange(station, operatingPlan.temperatureFloor, operatingPlan.temperatureCeiling, range)
- Redirect usages of
min
andmax
torange.min
andrange.max
inreadingsOutsideRange
:
// from
function readingsOutsideRange(station: Station, min: number, max: number, range: NumberRange): Reading[] {
return station.readings.filter((r) => r.temp < min || r.temp > max)
}
// to
function readingsOutsideRange(station: Station, min: number, max: number, range: NumberRange): Reading[] {
return station.readings.filter((r) => r.temp < range.min || r.temp > range.max)
}
- Safe delete the unused
min
andmax
parameters:
const alerts = readingsOutsideRange(station, range)
function readingsOutsideRange(station: Station, range: NumberRange): Reading[] {
return station.readings.filter((r) => r.temp < range.min || r.temp > range.max)
}
- Extract method
doesNotContain
onr.temp < range.min || r.temp > range.max
inreadingsOutsideRange
:
function doesNotContain(r: Reading, range: NumberRange): boolean {
return r.temp < range.min || r.temp > range.max
}
export function readingsOutsideRange(station: Station, range: NumberRange): Reading[] {
return station.readings.filter((r) => doesNotContain(r, range))
}
- Extract parameter on both occurrences of
r.temp
indoesNotContain
:
function doesNotContain(r: Reading, range: NumberRange, number: number): boolean {
return number < range.min || number > range.max
}
export function readingsOutsideRange(station: Station, range: NumberRange): Reading[] {
return station.readings.filter((r) => doesNotContain(r, range, r.temp))
}
- Safe delete the unused
r
parameter:
function doesNotContain(range: NumberRange, number: number): boolean {
return number < range.min || number > range.max
}
export function readingsOutsideRange(station: Station, range: NumberRange): Reading[] {
return station.readings.filter((r) => doesNotContain(range, r.temp))
}
- Move
doesNotContain
tonumber-range.ts
:
export class NumberRange {
constructor(readonly min, readonly max) {}
}
export function doesNotContain(range: NumberRange, number: number): boolean {
return number < range.min || number > range.max
}
// In introduce-parameter-object.ts
// import { doesNotContain } from './number-range'
- Manually create method
doesNotContain(number: number): boolean
onNumberRange
and delegate todoesNotContain
:
export class NumberRange {
constructor(readonly min, readonly max) {}
doesNotContain(number: number): boolean {
return doesNotContain(this, number)
}
}
export function doesNotContain(range: NumberRange, number: number) {
return number < range.min || number > range.max
}
- Redirect call to
doesNotContain
inreadingsOutsideRange
torange.doesNotContain
:
function readingsOutsideRange(station: Station, range: NumberRange): Reading[] {
return station.readings.filter((r) => range.doesNotContain(r.temp))
}
- Inline
doesNotContain
inside ofnumber-range.ts
:
export class NumberRange {
constructor(readonly min, readonly max) {}
doesNotContain(number: number): boolean {
return number < this.min || number > this.max
}
}
- Remove unused import of
doesNotContain
inintroduce-parameter-object.ts
:
import { NumberRange } from './number-range'