ℹ️ This repository is part of my Refactoring catalog based on Fowler's book with the same title. Please see kaiosilveira/refactoring for more details.
Before | After |
---|---|
class Department {
get totalAnnualCost() { ... }
get name() { ... }
get headCount() { ... }
}
class Employee {
get annualCost() { ... }
get name() { ... }
get id() { ... }
} |
class Party {
get name() { ... }
get annualCost() { ... }
}
class Department extends Party {
get annualCost() { ... }
get headCount() { ... }
}
class Employee extends Party {
get id() { ... }
get annualCost() { ... }
} |
Duplication is probably not the root of all evil, but it accounts for a good portion of it. Often enough, we have classes doing the same thing in slightly different ways. This refactoring helps with bringing this behavior to a single place.
Our working example is a program that contains an Employee
and a Department
. These two classes do very similar things in slightly different ways. Our goal is to introduce a superclass so both of them can extend it.
Our test suite starts pretty simple and covers the basic behavior of each class (such as validating that it correctly calculates the annual costs). With that in place, we're safe to proceed.
PS: Take a look at the source code for the implementation.
We start by introducing Party
, the superclass. It starts empty, and we'll gradually move behavior into it:
+export class Party {}
We then make Department
extend Party
:
+export class Department extends Party {
constructor(name, staff) {
+ super();
this._name = name;
this._staff = staff;
}
and also Employee
:
+export class Employee extends Party {
constructor(name, id, monthlyCost) {
+ super();
this._name = name;
this._id = id;
this._monthlyCost = monthlyCost;
Then, we can start moving behavior. First, we pull up the name
field to Party
:
diff --git Party
-export class Party {}
+export class Party {
+ constructor(name) {
+ this._name = name;
+ }
+}
diff --git Department...
export class Department extends Party {
constructor(name, staff) {
- super();
- this._name = name;
+ super(name);
this._staff = staff;
}
diff --git Employee...
export class Employee extends Party {
constructor(name, id, monthlyCost) {
- super();
- this._name = name;
+ super(name);
this._id = id;
this._monthlyCost = monthlyCost;
}
And then, we do the same for the name
getter:
diff --git Party
export class Party {
+ get name() {
+ return this._name;
+ }
}
diff --git Department...
export class Department extends Party {
- get name() {
- return this._name;
- }
diff --git Employee...
export class Employee extends Party {
- get name() {
- return this._name;
- }
Finally, we notice that the calculation of annual costs, although particular to each subclass, can be implemented as a template method at Party
. To accomplish that, we first need to rename totalMonthlyCost
to monthlyCost
at Department
:
export class Department extends Party {
return this._staff.slice();
}
- get totalMonthlyCost() {
+ get monthlyCost() {
...
get totalAnnualCost() {
- return this.totalMonthlyCost * 12;
+ return this.monthlyCost * 12;
}
}
and also rename totalAnnualCost
to annualCost
:
export class Department extends Party {
- get totalAnnualCost() {
+ get annualCost() {
return this.monthlyCost * 12;
}
}
Finally, we can pull up the annualCost
getter to Party
:
diff --git Party
export class Party {
+ get annualCost() {
+ return this.monthlyCost * 12;
+ }
}
diff --git Department...
export class Department extends Party {
- get annualCost() {
- return this.monthlyCost * 12;
- }
}
diff --git Employee...
export class Employee extends Party {
get id() {
return this._id;
}
-
- get annualCost() {
- return this._monthlyCost * 12;
- }
}
And that's it!
Below there's the commit history for the steps detailed above.
Commit SHA | Message |
---|---|
c93eceb | introduce Party class |
5ffb7ef | make Department extend Party |
c56e8ac | make Employee extend Party |
b650705 | pull up name field to Party |
907a85d | pull up name getter to Party |
1fa6909 | rename totalMonthlyCost to monthlyCost at Department |
07933a8 | rename totalAnnualCost to annualCost at Department |
5f107c1 | pull up annualCost getter to Party |
For the full commit history for this project, check the Commit History tab.