JavaUnderstanding Lambda-enabled Design Patterns

Understanding Lambda-enabled Design Patterns

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

With the recent introduction of Lambda expression, the chemistry of Java code is now amalgamated with a new chemical bonding. Code has become terse and compact. Programmers now have an additional way of implementing their code in a lambda way. I do not think it is always easy to find the right lambda expression to replace your existing code; rather, it may seem cryptic at first glance. But, once you get the idea behind it through various examples, you will surely appreciate such terseness. Here in this article, I’ll try to implement some of them from the perspective of a couple of standard design patterns.

The Chemistry of the Lambda Expression and Design Pattern

First, you have to spot a problem and then look out for a standard pattern that appropriately signifies the solution. This is easier said than done, especially in complex circumstances. One way it can be done is through the divide and conquer phenomenon. Break the problem into as many sub problems as possible. Now, use those smaller parts, take the pattern, and apply it to your situation. This is for a pattern; now, where does lambda expression fit in? If design pattern is the structure of a human body, lambda expression can be used as the fluid (Java code) to run through its veins. So, inject lambda in the code to implement old standard design patterns in a new way. The idea is quite simple. You know design patterns, you know lambda expressions; now, mix and match them in the appropriate circumstances.

The Lambda-enabled Command Pattern

The command pattern is appropriate in the situation where an object requester does not have any idea about the requested operation or the receiver of the request. Here, Command is an interface for executing an operation. BoldCommand, ItalicCommand, and UnderlineCommand are its concrete implementation. Editor is the invoker that invokes the command to carry out the request. Document is the concrete implementation of the Editor. Only Receiver knows how to perform the operation associated with the request. This is the basic structure of our example that the code will follow.

Lambda1
Figure 1: Command Pattern Structure

Let’s first implement it in Java code and try to replace some code snippets with lambda expression later. So, the Document editor has some operation upon it, such as given in the Editor interface. These operations would be called from the receiver object called Macro. The use of the Macro class is that we will record some editor operation and execute it later as a single batch operation.

public interface Editor {
   public void bold();
   public void italic();
   public void underline();
}

Document is the concrete implementation class of the Editor where we define the operation details.

public class Document implements Editor {
   @Override
   public void bold() {
      System.out.println("Bold text...");
   }
   @Override
   public void italic() {
      System.out.println("Italic text...");
   }
   @Override
   public void underline() {
      System.out.println("Underline text...");
   }
}

All concrete command classes should implement the Command interface as shown in BoldCommand, ItalicCommand, and UnderlineCommand.

public interface Command {
   public void execute();
}

public class BoldCommand implements Command{

   private final Editor editor;

   public BoldCommand(Editor editor){
      this.editor=editor;
   }

   @Override
   public void execute() {
      editor.bold();
   }

}

public class ItalicCommand implements Command{
   private final Editor editor;

   public ItalicCommand(Editor editor){
      this.editor=editor;
   }

   @Override
   public void execute() {
      editor.italic();
   }
}

public class UnderlineCommand implements Command{
   private final Editor editor;

   public UnderlineCommand(Editor editor){
      this.editor=editor;
   }

   @Override
   public void execute() {
      editor.underline();
   }
}

Macro is the receiver class where we record commands in a list and execute those commands in one go when the runMacro() function is called.

public class Macro {
   private final List<Command> commands = new ArrayList<>();
   public void recordMacro(Command action) {
      commands.add(action);
   }
   public void runMacro() {
       commands.forEach(Command::execute);
   }
}

This is the common and simple way we do this process in Java without using any lambda expression.

public class LamdaCommandPattern {
   public static void main(String[] args) {
      Document writer=new Document()
      Macro macro = new Macro();

      macro.recordMacro(new BoldCommand(writer));
      macro.recordMacro(new ItalicCommand(writer));
      macro.recordMacro(new UnderlineCommand(writer));
      macro.runMacro();
   }
}

Now, to introduce a lambda expression; we can implement it as follows. Because our command classes are basically blocks of behavior, the code snippets can be best expressed with the help of lambda. Observe how the code becomes a lot simpler and more intuitive.

      macro.recordMacro(() -> writer.bold());
      macro.recordMacro(() -> writer.italic());
      macro.recordMacro(() -> writer.underline());
      macro.runMacro();

In fact, we can do even better by using method references to wire the commands to the macro object.

      macro.recordMacro(writer::bold);
      macro.recordMacro(writer::italic);
      macro.recordMacro(writer::underline);
      macro.runMacro();

Lambda-enabled Strategy Pattern

A strategy pattern is appropriate when the algorithmic behavior changes dynamically and we need different variants of an algorithm. These variants are implemented as a class hierarchy of the algorithm. When classes define behavior and they appear as multiple conditional statements in its operation, what we can do is create its own strategy class instead of using many conditional branches. However, implementing a strategy pattern may differ considerably because it depends upon circumstantial needs. But, in any case, the conceptual framework is that one common problem is solved by different algorithms, and we have to encapsulate those algorithms behind a common programming interface. That’s it.

An example we will consider is commonly found any real-time strategy game (such as Age of Empire). Each soldier in the game has its own unique stance strategy, which can be either aggressive or defensive. This stance strategy is uniquely defined by the set of rules in the appropriate classes.

Lambda2
Figure 2: Strategy Pattern Structure

SoldierStrategy is the base interface that should be implemented by any class that wants to change the behavior of the Soldier. Here, there are two concrete implementations of this behavior, such as Aggressive strategy and Defensive strategy.

public interface SoldierStrategy {
   public void stance();
}


public class Aggresive implements SoldierStrategy {

   @Override
   public void stance() {
      System.out.println("Attack enemy on sight...");
   }

}


public class Defensive implements SoldierStrategy {

   @Override
   public void stance() {
      System.out.println("Attack only when attacked...");
   }

}

The Soldier class is the context where we use our strategy. Here, type defines the type of soldiers and strategy defines the specific strategical stance we want to set for the particular soldier.

public class Soldier {

   private final SoldierStrategy strategy;
   private String type;

   public Soldier(String type, SoldierStrategy strategy) {
      this.type=type;
      this.strategy = strategy;
   }

   public String getType(){
      return type;
   }

   public void strategyStatus() {
      strategy.stance();
   }
}

This is the traditional Java code to implement the pattern: Create a soldier object with the type and its strategic stance.

public class LambdaStrategyPattern {

   public static void main(String[] args) {
      Soldier pikeman=new Soldier("Pikeman", new Defensive());
      pikeman.strategyStatus();
      Soldier militia=new Soldier("Militia", new Aggresive());
      militia.strategyStatus();
   }

}

As we have been tweaking, we can tinge the code with an appropriate lambda expression. Here, we remove each concrete strategy implementation and refer to the method that implements the algorithm. Because algorithms are represented by the constructors, we can administrate Aggressive and Defensive strategies by using method references as follows.

      Soldier archer=new Soldier("Archer", Defensive::new);
      archer.strategyStatus();
      Soldier knight=new Soldier("Knight", Aggresive::new);
      knight.strategyStatus();

Conclusion

These are just examples to give a hint or direction on how to tweak your code to lambda expression from the perspective of design pattern. In the same manner, other design patterns can be tweaked as well. In fact, lambda is a way of expression and can be used in any way you like or deem fit. But, there are code and circumstances where lambda expresses better than traditional Java code. You can enrich your code only if you embrace lambda. The interesting thing about life and Java is that everything is optional. What the code and you will be depends on the choices you make 😉

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories