Showing posts with label Strategy Design pattern. Show all posts
Showing posts with label Strategy Design pattern. Show all posts

Monday, December 23, 2013

Springify The Strategy Pattern - Part 2


I talked about how to implement strategy pattern in springapplication in my previous post. Here I want to show you how Spring Annotations can make our lives easier to implement these patterns.
Spring 2.5 version introduced way to configure the dependency injection using annotations. This eliminates all those XML files in an application that just defined beans and bean wiring. Now annotation allows you to configure these inside the bean (component).

Some of the annotations that I will be using were @Autowired, @Resource, @Component, @Service etc.
All right let’s get into the code, I will take the second approach from the previous article and configure it using annotation. The new configuration XML looks like this,

<context:component-scan base-package="xyz.xyz.strategy"/>

What happened to all bean definitions? What is this component scan?
Well annotation based configuration eliminated the bean definition in the XML. Instead we enable spring auto component scanning of components in the base package where it can scan, detect, instantiate and wire all these beans (components).

Now let’s look at the beans again. You will have to go back to your strategy implementation classes like RegularShippingCostStrategyImpl etc. and add @Service or @Component annotation to the class as shown below,
@Service
public class RegularShippingCostStrategyImpl implements ShippingCostStrategy {
//calculation code
}

@Service
public class PremiumShippingCostStrategyImpl implements ShippingCostStrategy {
//calculation code
}

@Service
public class PrimeShippingCostStrategyImpl implements ShippingCostStrategy {
//calculation code
}

 
Your ShippingCostService class with added annotation below,
@Service
public class ShippingCostService {
 
 @Autowired
 private ShippingCostContext shippingContext = null;
  
 public double calculateShipping(Item item, String memberStatus){
  double cost = shippingContext.calculateShipping(item, memberStatus);
     return cost;
 }
 
}

 
Now look at this strategy context class,
@Component
public class ShippingCostContext {
 
 @Autowired
 private PrimeShippingCostStrategyImpl primeShippingStrategy = null;
 
 @Autowired
 private PremiumShippingCostStrategyImpl premiumShippingStrategy = null;
 
 @Autowired
 private RegularShippingCostStrategyImpl regularShippingStrategy = null;
        
 public double calculateShipping(Item item, String memberStatus){
       double cost = 0.0;
       if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_PRIME))
          cost = primeShippingStrategy.calculate(item);
       else if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_PREMIUM))
          cost = premiumShippingStrategy.calculate(item);
       else if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_REGULAR))
          cost = regularShippingStrategy.calculate(item);
    
       return cost;
 }
   
}

Now run the same test to check this out.
The same can be achieved for the first approach as well by simply switching auto wire strategy implementation classes from strategy context class to service class.
About the third approach is not all required since spring auto scan and auto wire simplifies the implementation already. But in case if you are interested here is how you can do it.

<context:component-scan base-package="xyz.xyz.strategy"/>
   
<util:map id="strategies" map-class="java.util.HashMap">
       <entry>
         <key>
           <util:constant static-field=" xyz.xyz.strategy.StrategyConstants.MEMBER_REGULAR"/>
          </key>
            <ref bean="regularShippingCostStrategyImpl" />
         </entry>
         <entry>
             <key>
              <util:constant static-field=" xyz.xyz.strategy.StrategyConstants.MEMBER_PREMIUM"/>
             </key>
             <ref bean="premiumShippingCostStrategyImpl" />
         </entry>
         <entry>
             <key>
              <util:constant static-field=" xyz.xyz.strategy.StrategyConstants.MEMBER_PRIME"/>
             </key>
             <ref bean="primeShippingCostStrategyImpl" />
         </entry>
   </util:map>

The XML context will have to define the spring util Map with key and ref value as shown above. You will then have to Autowire the map into the strategy context like this,
@Service
public class ShippingCostContext {
 
 @Resource
 private Properties strategies;
 
 public double calculateShipping(Item item, String memberstatus){
        double cost = 0.0;
        if(strategies.containsKey(memberstatus)){
         ShippingCostStrategy shippingCostStrategy = (ShippingCostStrategy)strategies.get(memberstatus);
         if(shippingCostStrategy != null)
          cost = shippingCostStrategy.calculate(item);
        }
               
 return cost;
 }


That’s all, the strategy pattern is now spring annotated. The spring annotations are quite powerful and simplify the configuration.

Friday, July 27, 2012

Springify The Strategy Pattern


