Skip to content

kaiosilveira/replace-parameter-with-query-refactoring

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 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 Parameter With Query

Formerly: Replace Parameter with Method

Before After
availableVacation(anEmployee, anEmployee.grade);

function availableVacation(anEmployee, grade) {
  // calculate vacation...
availableVacation(anEmployee);

function availableVacation(amEmployee) {
  const grade = anEmployee.grade;
  // calculate vacation...

Inverse of: Replace Query with Parameter

Data can take many shapes and form, and so does function signatures. Sometimes, though, we can easily derive values from a related top-level structure that we already have access to, therefore simplifying argument lists and reliefing callers of one more parameter. This refactoring helps with that.

Working example

Our working example is a simple program which calculates the final price of an Order, based on its item quantity, base price, and discount level. Order looks like this:

export class Order {
  constructor({ quantity, itemPrice }) {
    this.quantity = quantity;
    this.itemPrice = itemPrice;
  }

  get finalPrice() {
    const basePrice = this.quantity * this.itemPrice;
    let discountLevel;
    if (this.quantity > 100) discountLevel = 2;
    else discountLevel = 1;
    return this.discountedPrice(basePrice, discountLevel);
  }

  discountedPrice(basePrice, discountLevel) {
    switch (discountLevel) {
      case 1:
        return basePrice * 0.95;
      case 2:
        return basePrice * 0.9;
    }
  }
}

Our goal here is to get rid of the duplicated logic around discountLevel.

Test suite

The imlpemented test suite for this example covers the border of the conditional logic around discount levels:

describe('Order', () => {
  describe('finalprice', () => {
    it('should apply discount level 1 if quantity is lower than 100', () => {
      const order = new Order({ quantity: 10, itemPrice: 10 });
      expect(order.finalPrice).toBe(95);
    });

    it('should apply discount level 1 if quantity is equal 100', () => {
      const order = new Order({ quantity: 100, itemPrice: 10 });
      expect(order.finalPrice).toBe(950);
    });

    it('should apply discount level 2 if quantity is higher than 100', () => {
      const order = new Order({ quantity: 101, itemPrice: 10 });
      expect(order.finalPrice).toBe(909);
    });
  });
});

With that in place, we're good to go.

Steps

We started by introducing a discountLevel getter. This will strategically relief finalPrice of calculating its value:

+  get discountLevel() {
+    return this.quantity > 100 ? 2 : 1;
+  }

Then, we replace the derivation of discountLevel at finalPrice with a call to the new discountLevel getter, effectively removing the need to calculate this parameter and allowing us to remove all the temps related to it:

   get finalPrice() {
     const basePrice = this.quantity * this.itemPrice;
-    return this.discountedPrice(basePrice, discountLevel);
+    return this.discountedPrice(basePrice, this.discountLevel);
   }

Going even further, we now can call the discountLevel getter directly at discountedPrice, instead of receiving it as argument:

   discountedPrice(basePrice, discountLevel) {
-    switch (discountLevel) {
+    switch (this.discountLevel) {

And since the argument is now useless, we just remove it:

   get finalPrice() {
     const basePrice = this.quantity * this.itemPrice;
-    return this.discountedPrice(basePrice, this.discountLevel);
+    return this.discountedPrice(basePrice);
   }

-  discountedPrice(basePrice, discountLevel) {
+  discountedPrice(basePrice) {
     switch (this.discountLevel) {

And that's all for this one!

Commit history

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

Commit SHA Message
7fad9bc introduce discountLevel getter
025c71c replace derivation of discountLevel at finalPrice with a call to the new discountLevel getter
c5d4999 call discountLevel getter instead of receiving it as argument at discountedPrice
d490a84 remove discountLevel argument at discountedPrice

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

About

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

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project