Template method pattern

Definition

The Template Method Pattern defines the steps of an algorithm and allow subclasses to provide the implementation for one or more steps.

templatemethod1

Figure 01. Class diagram Template method pattern. Ref.:http://programmersnotes.info/2009/03/03/difference-between-adapter-and-template-method-pattern/

Scenario

A telecommunication provider wants to automate its order process. For now the telecommunication provider offers internet and telephony service and they may offer in the future cable, or any other type of services.

To present a solution to this requirement, it was noticed that the order generation for telephony and internet were almost similar with small differences, is for this reason that for the order generation, the Template method pattern fits properly and as a plus, this design pattern is very flexible to add in the future other kind of offers that the telco provider may want to add.

The class diagram to generate orders is as shown in the next picture:

Template1

Figure 02. Template pattern applied to the order generation.

Template2

Figure 03. Template pattern applied to a simple Order printer to test the implementation.

Talking about the first Template pattern example, the classes InternetOrderGenerator and TelephonyOrderGenerator do almost the same thing with small differences, is for this reason that generalizing the common steps in the abstract class OrderGenerator, and by this way applying the Template Method pattern, is the best way to go and as a plus this pattern is flexible to extending this order generation to other kind of services that may come in the future.

The code of the abstract class is as follows:

OrderGenerator.java

package chris.designpatterns.template;

import java.util.List;
import java.util.Random;

import chris.designpatterns.template.model.Order;
import chris.designpatterns.template.model.Product;

public abstract class OrderGenerator {
    protected List<Product> products;
    
    public Order generate() {
        Order order = createOrder();
        //First step: Add products from the basket
        setProducts();
        
        //set order particular characteristics
        setCharacteristics();
        
        //calculate price
        float total = priceCalculator();
        order.setCost(total);
        
        //generate order id
        int orderId = generateId();
        order.setId(orderId);
        
        return order;
    }
    
    public abstract Order createOrder();
    public abstract void setCharacteristics();
    public abstract void setProducts();      

    private int generateId() {
        return new Random().nextInt(100000000);
    }

    private float priceCalculator() {
        float total = 0;
        for(Product p : products){
            total += p.getPrice();
        }   
        return total;
    }
}

 

The subclasses are simpler because a big part of the logic is done in the template abstract class.

TelephonyOrderGenerator.java

package chris.designpatterns.template;

import java.util.ArrayList;

import chris.designpatterns.template.model.Order;
import chris.designpatterns.template.model.Product;
import chris.designpatterns.template.model.TelephoneProduct;
import chris.designpatterns.template.model.TelephonyOrder;
import chris.designpatterns.template.model.TelephonyPlanProduct;

public class TelephonyOrderGenerator extends OrderGenerator {
    private TelephoneProduct telephone;
    private TelephonyPlanProduct plan;
    private TelephonyOrder order;
    
    
    public TelephonyOrderGenerator(TelephoneProduct telephone, 
TelephonyPlanProduct plan){
        this.telephone = telephone;
        this.plan = plan;
    }

    @Override
    public void setCharacteristics() {
        TelephonyOrder telOrder = (TelephonyOrder) order;
        telOrder.setPhoneNumber(telephone.getNumber());
        telOrder.setPlanMinutes(plan.getMinutes());
    }

    @Override
    public Order createOrder() {
        this.order = new TelephonyOrder();
        
        return order;
    }

    @Override
    public void setProducts() {
        products = new ArrayList<Product>();
        products.add(telephone);
        products.add(plan);
        
        order.setProducts(products);
    }

}

InternetOrderGenerator.java

package chris.designpatterns.template;

import java.util.ArrayList;

import chris.designpatterns.template.model.InternetOrder;
import chris.designpatterns.template.model.InternetProduct;
import chris.designpatterns.template.model.Order;
import chris.designpatterns.template.model.Product;

