Implement the factory pattern using Lambda expressions in Java 8

  • 2020-06-19 10:15:12
  • OfStack

preface

The factory pattern is one of the most familiar design patterns in object-oriented design patterns. The traditional implementations are familiar, but today I'll introduce you to a more elegant implementation of the factory pattern using Java8 Lambda expressions.

The cover

The factory pattern, one of the most commonly used design patterns in java, provides a good way to instantiate objects and is a common alternative to the new pattern. Factory design patterns allow you to instantiate object logic without exposing it to clients.

In the following article I will give an example of implementing the factory pattern using traditional code and then re-implementing it using Java8 Lambada

One example

I will first create an Shape interface and several implementation classes, then implement ShapeFactory in the next steps, and finally give a detailed invocation instance and output.

New interface: ES29en.java


public interface Shape {
 void draw();
}

Define two implementation classes for Shape: ES35en.java & Rectangle.java


public class Rectangle implements Shape {
 @Override
 public void draw() {
  System.out.println("Inside Rectangle::draw() method.");
 }
}
public class Circle implements Shape {
 @Override
 public void draw() {
  System.out.println("Inside Circle::draw() method.");
 }
}

Create a factory class for Shape and implement a factory method that returns a different Shape based on the specified parameters:


public class ShapeFactory {
 //use getShape method to get object of type shape 
 public Shape getShape(String shapeType){
  if(shapeType == null){
   return null;
  }
  if(shapeType.equalsIgnoreCase("CIRCLE")){
   return new Circle();
  } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
   return new Rectangle();   
  }  
  return null;
 }
}

ShapeFactory returns a different Shape based on the incoming shapeType.

Here's an example of how to use it.


public class FactoryPatternDemo {
 public static void main(String[] args) {
  ShapeFactory shapeFactory = new ShapeFactory();
  //get an object of Circle and call its draw method.
  Shape shape1 = shapeFactory.getShape("CIRCLE");
  //call draw method of Circle
  shape1.draw();
  //get an object of Rectangle and call its draw method.
  Shape shape2 = shapeFactory.getShape("RECTANGLE");
  //call draw method of Rectangle
  shape2.draw();
 }
}

Program output


Inside Circle::draw() method.
Inside Rectangle::draw() method.

Implement the factory pattern using Lambada

The Lambda expression allows us to define an anonymous method and to use it as a functional interface. We also want to be able to implement the same features on existing methods.

Method references have the same properties as lambda expressions (for example, they both need a target type and need to be converted to an instance of a functional interface), but we don't need to provide a method body for a method reference, we can refer to existing methods directly by method name.

The following example shows a constructor reference


Supplier circleSupplier = Circle::new;
Circle circle = circleSupplier.get();

According to the principle of constructor reference, we can rewrite the previous code and define 1 Map to hold shape name and its corresponding constructor reference:


final static Map<String, Supplier> map = new HashMap<>();
 static {
 map.put("CIRCLE", Circle::new);
 map.put("RECTANGLE", Rectangle::new);
 }

Now we can use this map to instantiate different shapes


public class ShapeFactory {
 final static Map<String, Supplier> map = new HashMap<>();
 static {
 map.put("CIRCLE", Circle::new);
 map.put("RECTANGLE", Rectangle::new);
 } 
 public Shape getShape(String shapeType){
  Supplier shape = map.get(shapeType.toUpperCase());
  if(shape != null) {
  return shape.get();
  }
  throw new IllegalArgumentException("No such shape " + shapeType.toUpperCase());
 }
}

Use factory methods implemented with lambada expressions to create shape objects:

FactoryPatternDemo.java


public class FactoryPatternDemo {
 public static void main(String[] args) {
  Supplier shapeFactory = ShapeFactory::new;
  //call draw method of circle
  shapeFactory.get().getShape("circle").draw();
  //call draw method of Rectangle
  shapeFactory.get().getShape("rectangle").draw();  
 }
}

Program output


Inside Circle::draw() method.
Inside Rectangle::draw() method.

Here Shape::new can be seen as a shorthand for lambda expressions. Although the variable method reference (as in this example) makes the syntax more compact, it has more explicit semantics -- if the method we want to call has a name, we can call it directly by its name.

If the Shape constructor takes multiple arguments, then you need to re-implement your own Supplier

Such as:


public class Rectangle implements Shape {
 @Override
 public void draw() {
  System.out.println("Inside Rectangle::draw() method.");
 }
}
public class Circle implements Shape {
 @Override
 public void draw() {
  System.out.println("Inside Circle::draw() method.");
 }
}
0

conclusion


Related articles: