Factory Design Pattern using Spring Boot

Vipul Kumar
5 min readMar 20, 2023

--

In this guide, we will be implementing Factory Design Pattern using the spring-boot.

Factory Design Pattern using spring-boot
Photo by Patrick Hendry on Unsplash

What is a Design Pattern?

Design patterns are specific solutions to common problems in software design. There are multiple kinds of design patterns available and here we are going to discuss one of the Creational Design Patterns which is the Factory Design Pattern.

What is a Factory Design Pattern?

Factory Design Pattern is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created. You can find more details here.

The Usecase

In the guide we will take the exapmle of Payment Provider(PayU and Paypal) and follwoing are the objectives we would like to achieve-

  1. Create PaymentProviderFactory to create payment different type of payment providers based on client need.
  2. Accept Payment(a dummy implementation) using the payment provider created in the 1st step.
  3. Decouple PaymentProvider creation logic from normal business logic.

How to implement Factory Design Pattern in Spring Boot?

In this guide, we will go step by step and implement Factory Design Pattern using spring boot.

Step 1: Create the project

The 1st step is to initialize your project using Spring Boot Initializer. We only need Lombok as a dependency to simplify our logging. You can use this link to initialize your project with the config user in this guide. Click on Generate, it will download the initial project. Import it into your favorite IDE. I’ll be using IntelliJ IDEA you can use your choice of IDE.

Step 2: Configuring spring boot application run some code on application startup

There are multiple ways to do this but here we will be using the CommandLineRunner approach.

Open FactoryDesignPatternApplication class and extend it by CommandLineRunner interface and override run(). Your class will look like below

@Slf4j
@SpringBootApplication
public class FactoryDesignPatternApplication implements CommandLineRunner {

public static void main(String[] args) {
SpringApplication.run(FactoryDesignPatternApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
log.info("Hello spring boot");
}
}

Try running your application to verify if you get Hello spring boot in the last line-


. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.4)

2023-03-20T15:56:43.056+05:30 INFO 99547 --- [ main] c.v.p.f.FactoryDesignPatternApplication : Starting FactoryDesignPatternApplication using Java 17.0.5 with PID 99547 (/Users/vipulkumar/code/factory-design-pattern/target/classes started by vipulkumar in /Users/vipulkumar/code/factory-design-pattern)
2023-03-20T15:56:43.058+05:30 INFO 99547 --- [ main] c.v.p.f.FactoryDesignPatternApplication : No active profile set, falling back to 1 default profile: "default"
2023-03-20T15:56:43.306+05:30 INFO 99547 --- [ main] c.v.p.f.FactoryDesignPatternApplication : Started FactoryDesignPatternApplication in 0.524 seconds (process running for 0.763)
2023-03-20T15:56:43.307+05:30 INFO 99547 --- [ main] c.v.p.f.FactoryDesignPatternApplication : Hello spring boot

Process finished with exit code 0

Step 3: Implementing Payment Providers

Lets start with PaymentProvider which will be an interface with a single acceptPayment() method which will act as a super class for all kind of payment providers.

package com.vksviit.patterns.factorydesignpattern;


public interface PaymentProvider {
public void acceptPayment();
}

Now, its time to create the implementation calsses for PaymentProvider imterface. Here we will create three implementations as follows — Stripe, PayPal and PayU.

package com.vksviit.patterns.factorydesignpattern;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service(StripePaymentProvider.BEAN_ID)
@Slf4j
public class StripePaymentProvider implements PaymentProvider {
public static final String BEAN_ID = "stripePaymentProvider";

@Override
public void acceptPayment() {
log.info("Accepting payment using Stripe");
}
}

In the above sample we have done multiple things —

  1. Extending the class using PaymentProvider interface.
  2. Annotated the class with @Service and given it a a unique BEAN_ID.
  3. Annotated the class with @Slf4j annotation to use log.info() line.
  4. Finnaly overriden acceptPayment() method with a simple logging line.

Following the same above approach, Lets create 2 more implementation of PaymentProvider interface.

package com.vksviit.patterns.factorydesignpattern;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service(PayPalPaymentProvider.BEAN_ID)
@Slf4j
public class PayPalPaymentProvider implements PaymentProvider {
public static final String BEAN_ID = "payPalPaymentProvider";

@Override
public void acceptPayment() {
log.info("Accepting payment using PayPal");
}
}

Nothing much changed here apart from class name, BEAN_ID value and logging line.

Now the last implementation for PayU. And here it is —