            The strategy pattern, sure we have heard enough and used enough many times before. But if you are wondering how effectively one could use this pattern in a spring powered application then here are few approaches. I wouldn’t waste time talking about strategy pattern. So let’s get started with some code, consider a typical eCommerce company offering various levels of savings on shipping cost based on the type of memberships. So let’s keep this logic simple here to try out various approaches.

First thing First
  Here are few basic components of strategy pattern first. Start with an interface ShippingCostStrategy.java as shown below,
public interface IShippingCostStrategy {
    public double calculate(Item item);
}

Now write few possible strategy implementations like below,

public class PrimeShippingCostStrategyImpl implements IShippingCostStrategy {
     public double calculate(Item item) {
        double cost = 0.0;
        //Calculate the shipping for prime members 
        return cost;
     }
} 
public class PremiumShippingCostStrategyImpl implements IShippingCostStrategy {
     public double calculate(Item item) {
        double cost = 0.0;
        //Calculate the shipping for premium members 
        return cost;
     }
}
public class RegularShippingCostStrategyImpl implements IShippingCostStrategy {
     public double calculate(Item item) {
        double cost = 0.0;
        //Calculate the shipping for regular members 
        return cost;
     }
}
public class Item {
   private int itemId;
   private String code;
   private double price;

   public Item() {
      super();
   }
 
   public Item(int itemId, String code, double price) {
      super();
      this.itemId = itemId;
      this.code = code;
      this.price = price;
   }

   //All the getter and setter methods are omitted for brevity
}

Define these beans in the shippingCost-context.xml like below and these are required no matter what the approach is,

<bean id="primeShippingStrategy" class=" xxx.xxx.PrimeShippingCostStrategyImpl" />
<bean id="premiumShippingStrategy" class=" xxx.xxx.PremiumShippingCostStrategyImpl" />
<bean id="regularShippingStrategy" class=" xxx.xxx.RegularShippingCostStrategyImpl" />


Approach #1
             In this approach we will keep our context for the strategy simple meaning we will let the client (service) to take the responsibility of determining the possible right strategy implementation class and setting in to the context.
The calculateShipping method in the context will then call the calculate method on the strategy that is being set.
The ShippingContext.java we got here is as shown below,

public class ShippingCostContext {
   IShippingCostStrategy shippingCostStrategy = null;
 
   public double calculateShipping(Item item){
      return shippingCostStrategy.calculate(item);
   }
 
   public void setShippingCostStrategy(IShippingCostStrategy shippingCostStrategy) {
      this.shippingCostStrategy = shippingCostStrategy;
   }
  
}

Let’s look at the ShippingCostService that is being the client for this strategy.
Here we are injecting all the strategy classes and context into this client.

public class ShippingCostService {

   private PrimeShippingCostStrategyImpl primeShippingStrategy = null;
   private PremiumShippingCostStrategyImpl premiumShippingStrategy = null;
   private RegularShippingCostStrategyImpl regularShippingStrategy = null;
   private ShippingCostContext shippingContext = null;
 
   public double calculateShipping(Item item, String memberStatus){
      double cost = 0.0;
      if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_PRIME))
         shippingContext.setShippingCostStrategy(primeShippingStrategy);
      else if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_PREMIUM))
         shippingContext.setShippingCostStrategy(premiumShippingStrategy);
      else if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_REGULAR))
         shippingContext.setShippingCostStrategy(regularShippingStrategy);
   
      cost = shippingContext.calculateShipping(item);
    return cost;
 }

 //All the setter and getter for the above attributes goes here

}//End of ShippingCostService

Now the spring configuration would be straight forward,


<bean id="shippingContext" class=" xxx.xxx.SpringShippingCostContext" />
<bean id="shippingCostService" class=" xxx.xxx.ShippingCostService" >
   <property name="primeShippingStrategy" ref="primeShippingStrategy"/>
   <property name="premiumShippingStrategy" ref="premiumShippingStrategy"/>
   <property name="regularShippingStrategy" ref="regularShippingStrategy"/>
   <property name="shippingContext" ref="shippingContext"/>
</bean>

         
Simple class to test this out,
public static void main(String args[]) {
   ApplicationContext context = new ClassPathXmlApplicationContext("shippingCost-Context.xml"); 
   ShippingCostService shippingCostService = ShippingCostService)context.getBean("shippingCostService");
  
   String memberStatus = StrategyConstants.MEMBER_PREMIUM; //REGULAR, PREMIUM, PRIME
  
   Item item = new Item(1, "PREMIUM", 20.00);
   double cost = shippingCostService.calculateShipping(item,memberStatus);
   System.out.println("Shipping cost: for Member type: "+ memberStatus+ ", cost: "+cost);
}

Approach #2
             In this approach we are transferring the responsibility of determining the right strategy from client to the context.
So we will have to inject all the strategy implementation classes into the context as shown below,

public class ShippingCostContext {
  