public class InternetOrderGenerator extends OrderGenerator {
    private InternetProduct internet;
    private InternetOrder order;
    
    public InternetOrderGenerator(InternetProduct internet) {
        this.internet = internet;
    }

    @Override
    public void setCharacteristics() {
        order.setInternetSpeed(internet.getSpeed());
        order.setRouterBrand(internet.getRouterBrand());
    }

    @Override
    public Order createOrder() {
        order = new InternetOrder();
        return order;
    }

    @Override
    public void setProducts() {
        products = new ArrayList<Product>();
        products.add(internet);
        
        order.setProducts(products);
    }

}

Notice the way the abstract methods were implemented in both classes.

And the client that will test our implementation:

Main.java

package chris.designpatterns.template;

import chris.designpatterns.template.model.InternetFactory;
import chris.designpatterns.template.model.InternetProduct;
import chris.designpatterns.template.model.Order;
import chris.designpatterns.template.model.TelephoneFactory;
import chris.designpatterns.template.model.TelephoneProduct;
import chris.designpatterns.template.model.TelephonyPlanFactory;
import chris.designpatterns.template.model.TelephonyPlanProduct;
import chris.designpatterns.template.utility.OrderPrinter;
import chris.designpatterns.template.utility.OrderPrinterInternet;
import chris.designpatterns.template.utility.OrderPrinterTelephony;

public class Main {
         
    public static void main(String[] args) {
        Main main = new Main();
        //Telephony Order
        main.runTelephonyOrder();
        
        //Internet Order
        main.runInternetOrder();
    }

    private void runInternetOrder() {
        InternetProduct internet = InternetFactory.createProduct();
        internet.setRouterBrand("Cisco 3500");
        
        OrderGenerator orderGenerator = new InternetOrderGenerator(internet);
        Order order = orderGenerator.generate();
        
        OrderPrinter printer = new OrderPrinterInternet();
        printer.print(order);  
    }

    private void runTelephonyOrder() {
        TelephoneProduct telephone = TelephoneFactory.createProduct();
        telephone.setNumber("0652630233");
        
        TelephonyPlanProduct telephonyPlan = TelephonyPlanFactory.createProduct();
        
        OrderGenerator orderGenerator = new TelephonyOrderGenerator(telephone, 
telephonyPlan); 
        Order order = orderGenerator.generate();
        
        OrderPrinter printer = new OrderPrinterTelephony()
        printer.print(order);
    }
}

Another example of the implementation of the Template method pattern can be checked in the classes:

  • OrderPrinter
  • OrderPrinterInternet
  • OrderPrinterTelephony

The code of the full project is attached to this post.

Template Pattern

Hope you find it useful and as always, if you have any questions and/or recommendations, feel free to make them.

Command Pattern

Definition

The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queues or log requests, and support undoable operations.

image

Figure 01. Class diagram of the Command Pattern. Ref: http://en.wikipedia.org/wiki/Command_pattern

As can be seen in the previous figure, the elements that characterize the Command Pattern are:

  • An invoker
  • A command
  • A receiver

The invoker is the one that will execute any Command that is set to it.

The command is the interface that will have the generic methods –commands- that will be specifically implemented by the classes that extend it.

The receiver is the class that will actually have all the logic required to execute any of the commands invoked by the invoker.

As we can see here, we are generating actual objects (ConcreteCommand objects) that will represent the commands that need to be executed by the receiver, as the definition of this pattern states.

Scenario

We need to implement a system that will either submit or update orders.

The solution proposed can be seen in the following class diagram:

image

Figure 02. Class diagram of the solution proposed for the study case.

In this diagram, these are our Command Pattern elements:

  • Invoker: OrderAgent class
  • Command: OrderCommand interface
  • Receiver: OrderManager class

The OrderAgent class has the following code:

OrderAgent.java

