Java 8 Lambda-Enabled Design patterns

Ranjith Raj D
3 min readMay 28, 2020

--

Overview

Java8 is providing a new way to redesign our existing code into a simple and more elegant one. The good old days (before java 8) of java version need more code to express even simple design patterns. In many cases, the language changes in Java 8 are the driving factor behind the thinking change. We have our new boss in our town Java 14; Still, Usage of Java 8 needs more fluent then now.

The critical design tool for software development is a mind well educated in design principles. It is not technology.

— Craig Larman

Java 8 highlights

Java 8 has several features added and improved as it is a major release, I have listed a few here.

  • Streams
  • Lambdas
  • Functional Interface
  • Consumer / Supplier

More reading about features refer to Release notes

Lambda

I am going to drive you through to rewrite a few of well-known Gang of four design patterns. Presumably, this would be useful to get a shift in our thinking to write better implementation.

Builder and Factory design patterns are more popular ones under the Creational design pattern. I am taking these two patterns to showcase Consumer & Supplier function’s practical usage. Usage of lambda expression not just limited to these design patterns, if there is good understanding then changes can be applied to all other patterns too. Lambdas are basically lazy, so it is good to learn and use them in an appropriate place. Of course, no practice is ever the best practice forever.

Note :

  • The factory design pattern is written using java8 Supplier
  • The builder design pattern is written using java 8 Consumer

Refer design pattern in java implementation

Lambdas & Functional Interfaces are another belt of a professional Java developer, no different from an interface or a class.

Let’s look at a concrete example of the Builder pattern and see how it improves with lambda expressions

Builder design pattern

Concrete example

package com.ran;class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}class PersonBuilder { private String firstName;
private String lastName;
public PersonBuilder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public PersonBuilder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
public Person build() {
return new Person(firstName, lastName);
}
}public class BuilderPattern {
public static void main(String[] args) {
Person person = new PersonBuilder().withFirstName("Super").withLastName("Hero").build();
}
}

The above code is valid, but there is morewith** method need to be added in the builder class when new properties introduced. Lambda solves this problem and provides as Groovy like syntax to java builder pattern.

Remove withFirstName,withLastName and replace the below code in the PersonBuilder class and change builder properties to the public. This saves a lot of lines in the builder class.

public PersonBuilder with(
Consumer<PersonBuilder> builderFunction) {
builderFunction.accept(this);
return this;
}

Final code

package com.ran;import java.util.function.Consumer;import java.util.function.Consumer;class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
class PersonBuilder {
public String firstName;
public String lastName;

public PersonBuilder with(
Consumer<PersonBuilder> builderFunction) {
builderFunction.accept(this);
return this;
}
public Person build() {
return new Person(firstName,lastName);
}

}
public class BuilderPattern{
public static void main(String[] args) {
Person person = new PersonBuilder()
.with($ -> $.firstName = "Super")
.with($ -> $.lastName = "hero")
.build();
}
}

Factory design pattern

The factory design pattern is written using lambda

package com.ran;import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
enum OS {
ANDROID, IOS;
}
interface Mobile {}class Android implements Mobile {}class IOS implements Mobile {}class MobileFactory {

static final Map<OS, Supplier<Mobile>> map = new EnumMap<>(OS.class); //Enum map

// Store references of different OS
static {
map.put(OS.ANDROID, Android::new); // :: method reference operator
map.put(OS.IOS, IOS::new);
}
public Mobile getMobile(OS type) {
return map.get(type).get();
}
}
public class FactoryPattern{
public static void main(String[] args) {
MobileFactory mobileFacotry = new MobileFactory();
Mobile mobile = null;
mobile = mobileFacotry.getMobile(OS.ANDROID);
mobile = mobileFacotry.getMobile(OS.IOS);

System.out.println(mobile.getClass().getName());
}
}

P.S: I did not go deep dive into what is Lambdas, Function, Consumer, Supplier, Method reference operator, Referential transparency, and so on. Leaving it the readers to learn more about it.

Refer below link for more reading :

Book : Java 8 Lambdas by Richard Warburton Link : Chapter 8. Design and Architectural Principles

--

--

Ranjith Raj D
Ranjith Raj D

Written by Ranjith Raj D

Software Architect ✦ Full stack developer ✦ Artist ✦ Autodidact ✦ Codeaholic ✦ https://www.linkedin.com/in/ranjithrajd/

No responses yet