Strategy Pattern using Lambda Expressions in Java 8

Strategy Pattern is one of the patterns from the Design Patterns : Elements of Reusable Object book. The intent of the strategy pattern as stated in the book is:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

In this post I would like to give an example or two on strategy pattern and then rewrite the same example using lambda expressions to be introduced in Java 8.

Strategy Pattern: An example

Consider an interface declaring the strategy:

interface Strategy{
  public void performTask();
}

Consider two implementations of this strategy:

class LazyStratgey implements Strategy{

  @Override
  public void performTask() {
    System.out.println("Perform task a day before deadline!");
  }
  
}

class ActiveStratgey implements Strategy{

  @Override
  public void performTask() {
    System.out.println("Perform task now!");
  }
  
}

The above strategies are naive and I have kept it simple to help readers grasp it quickly. And lets see these strategies in action:

public class StartegyPatternOldWay {

  public static void main(String[] args) {

    List<Strategy> strategies = 
        Arrays.asList(
          new LazyStratgey(), 
          new ActiveStratgey()
        );
                      
    for(Strategy stg : strategies){
      stg.performTask();
    }
  }
}

The output for the above is:

Perform task a day before deadline!
Perform task now!

Strategy Pattern: An example with Lambda expressions

Lets look at the same example using Lambda expressions. For this we will retain our Strategy interface, but we need not create different implementation of the interface, instead we make use of lambda expressions to create different implementations of the strategy. The below code shows it in action:

import java.util.Arrays;
import java.util.List;

public class StrategyPatternOnSteroids {
  public static void main(String[] args) {
      
    System.out.println("Strategy pattern on Steroids");
    
    List<Strategy> strategies = 
      Arrays.asList(
        () -> {System.out.println("Perform task a day before deadline!");},
        () -> {System.out.println("Perform task now!");}
      );
    
    strategies.forEach((elem) -> elem.performTask());
  }
  
}

The output for the above is:

Strategy pattern on Steroids
Perform task a day before deadline!
Perform task now!

In the example using lambda expression, we avoided the use of class declaration for different strategies implementation and instead made use of the lambda expressions.

Strategy Pattern: Another Example

This example is inspired from Neal Ford’s article on IBM Developer works: Functional Design Pattern-1. The idea of the example is exactly similar, but Neal Ford uses Scala and I am using Java for the same with a few changes in the naming conventions.

Lets look at an interface Computation which also declares a generic type T apart from a method compute which takes in two parameters.

interface Computation<T> {

  public T compute(T n, T m);
}

We can have different implementations of the computation like: IntSum – which returns the sum of two integers, IntDifference – which returns the difference of two integers and IntProduct – which returns the product of two integers.

class IntSum implements Computation<Integer> {

  @Override
  public Integer compute(Integer n, Integer m) {
    return n + m;
  }

}

class IntProduct implements Computation<Integer> {

  @Override
  public Integer compute(Integer n, Integer m) {
    return n * m;
  }
}

class IntDifference implements Computation<Integer> {

  @Override
  public Integer compute(Integer n, Integer m) {
    return n - m;
  }
}

Now lets look at these strategies in action in the below code:

public class AnotherStrategyPattern {

  public static void main(String[] args) {
  
    List<Computation> computations = 
        Arrays.asList(
          new IntSum(), 
          new IntDifference(), 
          new IntProduct()
        );
    
    for (Computation comp : computations) {
      System.out.println(comp.compute(10, 4));
    }
  }
}

The output for the above is:

14
6
40

Strategy Pattern: Another Example with lambda expressions

Now lets look at the same example using Lambda expressions. As in the previous example as well we need not declare classes for different implementation of the strategy i.e the Computation interface, instead we make use of lambda expressions to achieve the same. Lets look at an example:

public class AnotherStrategyPatternWithLambdas {
  public static void main(String[] args) {

    List<Computation<Integer>> computations = 
          Arrays.asList(
              (n, m)-> { return n+m; },
              (n, m)-> { return n*m; },
              (n, m)-> { return n-m; }
          );
    computations.forEach((comp) -> System.out.println(comp.compute(10, 4)));
  }
}

The output for above is:

14
6
40

From the above examples we can see that using Lambda expressions will help in reducing lot of boilerplate code to achieve more concise code. And with practice one can get used to reading lambda expressions.

Advertisements

10 thoughts on “Strategy Pattern using Lambda Expressions in Java 8

    1. Mohamed Sanaulla Post author

      Why do you think it is difficult to test a lambda? Lambda expressions are just implementation of certain interface/single abstarct method classes. One can assign them to different variables and then use these variables to invoke the methods thereby unit testing them.

      Like

      Reply
    1. Mohamed Sanaulla Post author

      1. Lambda expressions and the related changes in the collections API to support them really change the way collections are accessed. And this is of greater advantage when the collection is being processed in a mutlicore environment. (http://stackoverflow.com/questions/16981796/what-are-the-advantages-of-lambda-expressions-for-multicore-systems)
      2. There are few different usecases of lambda expressions mentioned on the official documentation(http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)

      Lambda expressions give a more cleaner way of creating implementations for Single Abstract Method(SAM) classes. One can even assign these lambda expressions implementations to different variables, something like:

      Computation sumCompute = (n, m)-> {return n+m;};
      Computation prodCompute = (n, m)-> {return n*m;};
      

      The above would not have been so straight forward if the lambda expressions were not supported.

      Like

      Reply
  1. Sebastian

    The approach is suitable for simple strategies, that can invented by a client on the spur of the moment supplied to the strategy executor. But these are not re-usable. If a strategy is complex (think creating a PDF report from a database query vs. creating a csv-file) you want it to be a re-usable class and not an anonymous function. Lambdas really don’t have an advantage here, in my view.

    Like

    Reply
  2. tshepo

    how can i display the values being calculated…like this requires a user tho input two numbers and the system computes the answer and displays the numbers and the answer!!

    Like

    Reply
  3. Bharat

    I don’t think the below lines are replacement for Strategy pattern as they do not call actual classes that implement the strategy.
    List<Computation> computations =
    Arrays.asList(
    (n, m)-> { return n+m; },
    (n, m)-> { return n*m; },
    (n, m)-> { return n-m; }
    );
    computations.forEach((comp) -> System.out.println(comp.compute(10, 4)));

    (n, m) -> { return n+m; } does not invoke class IntSum. Instead it just adds two values as anonymous method.
    This line (n, m) -> { return n+m; } can be replace by (n, m) -> Integer::sum;

    How could one invoke an existing strategy rather than writing new strategy as you have written in this blog in form of lambda? Ideal way should be to reuse the simple or complex strategies and not to reinvent it.

    Like

    Reply
    1. Mohamed Sanaulla Post author

      I guess you need to read about:
      1. Functional Interfaces
      2. How to write Lambda expressions which are implementations for your Functional Interface.

      In the IntSum example, instead of creating an interface which implements Computation, we are just providing a Lambda expression.

      Like

      Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s