package com.vksviit.patterns.factorydesignpattern;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service(PayUPaymentProvider.BEAN_ID)
@Slf4j
public class PayUPaymentProvider implements PaymentProvider {
public static final String BEAN_ID = "payUPaymentProvider";

@Override
public void acceptPayment() {
log.info("Accepting payment using PayU");
}
}

Step 4: Implementing a factory using Spring Boot

With the beauty of spring framework creating a factory for the above implementation is very simple you just have write following lines —

package com.vksviit.patterns.factorydesignpattern;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class PaymentProviderFactory {
private final Map<String, PaymentProvider> paymentProviderMap;

public PaymentProvider getPaymentProvider(String paymentProviderType) {
return paymentProviderMap.get(paymentProviderType);
}
}

And spring will magicily inject all the instances of PaymentProvider into paymentProviderMap with BEAN_ID of respective @Service as key and actual instance as value. Now, we can use getPaymentProvider() by passing paymentProviderType as BEAN_ID of respective implementations and use the returned instance to call methods present in theie respective implementations.

Note: With @RequiredArgsConstructor we are using constructor injection for paymentProviderMap.

Step 5: Using Factory to get specific implementations

This the last step. Lets goto our FactoryDesignPatternApplication class and modifiy it to use PaymentProviderFactory to accpet payment —

package com.vksviit.patterns.factorydesignpattern;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@Slf4j
@SpringBootApplication
public class FactoryDesignPatternApplication implements CommandLineRunner {

@Autowired
PaymentProviderFactory paymentProviderFactory;

public static void main(String[] args) {
SpringApplication.run(FactoryDesignPatternApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
log.info("Hello spring boot");

// Provides StripePaymentProvider instance usage the same to accept payment
paymentProviderFactory.getPaymentProvider(StripePaymentProvider.BEAN_ID).acceptPayment();

// Provides PayPalPaymentProvider instance usage the same to accept payment
paymentProviderFactory.getPaymentProvider(PayPalPaymentProvider.BEAN_ID).acceptPayment();

// Provides PayUPaymentProvider instance usage the same to accept payment
paymentProviderFactory.getPaymentProvider(PayUPaymentProvider.BEAN_ID).acceptPayment();

}
}

Few points to consider here —

  1. We have injected PaymentProviderFactory using @Autowired.
  2. Using StripePaymentProvider.BEAN_ID, PayPalPaymentProvider.BEAN_ID and PayUPaymentProvider.BEAN_ID to refer the specific implementation of PaymentProvider implementation.

Step 6: Run the application to see the results in action

Run the application and you should be able to see the below output —


. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.4)

2023-03-20T16:56:55.911+05:30 INFO 4380 --- [ main] c.v.p.f.FactoryDesignPatternApplication : Starting FactoryDesignPatternApplication using Java 17.0.5 with PID 4380 (/Users/vipulkumar/code/factory-design-pattern/target/classes started by vipulkumar in /Users/vipulkumar/code/factory-design-pattern)
2023-03-20T16:56:55.913+05:30 INFO 4380 --- [ main] c.v.p.f.FactoryDesignPatternApplication : No active profile set, falling back to 1 default profile: "default"
2023-03-20T16:56:56.166+05:30 INFO 4380 --- [ main] c.v.p.f.FactoryDesignPatternApplication : Started FactoryDesignPatternApplication in 0.442 seconds (process running for 0.626)
2023-03-20T16:56:56.167+05:30 INFO 4380 --- [ main] c.v.p.f.FactoryDesignPatternApplication : Hello spring boot
2023-03-20T16:56:56.167+05:30 INFO 4380 --- [ main] c.v.p.f.StripePaymentProvider : Accepting payment using Stripe
2023-03-20T16:56:56.167+05:30 INFO 4380 --- [ main] c.v.p.f.PayPalPaymentProvider : Accepting payment using PayPal
2023-03-20T16:56:56.167+05:30 INFO 4380 --- [ main] c.v.p.f.PayUPaymentProvider : Accepting payment using PayU

Process finished with exit code 0

Last 3 lines in these logs are very important which is getting printed from the log.info() lines which we printed in Specific implementations of PaymentProvider.

That takes to the end of this guide. Hope you loved it and will be using it in your code. You can find the source code of this guide on my github account.

--

--

Vipul Kumar
Vipul Kumar

Written by Vipul Kumar

A passionate software developer working on java, spring-boot and related technologies for more than 4 years.

Responses (4)