Strategy is very flexible behavioral design pattern. An object of a class has got an access to strategy collection, which can be set during the runtime of application. By calling one method the object behavior can be changed depending of which strategy was set. Strategy pattern allows to isolate certain behavior from the class, thanks to which the main class does not need to know all behaviors of all particular methods. All the main class needs to do is just set particular strategy and call one method to get desired effect. Let’s see this pattern in following example. We need to have a class which will be calculating price for our customer depends on whether the customer is signed up for our newsletter or not. If the customer is not signed up he gets a regular price but if he is signed up he gets sale price with 5% discount. We do not want to have different methods in our class for calculating prices, we just want to have only one method to do this and the second one to set the proper strategy. First let’s separate calculating price behavior to another classes. We have two ways for calculate price so we need to have two classes. To do it according to strategy pattern we need to declare an interface. We will call it PricingStrategy and it will have only one method, calculatePrice. This method will not be returning anything, it will just print calculated price. We need to pass the price and true or false whether the customer is signed up for our newsletter or not.
package StrategyTask.pricing_strategy;
public interface PricingStrategy {
void calculatePrice(float price, boolean isSignedUpForNewsletter);
}
Now let’s create the first class which will implement our PricingStrategy interface. This class will implement strategy for regular price. We just need to override calculatePrice method. If user is not signed up to our newsletter he gets a regular price, so we will just print appropriate message. But if the user is signed up, we need to use different strategy, so we will just inform another developer about this.
package StrategyTask.pricing_strategy;
public class RegularPrice implements PricingStrategy {
@Override
public void calculatePrice(float price, boolean isSignedUpForNewsletter) {
if(!isSignedUpForNewsletter) {
System.out.println("Your price is: " + price + "PLN.");
} else {
System.out.println("This user is signed up for newsletter! Different strategy should be chosen!");
}
}
}
Then we need to create the second class which will implement strategy for sale price. If user is signed up for our newsletter we will print a price with 5% discount. But if he is not signed up, the regular price strategy should be used.
package StrategyTask.pricing_strategy;
public class SalePrice implements PricingStrategy {
@Override
public void calculatePrice(float price, boolean isSignedUpForNewsletter) {
if(isSignedUpForNewsletter) {
System.out.println("Your price is: " + (Math.round((price * 0.95) * 100) / 100.0) + "PLN.");
} else {
System.out.println("This user is not signed up for newsletter! Different strategy should be chosen!");
}
}
}
Now it’s time for our PriceCalculator class! It will have only one private field which will be an object of our PricingStrategy interface. It will have only one setter to set appropriate strategy and one method to calculate price. This method will be passing the price and true or false, depends whether the user is signed up or not, to calculatePrice method of our object.
package StrategyTask;
import StrategyTask.pricing_strategy.*;
public class PriceCalculator {
private PricingStrategy pricingStrategy;
public void setPricingStrategy(PricingStrategy pricingStrategy) {
this.pricingStrategy = pricingStrategy;
}
public void calculatePrice(float price, boolean isSignedUpForNewsletter) {
pricingStrategy.calculatePrice(price, isSignedUpForNewsletter);
}
}
Then we can test it in the main class. First we declare an object of our PriceCalculator class. Then we set the pricing strategy of regular price and call the calculatePrice method with the price of 100.99 and false because our user is not signed up to newsletter. After this we set the same strategy, we pass the same price but we pass true now, which is wrong for regular price strategy. Then we do the same for sale price strategy.
import StrategyTask.*;
import StrategyTask.pricing_strategy.*;
public class Main {
public static void main(String[] args) {
PriceCalculator priceCalculator = new PriceCalculator();
//User is not signed up for newsletter - regular price
priceCalculator.setPricingStrategy(new RegularPrice());
priceCalculator.calculatePrice(100.99f, false);
//User is signed up for newsletter but wrong strategy was chosen
priceCalculator.setPricingStrategy(new RegularPrice());
priceCalculator.calculatePrice(100.99f, true);
System.out.println();
//User is signed up for newsletter - sale price
priceCalculator.setPricingStrategy(new SalePrice());
priceCalculator.calculatePrice(100.99f, true);
//User is signed up for newsletter but wrong strategy was chosen
priceCalculator.setPricingStrategy(new SalePrice());
priceCalculator.calculatePrice(100.99f, false);
}
}
When we run our program we get the below as expected.
Your price is: 100.99PLN.
This user is signed up for newsletter! Different strategy should be chosen!
Your price is: 95.94PLN.
This user is not signed up for newsletter! Different strategy should be chosen!
Process finished with exit code 0