Java Strategy Pattern example

05 March 2014
By Gonçalo Marques
In this article we will see how to implement the Strategy design pattern in Java.

Strategy Design Pattern

The Strategy Design Pattern provides the ability of selecting a specific algorithm from a family of similar algorithms at runtime in order to complete a given task.

The concrete algorithm implementation itself is completely decoupled from the business component (or the context) that will actually make use of it. The client will select a concrete algorithm implementation and provide it to the business component at runtime.

This decoupling makes it easy for the client to change between algorithm implementations without the need for business code modification.

The Strategy pattern is achieved by the good practice of programming against interfaces: The business component holds a reference to an interface of an algorithm that processes a specific task. When the application is executing, the client will select a concrete algorithm implementation and pass it to the business component, which in turn will be able to process the task without knowing the details about the algorithm implementation chosen by the client.

In this article we will consider an illustrative product order placed in an online store. The order will know that there will be a payment processor entity that will be used to charge the consumer's credit card, but it will not know the specific details of the payment processor. It will only know that, at some point, it will be provided with a payment processor implementation that will be used to charge the consumer's credit card.

The Payment Processor

We will need an interface for our strategy (the payment processor):

Payment Processor

package com.byteslounge.payment;

public interface PaymentProcessor {

  void execute(int amount);

}

And a couple of strategy implementations: Visa and Mastercard payment processors.

Visa Payment Processor

package com.byteslounge.payment;

public class VisaPaymentProcessor implements PaymentProcessor {

  @Override
  public void execute(int amount) {
    System.out.println("Executing Visa payment: Charging $" + amount);
  }

}

Mastercard Payment Processor

package com.byteslounge.payment;

public class MastercardPaymentProcessor implements PaymentProcessor {

  @Override
  public void execute(int amount) {
    System.out.println("Executing Mastercard payment: Charging $" + amount);
  }

}

The Context

We will now define the context (or the business component) that will actually make use of the strategy:

The order

package com.byteslounge.order;

import com.byteslounge.payment.PaymentProcessor;

public class Order {

  private final PaymentProcessor paymentProcessor;
  private final int amount;

  public Order(int amount, PaymentProcessor paymentProcessor) {
    this.amount = amount;
    this.paymentProcessor = paymentProcessor;
  }

  public void process() {
    paymentProcessor.execute(amount);
  }

}

We have just defined a class that will represent an illustrative order placed in an online store. The order consists of an amount that will be charged to the consumer and a payment processor. Note that the reference to the payment processor is done by using an interface.

This means that we may provide the order with any concrete payment processor implementation without the order knowing specific details about the processor implementation: they are decoupled.

Testing

Now we may write a simple test class:

Testing

package com.byteslounge;

import com.byteslounge.order.Order;
import com.byteslounge.payment.VisaPaymentProcessor;

public class Main {

  public static void main(String[] args) {
    Order order = new Order(15, new VisaPaymentProcessor());
    order.process();
  }

}

We are simply building an order which total amount is $15 and the payment processor will be the Visa processor. When we run the test class the following output will be generated:

Executing Visa payment: Charging $15

Creational design patterns

As a final note we may still correlate the Strategy pattern with the creational patterns (ex: the Factory pattern). As we have just seen, the business component is decoupled from the concrete strategy implementation, but the client still needs to know about the concrete implementations and provide one to the business component.

We may also decouple the client from the concrete strategy implementations by using a creational pattern in order to produce the strategy implementations. For example, we may use the Factory pattern and create a factory that produces strategy implementations, and then let the client ask the factory for concrete strategies. The client itself would only hold a reference to the strategy interface so it becomes also decoupled from the concrete strategy implementations.

You may find more information in Java Factory Pattern example and Java Abstract Factory Pattern example.

Reference

Strategy pattern

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: