The UrBlog

John's Ramblings about Software Development

Using Spring as a Factory Method

One common strategy in object-oriented program is to use the dependency inversion principle to decouple high level code from the low level details. To demonstrate this, let’s use the movie example from Martin Fowler’s Refactoring book.



After some initial refactoring, we have a Movie class that can calculate it’s charge based on days rented:

// From book: 'Refactoring' by Martin Fowler
public class Movie {

   public static final int CHILDRENS = 2;
   public static final int NEW_RELEASE = 1;
   public static final int REGULAR = 0;

   double charge(int daysRented) {
      double result = 0;
      switch (getPriceCode()) {
    case Movie.REGULAR:
       result += 2;
       if (daysRented > 2)
          result += (daysRented - 2) * 1.5;
       break;
    case Movie.NEW_RELEASE:
       result += daysRented * 3;
       break;
    case Movie.CHILDRENS:
       result += 1.5;
       if (daysRented > 3)
          result += (daysRented - 3) * 1.5;
       break;
    }
    return result;
   }

}

Next we replace the switch statement with polymorphism using the state pattern:

// From book: 'Refactoring' by Martin Fowler
public class Movie {

   public static final int CHILDRENS = 2;
   public static final int NEW_RELEASE = 1;
   public static final int REGULAR = 0;

   public static Price newPrice(int priceCode) {
      switch (priceCode) {
         case REGULAR:
        return new RegularPrice();
     case NEW_RELEASE:
        return new NewReleasePrice();
     case CHILDRENS:
        return new ChildrensPrice();
     default:
        throw new IllegalArgumentException();
    }
   }

   double charge(int daysRented) {
      return newPrice(getPriceCode()).charge(daysRented);
   }

}

public interface Price {
   double charge(int daysRented);
}

public class RegularPrice implements Price {
   public double charge(int daysRented) {
      double result = 2;
      if (daysRented > 2)
         result += (daysRented - 2) * 1.5;
      return result;
   }
}

public class NewReleasePrice implements Price {
   public double charge(int daysRented) {
      return daysRented * 3;
   }
}

public class ChildrensPrice implements Price {
   public double charge(int daysRented) {
      double result = 1.5;
      if (daysRented > 3)
         result += (daysRented - 3) * 1.5;
      return result;
   }
}

This requires us to create some sort of factory method to decide which Price to create based on the Movie type (such as newPrice in the example). Usually this occurs in a static method with a switch statement on type. In a large program where Price is used multiple times, this is an advantage because we only have to change the one switch statement. But what if we could get rid of that switch statement also?

If you are using Spring, you can take advantage of the replace-method tag. You can pass it a class implementing MethodReplacer to replace a method in the class. That method could be an abstract factory method. Let’s modify our Movie class so that newPrice is an abstract method:

// From book: 'Refactoring' by Martin Fowler
public abstract class Movie {

   public static final int CHILDRENS = 2;
   public static final int NEW_RELEASE = 1;
   public static final int REGULAR = 0;

   public abstract Price newPrice(int priceCode);

   double charge(int daysRented) {
      return newPrice(getPriceCode()).charge(daysRented);
   }

}

Now we create a handy method replacer utility class that joins a prefix with the first argument to create a bean name and looks up the bean in the context:

public class BeanFactoryMethodReplacer implements MethodReplacer, ApplicationContextAware {

   private ApplicationContext context;

   private String prefix;

   public void setApplicationContext(ApplicationContext context) throws BeansException {
      this.context = context;
   }

   public void setPrefix(String prefix) {
      this.prefix = prefix;
   }

   public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
      String bean = (prefix != null) ? prefix : "";
      bean += args[0];
      return context.getBean(bean);
   }

}

Finally, we register the method replacer, the movie with the newPrice method replaced and beans for each Price. We use “price” as a prefix. The name of each price bean should be “price” + the number code for it’s type:

<bean id="movie" class="Movie">
   <replaced-method name="newPrice" replacer="factoryMethodReplacer">
      <arg-type>int</arg-type>
   </replaced-method>
</bean>

<bean id="factoryMethodReplacer" class="BeanFactoryMethodReplacer">
   <property name="prefix" value="price"/>
</bean>

<bean id="price0" class="RegularPrice"/>

<bean id="price1" class="NewReleasePrice"/>

<bean id="price2" class="ChildrensPrice"/>

Spring has now become our factory method. As new price codes are added, we can just register the Price class as a bean without having to manually change a factory method.

Comments