In this article, we will discuss the most widely used Factory Design Pattern implementation using Java 8 lambda expressions. This type of design pattern comes under the creational pattern as this pattern provides one of the best ways to create an object.
Let's look at the definition of the Factory Design Pattern: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
You can read more details about this pattern on The Factory Pattern from Head First Design Patterns.
In this article, I would like to give an example of the Factory design pattern and then rewrite the same example using the lambda expression to be introduced in Java 8.
Factory Design Pattern: Without Using Lambda Expressions
We're going to create a Shape interface and concrete classes implementing the Shape interface. Let's implement Factory Pattern step by step.
Step 1: Create an interface - Shape.java
package net.javaguides.corejava.factorypattern;
public interface Shape {
void draw();
}
Step 2: Create concrete classes implementing the same interface - Rectangle, Square, and Circle.
package net.javaguides.corejava.factorypattern;
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
package net.javaguides.corejava.factorypattern;
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
package net.javaguides.corejava.factorypattern;
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
Step 3: Create a ShapeType enum with predefined shapes like CIRCLE, RECTANGLE, and SQUARE.
package net.javaguides.corejava.factorypattern;
public enum ShapeType {
CIRCLE, RECTANGLE, SQUARE;
}
Step 4: Create a Factory to generate an object of concrete class based on given information -ShapeFactory.java
package net.javaguides.corejava.factorypattern;
public class ShapeFactory {
// use getShape method to get object of type shape
public Shape getShape(ShapeType shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equals(ShapeType.CIRCLE)) {
return new Circle();
} else if (shapeType.equals(ShapeType.RECTANGLE)) {
return new Rectangle();
} else if (shapeType.equals(ShapeType.SQUARE)) {
return new Square();
}
return null;
}
}
Step 5: Use the Factory to get an object of the concrete class by passing a type - FactoryPatternDemo.java
package net.javaguides.corejava.factorypattern;
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(ShapeType.CIRCLE);
// call draw method of Circle
shape1.draw();
// get an object of Rectangle and call its draw method.
Shape shape2 = shapeFactory.getShape(ShapeType.RECTANGLE);
// call draw method of Rectangle
shape2.draw();
// get an object of Square and call its draw method.
Shape shape3 = shapeFactory.getShape(ShapeType.SQUARE);
// call draw method of square
shape3.draw();
}
}
Output:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
That's all, we have created a Factory design pattern, now let's rewrite the same example using the lambda expression to be introduced in Java 8.
Factory Design Pattern: Refactor With Lambda Expressions
All the above steps remain the same, we will only be required to change the ShapeFactory and FactoryPatternDemo class using lambda expressions, functional interface and method references that's all.
In Java 8, we can refer to constructors just like we refer to methods, by using method references. For example, here’s how to refer to the Circle constructor:
Supplier<Shape> circleSupplier = Circle::new;
Circle circle = circleSupplier.get();
Using this technique, we could rewrite the previous code by creating a Map that maps a shape name to its constructor:
final static Map<ShapeType, Supplier<Shape>> map = new HashMap<>(); static { map.put(ShapeType.CIRCLE, Circle::new); map.put(ShapeType.RECTANGLE, Rectangle::new); map.put(ShapeType.SQUARE, Square::new); }
We can now use this Map to instantiate different shapes, just as we did with the previous factory code:
package net.javaguides.corejava.factorypattern;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ShapeFactory {
final static Map<ShapeType, Supplier<Shape>> map = new HashMap<>();
static {
map.put(ShapeType.CIRCLE, Circle::new);
map.put(ShapeType.RECTANGLE, Rectangle::new);
map.put(ShapeType.SQUARE, Square::new);
}
public Shape getShape(ShapeType shapeType) {
Supplier<Shape> shape = map.get(shapeType);
if (shape != null) {
return shape.get();
}
throw new IllegalArgumentException("No such shape " + shapeType.name());
}
}
Let's test above FactoryShape using FactoryPatternDemo class.
package net.javaguides.corejava.factorypattern;
import java.util.function.Supplier;
public class FactoryPatternDemo {
public static void main(String[] args) {
Supplier<ShapeFactory> shapeFactory = ShapeFactory::new;
// get an object of Circle and call its draw method.
Shape shape1 = shapeFactory.get().getShape(ShapeType.CIRCLE);
// call draw method of Circle
shape1.draw();
// get an object of Rectangle and call its draw method.
Shape shape2 = shapeFactory.get().getShape(ShapeType.RECTANGLE);
// call draw method of Rectangle
shape2.draw();
// get an object of Square and call its draw method.
Shape shape3 = shapeFactory.get().getShape(ShapeType.SQUARE);
// call draw method of square
shape3.draw();
}
}
Output:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
Note that we have used the Supplier functional interface and method references.
Learn complete Java 8 on Java 8 Tutorial
The source code of this article is available on my GitHub repository https://github.com/RameshMF/factory-pattern-java8
Comments
Post a Comment
Leave Comment