Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring: Replace Method with Method Object explanation

I was looking for refactoring a too length method. Searching I found this technique: Replace Method with Method Object but I don't understand at all.

If the method to refactor is:

public class Order {
  //Method to refactor
  public double price() {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;

     //compute impl
  }

 //.....
}

With the web example the refactor Replace Method with Method Object would be as next:

public class Order {

  //Method refactored
  public double price() {
    return new PriceCalculator(this).compute();
  }
  //.......
}

//Method object
public class PriceCalculator {
  private double primaryBasePrice;
  private double secondaryBasePrice;
  private double tertiaryBasePrice;

  public PriceCalculator(Order order) {
    //??
  }

  public double compute() {
    // impl
  }
}

But how PriceCalculator gets primaryBasePrice, secondaryBasePrice, tertiaryBasePrice values to do the compute?

I only see it is possible passing the values in the constructor as next:

//Method object
public class PriceCalculator {
  private double primaryBasePrice;
  private double secondaryBasePrice;
  private double tertiaryBasePrice;

  public PriceCalculator(Order order, double primaryBasePrice, 
     double secondaryBasePrice, double tertiaryBasePrice) {
    this.primaryBasePrice = primaryBasePrice;
    this.secondaryBasePrice = secondaryBasePrice;
    this.tertiaryBasePrice = tertiaryBasePrice;
  }

  public double compute() {
    // impl
  }
}

Otherwise, why pass in the constructor order instance reference? Why is needed?

  • Passing instance of order:

    return new PriceCalculator(this, primaryBasePrice, secondaryBasePrice, tertiaryBasePrice).compute();

  • Without order instance reference:

    return new PriceCalculator(primaryBasePrice, secondaryBasePrice, tertiaryBasePrice).compute();

like image 325
Pau Avatar asked Feb 06 '23 04:02

Pau


2 Answers

But how PriceCalculator gets primaryBasePrice, secondaryBasePrice, tertiaryBasePrice values to do the compute?

The same way it was before refactoring.

If you look at the original code, primaryBasePrice, secondaryBasePrice and tertiaryBasePrice are local variables, whose values are being set somewhere in the //compute impl section.

  public double price() {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;

     // compute impl
     // all base price variables are assigned somewhere in here
  }

After refactoring, the compute() method has a copy of the //compute impl code, and simply assigns the exact same values to the fields in PriceCalculator as it did to the local variables before refactoring.

We need to pass in a reference to the Order object in case those values are dependent on its methods or internal state.

For instance, if we previously had

public double price() {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;

    // some of compute impl
    primaryBasePrice = getPrimary();
    secondaryBasePrice = getSecondary();
    tertiaryBasePrice = getTertiary();
    // the rest of compute impl
}

after refactoring, we would instead have something like

public double compute() {
    double primaryBasePrice;
    double secondaryBasePrice;
    double tertiaryBasePrice;

    // some of compute impl
    primaryBasePrice = order.getPrimary();
    secondaryBasePrice = order.getSecondary();
    tertiaryBasePrice = order.getTertiary();
    // the rest of compute impl
}
like image 192
azurefrog Avatar answered Mar 02 '23 18:03

azurefrog


So to answer both your questions:

  1. But how PriceCalculator gets primaryBasePrice, secondaryBasePrice, tertiaryBasePrice values to do the compute?

. Yes, you are right, you need to send them as constructor parameters.

  1. Otherwise, why pass in the constructor order instance reference? Why is needed?

You need to pass the order instance reference in order to be able to call other methods of Order. In the example you gave, it is a trivial method, but in some other methods you might be calling some of the Order's methods...

One problem with this refactoring technique is that if your original method accesses Order's private methods, then you'd have to either make them package protected or replicate those methods in the target Method Object...

And if you make those private methods as package protected, then you get tight coupling, feature envy and maybe other problems

like image 23
ACV Avatar answered Mar 02 '23 17:03

ACV