package chris.desingpatterns.command; import chris.desingpatterns.command.commmands.OrderCommand; public class OrderAgent { private OrderCommand command; /** * Method that will store all the * orders places by an Agent. * * @param command */ public void sendCommand(OrderCommand command){ this.command = command; command.excecute(); } public void undoCommand() { if(command != null){ command.undo(); } } }

As we can see here the agent will be capable of execute to actions –commands-:

  • sendCommand() to execute the command.
  • undoCommand() to revert the command executed.

The command interface looks like this:

OrderCommand.java

package chris.desingpatterns.command.commmands; public interface OrderCommand { /** * This is a general method that will * execute an action in a Receiver, * in this case the OrderManager class. */ void excecute(); /** * Method that will revert an action * that could be processed at the end. * */ void undo(); }

One of the concrete commands is:

UpdateOrder.java

package chris.desingpatterns.command.commmands; import chris.desingpatterns.command.receivers.OrderManager; public class UpdateOrder implements OrderCommand { private OrderManager orderManager; public UpdateOrder(OrderManager orderManager) { this.orderManager = orderManager; } /** * {@inheritDoc} */ public void excecute() { orderManager.update(); } /** * {@inheritDoc} * */ public void undo() { orderManager.restoreOriginal(); } }

And the class that have all the logic to execute all the actions required by the invoker is:

OrderManager.java

package chris.desingpatterns.command.receivers; import java.util.List; import org.apache.log4j.Logger; import chris.desingpatterns.command.dao.OrderDao; import chris.desingpatterns.command.pojos.Order; import chris.desingpatterns.command.pojos.OrderFactory; import chris.desingpatterns.command.pojos.Product; public class OrderManager { private static Logger log = Logger.getLogger(OrderManager.class); private OrderDao dao; private OrderFactory orderFactory; private List<Product> products; private Order orderFound; private Order currentOrder; public OrderManager() { dao = OrderDao.getInstance(); orderFactory = OrderFactory.getInstance(); } /** * Method that will replace an existing order * by a new one but that will preserve the same * ID as the old order. * */ public void update() { if(orderFound != null){ String oldOrderId = orderFound.getId(); orderFactory.setProducts(products); Order newOrder = orderFactory.createOrder(); newOrder.setId(oldOrderId); currentOrder = newOrder; dao.update(oldOrderId, newOrder); }else{ log.warn("Order not found"); } } /** * Method that will create a new Order * that will be saved by the DAO. * */ public void submit() { orderFactory.setProducts(products); Order newOrder = orderFactory.createOrder(); currentOrder = newOrder; dao.store(newOrder); } /** * Method that will remove an order * that was saved by the DAO. * */ public void remove() { String id = currentOrder.getId(); dao.delete(id); } /** * Method that will get an order * that was cached. * */ public void restoreOriginal() { String id = currentOrder.getId(); currentOrder = dao.getCached(id); } /** * Method that will retrieve an order * that was saved by the DAO. * * @param id */ public void findOrder(String id) { orderFound = dao.get(id); } public void setProducts(List<Product> products) { this.products = products; } public Order getCurrentOrder() { return currentOrder; } }

And the class that will run or study case (in Command Pattern terms: the Client) is:

Main.java

