Skip to content

Working example with detailed commit history on the "extract superclass" refactoring based on Fowler's "Refactoring" book

License

Notifications You must be signed in to change notification settings

kaiosilveira/extract-superclass-refactoring

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Continuous Integration

ℹ️ This repository is part of my Refactoring catalog based on Fowler's book with the same title. Please see kaiosilveira/refactoring for more details.


Extract Superclass

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.

Working example

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.

Test suite

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.

Steps

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!

Commit history

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.

About

Working example with detailed commit history on the "extract superclass" refactoring based on Fowler's "Refactoring" book

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project