Refactoring Techinques 1 – Preserve Whole Object

Often, when invoking subroutines or helper methods, we need to pass some of the data of the calling method to the subroutine. In many cases, this data is a subset of the data we are working on (For example, calculateDistanceTravelled() as applied to a Car object doesn’t need to know its colour, but may need to know its engine size, top speed and acceleration).

In these situations, we often pass the data subset as a list of parameters. There are some differing opinions as to whether this is a good thing ( See Don’t look for things )

Often, in cases where we are passing several related parameters, their source is the same object. In this situation, why not just pass the object itself?

For example:

public void calculateFuelCost( Journey journey, Car car ) {
  int journeyTime = journey.getTime( );
  int averageSpeed = journey.getAverageSpeed( )
  int distance = calculateDistanceTravelled( journeyTime, averageSpeed );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( int journeyTime, int averageSpeed ) {
  return journeyTime * averageSpeed;
}

Becomes:

public void calculateFuelCost( Journey journey, Car car ) {
  int distance = calculateDistanceTravelled( journey );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( Journey journey ) {
  return journey.getTime( ) * journey.getAverageSpeed( );
}

The advantages to this are that, if we require the method to use any additional information (for instance, a more detailed acccount of speeds), the method signature is much less likely to change (as the data is contained within the Journey object) and the code is therefore easier to maintain.

In order to alter our code in a safe, incremental fashion, a process exists called “Preserve Whole Object”. This process works as follows:

  • Add a parameter for the Data Object to the method signature
  • Run the unit tests

Then repeat the following steps for each parameter you wish to remove:

  • Replace a parameter with the equivalent method in the data object
  • Run the unit tests
  • Delete the parameter
  • Run the unit tests

Finally:

  • Remove any code in the calling method to obtain the parameters that is no longer used
  • Run the unit tests

For safety, there’s an awful lot of testing going on here. This is called “Shortening the feedback loop” and it leads to lower maintenance overheads and higher confidence in the code you write.

Example

Create Journey Object parameter

public void calculateFuelCost( Journey journey, Car car ) {
  int journeyTime = journey.getTime( );
  int averageSpeed = journey.getAverageSpeed( );
  int distance = calculateDistanceTravelled( journey.getTime( ), journey.getAverageSpeed( ) );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( int journeyTime, int averageSpeed, Journey journey ) {
  return journeyTime * averageSpeed;
}

Replace journeyTime Parameter

public void calculateFuelCost( Journey journey, Car car ) {
  int journeyTime = journey.getTime( );
  int averageSpeed = journey.getAverageSpeed( );
  int distance = calculateDistanceTravelled( journeyTime, averageSpeed, journey );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( int journeyTime, int averageSpeed, Journey journey ) {
  return journey.getTime( ) * averageSpeed;
}

Delete journeyTime Parameter

public void calculateFuelCost( Journey journey, Car car ) {
  int journeyTime = journey.getTime( );
  int averageSpeed = journey.getAverageSpeed( );
  int distance = calculateDistanceTravelled( averageSpeed, journey );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( int averageSpeed, Journey journey ) {
  return journey.getTime( ) * averageSpeed;
}

Replace averageSpeed Parameter

public void calculateFuelCost( Journey journey, Car car ) {
  int journeyTime = journey.getTime( );
  int averageSpeed = journey.getAverageSpeed( );
  int distance = calculateDistanceTravelled( averageSpeed, journey );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( int averageSpeed, Journey journey ) {
  return journey.getTime( ) * journey.getAverageSpeed( );
}

Delete averageSpeed Parameter

public void calculateFuelCost( Journey journey, Car car ) {
  int journeyTime = journey.getTime( );
  int averageSpeed = journey.getAverageSpeed( );
  int distance = calculateDistanceTravelled( journey );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( Journey journey ) {
  return journey.getTime( ) * journey.getAverageSpeed( );
}

Remove redundant code

public void calculateFuelCost( Journey journey, Car car ) {
  int distance = calculateDistanceTravelled( journey );
  return car.CostPerKilometre( ) * distance;
}

public int calculateDistanceTravelled( Journey journey ) {
  return journey.getTime( ) * journey.getAverageSpeed( );
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s