Skip to content

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

License

Notifications You must be signed in to change notification settings

kaiosilveira/replace-superclass-with-delegate-refactoring

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 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.


Replace Superclass With Delegate

Formerly: Replace Inheritance with Delegation

Before After
class List { ... }
class Stack extends List { ... }
class Stack {
  constructor() {
    this._storage = new List();
  }
}

class List { ... }

Inheritance works so well that sometimes we make mistakes. Just because a class has some similarities with another, that doesn't mean that there's a true inheritance relationship between them. This refactoring helps with breaking these wrong links.

Working example

Our working example contains scrolls and a catalog with items. Our Scroll class inherits from CatalogItem, which doesn't represent reality, since several scrolls can point to the same catalog item. Our goal is to break down this inheritance link.

Test suite

Since we have many moving pieces, our test suite is quite complex. Please refer to the initial commit to check the full implementation.

Steps

We start by creating an instance of catalogItem to Scroll:

Scroll extends CatalogItem {
   constructor(id, title, tags, dateLastCleaned) {
+    this._catalogItem = new CatalogItem(id, title, tags);
   }

This is the first step to start delegating behavior to the reference and, later, break down the inheritance link.

On to the delegations, we start with id:

export class Scroll extends CatalogItem {
+  get id() {
+    return this._catalogItem.id;
+  }

Then, title:

export class Scroll extends CatalogItem {
+  get title() {
+    return this._catalogItem.title;
+  }

And, then, hasTag:

export class Scroll extends CatalogItem {
+  hasTag(aString) {
+    return this._catalogItem.hasTag(aString);
+  }

Now we can formally break the inheritance link:

-export class Scroll extends CatalogItem {
+export class Scroll {
   constructor(id, title, tags, dateLastCleaned) {
-    super(id, title, tags);
   }

The main refactoring is done, but we still need to fix the modeling error (i.e.: linking one scroll to one catalog item). We start by defining a different id to Scroll, so it stops using CatalogItem's:

 export class Scroll {
   constructor(id, title, tags, dateLastCleaned) {
-    this._catalogItem = new CatalogItem(id, title, tags);
+    this._id = id;
+    this._catalogItem = new CatalogItem(null, title, tags);
     this._lastCleaned = dateLastCleaned;
   }

   get id() {
-    return this._catalogItem.id;
+    return this._id;
   }

Then, as an intermediary step, we provide catalog data to Scroll:

-export function loadScrollsFrom(aDocument) {
+export function loadScrollsFrom(aDocument, catalog) {
   const scrolls = aDocument.map(
     record =>
       new Scroll(
         record.id,
         record.catalogData.title,
         record.catalogData.tags,
-        new ManagedDateTime(record.lastCleaned)
+        new ManagedDateTime(record.lastCleaned),
+        record.catalogData.id,
+        catalog
       )
   );

And now we can dynamically resolve catalogItem from catalog at Scroll:

 export class Scroll {
-  constructor(id, title, tags, dateLastCleaned) {
+  constructor(id, title, tags, dateLastCleaned, catalogID, catalog) {
     this._id = id;
-    this._catalogItem = new CatalogItem(null, title, tags);
+    this._catalogItem = catalog.get(catalogID);
     this._lastCleaned = dateLastCleaned;
   }

Finally, we can now remove the now unused title and tags from Scroll:

 export class Scroll {
-  constructor(id, title, tags, dateLastCleaned, catalogID, catalog) {
+  constructor(id, dateLastCleaned, catalogID, catalog) {
     this._id = id;
     this._catalogItem = catalog.get(catalogID);
     this._lastCleaned = dateLastCleaned;

And that's it! Scroll no longer inherits from CatalogItem.

Commit history

Below there's the commit history for the steps detailed above.

Commit SHA Message
1b8bf10 add instance of catalogItem to Scroll
66fe0cd delegate id to catalogItem at Scroll
e7e70d2 delegate title to CatalogItem at Scroll
d26ef79 delegate hasTag to catalogItem at Scroll
07a4ed6 remove inheritance of CatalogItem from Scroll
9c32850 set id as Scroll's id and no longer CatalogItem's
47d9762 provide catalogData.id and catalog to Scroll at loadScrollsFrom
c393cac dynamically resolve catalogItem from catalog at Scroll initialization time
502b47b remove title and tags from Scroll

For the full commit history for this project, check the Commit History tab.

About

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

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

 

Packages

No packages published