package chris.desingpatterns.command; import java.util.ArrayList; import java.util.List; import chris.desingpatterns.command.commmands.OrderCommand; import chris.desingpatterns.command.commmands.SubmitOrder; import chris.desingpatterns.command.commmands.UpdateOrder; import chris.desingpatterns.command.pojos.Order; import chris.desingpatterns.command.pojos.Product; import chris.desingpatterns.command.receivers.OrderManager; public class Main { /** * @param args */ public static void main(String[] args) { /********* PRODUCTS *************/ Product tv = new Product(); tv.setDescription("TV Sony"); tv.setName("TV"); Product radio = new Product(); radio.setDescription("Radio Panasonic"); radio.setName("RADIO"); Product telephone = new Product(); telephone.setDescription("iPhone Phone"); telephone.setName("TELEPHONE"); /*********************************/ OrderAgent agent = new OrderAgent(); List<Product> productsA = new ArrayList<Product>(); productsA.add(tv); productsA.add(radio); OrderManager orderManagerA = new OrderManager(); orderManagerA.setProducts(productsA); // SUBMIT ORDER 1 System.out.println(".:.SUBMIT ORDER 1.:."); OrderCommand submitCommand = new SubmitOrder(orderManagerA); agent.sendCommand(submitCommand); print(orderManagerA.getCurrentOrder()); //REVERT ORDER SUBMITED System.out.println(".:.UNDO SUBMIT ORDER 1.:."); agent.undoCommand(); //New Order to Submit List<Product> productsB = new ArrayList<Product>(); productsB.add(telephone); productsB.add(tv); productsB.add(radio); OrderManager orderManagerB = new OrderManager(); orderManagerB.setProducts(productsB); // Submit ORDER 2 System.out.println(".:.SUBMIT ORDER 2.:."); submitCommand = new SubmitOrder(orderManagerB); agent.sendCommand(submitCommand); print(orderManagerB.getCurrentOrder()); String id = "1"; OrderManager orderManagerC = new OrderManager(); orderManagerC.findOrder(id); List<Product> productsC1 = new ArrayList<Product>(); productsC1.add(telephone); orderManagerC.setProducts(productsC1); //Update ORDER 1 System.out.println(".:.UPDATE ORDER 1.:."); OrderCommand updateCommand = new UpdateOrder(orderManagerC); agent.sendCommand(updateCommand); print(orderManagerC.getCurrentOrder()); //Submit ORDER 3 System.out.println(".:.SUBMIT ORDER 3.:."); OrderManager orderManagerD = new OrderManager(); List<Product> productsD = new ArrayList<Product>(); productsD.add(radio); productsD.add(telephone); orderManagerD.setProducts(productsD); submitCommand = new SubmitOrder(orderManagerD); agent.sendCommand(submitCommand); print(orderManagerD.getCurrentOrder()); //Update ORDER 3 System.out.println(".:.UPDATE ORDER 3.:."); OrderManager orderManagerE = new OrderManager(); List<Product> productsE = new ArrayList<Product>(); productsE.add(telephone); productsE.add(radio); productsE.add(tv); orderManagerE.setProducts(productsE ); orderManagerE.findOrder("3"); updateCommand = new UpdateOrder(orderManagerE); agent.sendCommand(updateCommand); print(orderManagerE.getCurrentOrder()); //UNDO Update ORDER 3 System.out.println(".:.UNDO UPDATE ORDER 3.:."); agent.undoCommand(); print(orderManagerE.getCurrentOrder()); } private static void print(Order order){ if(order != null){ System.out.println("----------------------------"); System.out.println("Products for Order["+order.getId()+"]: " ); for(Product p : order.getProducts()){ System.out.println("- " + p.getName()); } System.out.println("----------------------------"); } } }

That is it!

To download the code click in the following link:

Command Pattern

Do you have any recommendations or amendments to be done, please let me know to improve this post!

Observer Pattern

 

Definition

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

image

Figure 01. Observer Pattern class diagram. Ref: http://en.wikipedia.org/wiki/Observer_pattern

Scenario

We need to simulate a Horse Race, on which we need to keep the information updated in a website once all the results of the horse race are known and also we need to update a Data Base to keep history of the result of the race.

Class Diagram

image

Figure 02. Class diagram of the case of study

As it can be seen in the previous figure, the Observer Pattern was implemented to keep the WebsiteUpdater, RaceDataBaseUpdater and ComandLineRaceDisplayUpdater updated when the race is over, as it was part of the requirement.

The Subject is the Race class, because this class is the one that is keeping record of all the horses that reach the Final line and is the one that is going to accept any Observer that want to register themselves and keep them updated.

Subject.java

package chris.desingpatterns.observer.subject;

import chris.desingpatterns.observer.observers.Observer;

public interface Subject {
    
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();

}

Race.java

package chris.desingpatterns.observer.subject;

import java.util.ArrayList;
import java.util.List;

import chris.desingpatterns.observer.competitor.Raceable;
import chris.desingpatterns.observer.observers.Observer;

public class Race implements Subject {
    
    private int numberOfLaps;
    private int circuitPerimeter;
    private List<Raceable> competitors;
    private List<Raceable> raceRegister;

    //Here will be registered all the classes that want to get updates from Race
    private List<Observer> observers;
    
    public Race(int numberOfLaps, int circuitPerimeter) {
        this.numberOfLaps = numberOfLaps;
        this.circuitPerimeter = circuitPerimeter;
        raceRegister = new ArrayList<Raceable>();
        competitors = new ArrayList<Raceable>();
        observers = new ArrayList<Observer>();
    }
   
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    /**
     * This method is the one that will 
     * notify all the observer that the race
     * was over, so they can proceed to do 
     * whatever they need with the information
     * provided.
     */
    public void notifyObservers() {
        for(Observer o : observers){
            o.update(raceRegister);
        }
    }
    
    /**
     * Method that will wait to all the 
     * threads to get ready to run();
     * @throws InterruptedException
     */
    public synchronized void getReady() throws InterruptedException{
        this.wait();
    }
    
    /**
     * Method that will be triggered when all the
     * threads are ready to run()
     */
    public synchronized void startRace(){
        this.notifyAll();
    }
    
    /**
     * Method that will register all the objects
     * that have finished the competition.
     * @param raceable
     */
    public void reachedFinal (Raceable raceable){
        raceRegister.add(raceable);
        if(isRaceOver()){
            //Notify to update
            notifyObservers();
        }
    }
    
    public void setCompetitors(Raceable competitor){
        competitors.add(competitor);
    }
    
    /**
     * Method that will verify if all the 
     * competitors have cross the Final.
     * @return
     */
    private boolean isRaceOver(){
        return raceRegister.size() == competitors.size();
    }
    
    public int getNumberOfLaps() {
        return numberOfLaps;
    }

    public int getCircuitPerimeter() {
        return circuitPerimeter;
    }

}

The Observers are the ones that will be notified once the race is over, this will happen when the Subject class will execute the method notifyObservers() triggering the method update() in all the Observers.

Observer.java

package chris.desingpatterns.observer.observers;

import java.util.List;

import chris.desingpatterns.observer.competitor.Raceable;

public interface Observer {
    
    /**
     * This method will be triggered once our Race subject
     * has registered all the elements that were racing,
     * so by this way we can get the information about
     * positions and results of the race.
     * 
     * @param raceRegister
     */
    void update(List<Raceable> raceRegister);

}

CommandLineRaceDisplayUpdater.java

package chris.desingpatterns.observer.observers;

import java.util.List;

import chris.desingpatterns.observer.competitor.Raceable;
import chris.desingpatterns.observer.subject.Subject;

public class CommandLineRaceDisplayUpdater implements Observer {
    
    public CommandLineRaceDisplayUpdater(Subject race) {
        race.registerObserver(this);
    }
    
    /**
     * This method will display the results of the race.
     * 
     * @param raceRegister
     */
    public void display(List<Raceable> raceRegister){
        System.out.println("::::COMMAND LINE RACE DISPLAYER:::");
        System.out.println("The race is over!!!");
        
        System.out.println("The winner horse was: " 
        + raceRegister.get(0).getIdentification());
        
        System.out.println("These are the results: ");
        int i = 1;
        for(Raceable racingObject : raceRegister){
            System.out.println(i + "   " + racingObject.getIdentification() + "");
            
            System.out.println("    AVERAGE SPEED(Km/Hr): " 
                    + racingObject.getSpeed() 
                    + "| TOTAL TIME: " + racingObject.getTimeScored());
            
            i++;
        }
        System.out.println("Hope that you chose the winner horse :D!");
        System.out.println("::::::::::::::::::::::::::::::::::::");
    } 
        
    /**
     * This method will be triggered when the competition has 
     * finished.
     */
    public void update(List<Raceable> raceRegister) {
        display(raceRegister);
    }

}

And the class that will run the case of study:

Main.java

package chris.desingpatterns.observer;

import chris.desingpatterns.observer.competitor.Horse;
import chris.desingpatterns.observer.competitor.RunningObject;
import chris.desingpatterns.observer.observers.CommandLineRaceDisplayUpdater;
import chris.desingpatterns.observer.observers.RaceDataBaseUpdater;
import chris.desingpatterns.observer.observers.WebsiteUpdater;
import chris.desingpatterns.observer.subject.Race;

public class Main {

    /**
     * @param args
     * @throws InterruptedException 
     */
    public static void main(String[] args) throws InterruptedException {
        int numberOfLaps = 3;
        int circuitPerimeter = 1; // 1 Km
        Race race = new Race(numberOfLaps, circuitPerimeter);
        
        Horse horse1 = new Horse("Konan");
        race.setCompetitors(horse1);
        new Thread(new RunningObject(horse1, race)).start();
        
        Horse horse2 = new Horse("Achilles");
        race.setCompetitors(horse2);
        new Thread(new RunningObject(horse2, race)).start();
        
        Horse horse3 = new Horse("Brave Inca");
        race.setCompetitors(horse3);
        new Thread(new RunningObject(horse3, race)).start();
        
        Horse horse4 = new Horse("Black Beauty");
        race.setCompetitors(horse4);
        new Thread(new RunningObject(horse4, race)).start();
        
        Horse horse5 = new Horse("Trigger");
        race.setCompetitors(horse5);
        new Thread(new RunningObject(horse5, race)).start();
        
        //This are the objects that will update the information
        //once the race is over.
        new CommandLineRaceDisplayUpdater(race);
        new WebsiteUpdater(race);
        new RaceDataBaseUpdater(race);
        
        //Let's start the race
        System.out.println("Get ready...");
        Thread.sleep(1000);
        System.out.println("Get set...");
        Thread.sleep(1000);
        System.out.println("GO!!!");
        race.startRace();

    }

}

That is it! this design (thanks to the help of the Observable pattern) is very flexible (we can add or remove as many Observables as we want) and the Observers are not tightly coupled to the Subject.

So download the source code and enjoy the race!

Cheers!!!

Observer Pattern Code

Do you have any recommendations or amendments to be done, please let me know to improve this post!

Strategy Pattern

Definition

This pattern defines a family of algorithms,  encapsulates each one and make them interchangeable.

Strategy lets the algorithm vary independently from clients that use it.

image

Figure  01. Class diagram Ref: http://en.wikipedia.org/wiki/Strategy_pattern

Scenario

We need to design a Customer Promotion Tool for a cell phone company, on which per different seasons of the year or any special random day that the manager of the company came up in his mind, there is supposed to be a new promotion notified to the  customer when he wants to buy a new telephone so he/she can have a discount on his/her buying and by that way be happier 😀

Class Diagram

For the scenario described we have the following class diagram:

UntitledDocument (1)

Figure 02. Class diagram of the case of study.

The strategy pattern was applied on the calculation of the promotional prices as is shown in the previous figure. For this example the Context is the Calculator class and the Strategy is the Promotion interface.

To get an idea of the way that the pattern was implemented, this is the class that will do the product calculations:

ProductCalculator.class

package chris.desingpatterns.strategy.calculations;

import chris.desingpatterns.strategy.products.Product;
import chris.desingpatterns.strategy.promotions.Promotion;

public class ProductCalculator implements Calculator {
    private Product product;
    private Promotion promotion;

    public ProductCalculator(Product product) {
        this.product = product;
    }

    /**
     * {@inheritDoc}
     */
    public float getCalculation() {
        float productPrice = product.getProductPrice();
        float calculatedPrice = productPrice;

        if(promotion != null){
            float discount = promotion.getPromotionDisscount(productPrice);
            calculatedPrice -= discount;     
        }

        return calculatedPrice;
    }

    public Promotion getPromotion() {
        return promotion;
    }
    public void setPromotion(Promotion promotion) {
        this.promotion = promotion;
    }
    public Product getProduct() {
        return product;
    }
    public void setProduct(Product product) {
        this.product = product;
    }
}

And this is one of the implementations of the Promotions:

WinterPromotion.class

package chris.desingpatterns.strategy.promotions;

public class WinterPromotion implements Promotion {

    public String getPromotionName() {
        return "Winter Promotion";
    }

    public float getPromotionDisscount(float originalPrice) {
        return originalPrice * 30/100;
    }

    public String getPromotionInformation() {
        return "This is the Witer Promotion, because the weather " +
                "is cold and you are spending all your money paying " +
                "the gas bills, buy one of our products" +
                "and get an amazing discount of 30%";
    }

}

And finally the class that will run the case of study for this design pattern:

Main.class

package chris.desingpatterns.strategy;

import chris.desingpatterns.strategy.calculations.Calculator;
import chris.desingpatterns.strategy.calculations.ProductCalculator;
import chris.desingpatterns.strategy.customer.Customer;
import chris.desingpatterns.strategy.products.Htc;
import chris.desingpatterns.strategy.products.Product;
import chris.desingpatterns.strategy.promotions.BirthdayPromotion;
import chris.desingpatterns.strategy.promotions.Promotion;
import chris.desingpatterns.strategy.promotions.SummerPromotion;
import chris.desingpatterns.strategy.promotions.WinterPromotion;

public class Main {
    public static void main(String[] args) {
        Customer kasia = new Customer("Kasia");

        Product htcPhone = new Htc();
        kasia.buyProduct(htcPhone);

        // Case 01 Summer Promotion
        Promotion summerPromotion = new SummerPromotion();
        Calculator calculator = new ProductCalculator(htcPhone);
        calculator.setPromotion(summerPromotion);
        System.out.println(".:. Case 1: ");
        printInfo(kasia, htcPhone, calculator, summerPromotion);

        // Case 02 Winter Promotion
        Promotion winterPromotion = new WinterPromotion();
        calculator.setPromotion(winterPromotion);
        System.out.println(".:. Case 2");
        printInfo(kasia, htcPhone, calculator, winterPromotion);

        // Case 03 Birthday Promotion
        Promotion bdayPromotion = new BirthdayPromotion();
        calculator.setPromotion(bdayPromotion);
        System.out.println(".:. Case 3");
        printInfo(kasia, htcPhone, calculator, bdayPromotion);

    }

    private static void printInfo(Customer customer, Product product,
            Calculator calculator, Promotion promotion) {

        System.out.println("Hi " + customer.getName() + "!");
        System.out.println(promotion.getPromotionInformation());

        System.out.println("The original price of the: "
                + product.getProductName() + " is: "
                + product.getProductPrice() + "Euros");

        System.out.println("The price of your " + product.getProductName()
                + " thanks to the promotion: " + promotion.getPromotionName()
                + " will be: " + calculator.getCalculation() + "Euros");

        System.out.println("-----------------------------");
    }

}

As we can see, applying the Strategy Pattern makes the design more flexible in case a new promotion needs to be introduced, removed or simply updated, and the most important thing is that all these changes will not affect the way that the class ProductCalculator works. Modifications of promotions are done only under the Promotion classes.

If you want to check all the implementation of the case of study, download the code here:

Strategy Pattern Code

Do you have any recommendations or amendments to be done, please let me know to improve this post!