  private PrimeShippingCostStrategyImpl primeShippingStrategy = null;
  private PremiumShippingCostStrategyImpl premiumShippingStrategy = null;
  private RegularShippingCostStrategyImpl regularShippingStrategy = null;
 
 
  public double calculateShipping(Item item, String memberStatus){
     double cost = 0.0;
     if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_PRIME))
         cost = primeShippingStrategy.calculate(item);
     else if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_PREMIUM))
         cost = premiumShippingStrategy.calculate(item);
     else if(memberStatus.equalsIgnoreCase(StrategyConstants.MEMBER_REGULAR))
         cost = regularShippingStrategy.calculate(item);
     return cost;
  }
 
  // All the setter and getter for the above attributes goes here
}


As we see the calculateShipping method in context will determine the call to the right strategy class based on the new parameter memberStatus. There by we freed the ShippingCostService from making the decision of injecting the right strategy implementation class into the context.

public class ShippingCostService {
 
     private ShippingCostContext shippingContext = null;
 
     public double calculateShipping(Item item, String memberStatus){
         double cost = 0.0;
         cost = shippingContext.calculateShipping(item, memberStatus);
         return cost;
     }
 
     // All the setter and getter for the above attributes goes here

}

The spring configuration for this approach would be,


<bean id="shippingContext" class=" xxx.xxx.SpringShippingCostContext" >
     <property name="primeShippingStrategy" ref="primeShippingStrategy"/>
     <property name="premiumShippingStrategy" ref="premiumShippingStrategy"/>
     <property name="regularShippingStrategy" ref="regularShippingStrategy"/>
</bean>


<bean id="shippingCostService" class=" xxx.xxx.ShippingCostService" >
     <property name="shippingContext" ref="shippingContext"/>
</bean>


We could test this approach out using the same test class code as given above in approach 1.

Approach #3
            The third approach improvises the approach 2 by introducing a map of available strategies.
So introducing any new strategy would just be a matter of configuring rather than coding in context.

Look at this code for context under this approach,

public class ShippingCostContext {
     private Map shippingStrategies = new HashMap(); 
  
     public double calculateShipping(Item item, String memberstatus){
         double cost = 0.0;
         if(shippingStrategies.containsKey(memberstatus)){
            IShippingCostStrategy shippingCostStrategy = (IShippingCostStrategy)shippingStrategies.get(memberstatus);
         if(shippingCostStrategy != null)
             cost = shippingCostStrategy.calculate(item);
  }
  
     return cost;
 }
 
     public void setShippingStrategies(Map shippingStrategies) {
         this.shippingStrategies = shippingStrategies;
 }
}

Context now holds the map of all configured strategy beans in it. This wouldn’t affect the way client invokes the strategy at all.
All it needs to do is pass an additional parameter memberStatus to the context to help determine the right strategy.

public class ShippingCostService {
     private ShippingCostContext shippingContext = null;
 
     public double calculateShipping(Item item, String memberStatus){
         double cost = 0.0;
         cost = shippingContext.calculateShipping(item, memberStatus);
         return cost;
     }
}

The configuration for this approach is simple enough as we are going to use the util schema to achieve this.
We created the property shippingStrategies of type Map with key using static fields and value would be respective bean.


<bean id="shippingContext" class="xxx.xxx.ShippingCostContext" >
   <property name="shippingStrategies">
     <map>
       <entry>
         <key>
           <util:constant static-field="xxx.xxx.StrategyConstants.MEMBER_REGULAR"/>
         </key>
           <ref bean="regularShippingStrategy" />
        </entry>
        <entry>
            <key>
             <util:constant static-field=" xxx.xxx.StrategyConstants.MEMBER_PREMIUM"/>
            </key>
            <ref bean="premiumShippingStrategy" />
        </entry>
        <entry>
            <key>
             <util:constant static-field=" xxx.xxx.StrategyConstants.MEMBER_PRIME"/>
            </key>
            <ref bean="primeShippingStrategy" />
        </entry>
    </map>
  </property>
</bean>


<bean id="shippingCostService" class=" xxx.xxx.ShippingCostService" >
      <property name="shippingContext" ref="shippingContext"/>
</bean>


Make sure to add the util namespace and shema location information to the header beans of the context xml like as shown,


xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd"


That’s it you are ready to test your strategy using the same test class above.

Final Note
    The advantage of approach 1 is, the client will be in control if the business logic in client (service) that determines the strategy to use is inseparable or tightly coupled. The same advantage could turn into a disaster with tight coupling. So approach 2 would bring in the much needed loose coupling and let strategy context take care of determining the right strategy. This brings in an opportunity to add more business intelligence into the context. The last approach as you saw definitely improvises the overall strategy to implement the strategy pattern in spring. Sure there must be even better approaches out there and if you do have one please share.

Reference
  1. Reference to the strategy pattern.
  2. Reference to the Spring Util schema documentation.