Backend Development 12 min read

Applying Strategy, Factory Method, and Builder Patterns in Spring for Reward Distribution

This article explains how to implement the Strategy, Factory Method, and Builder design patterns within a Spring application to create a flexible reward‑distribution system, providing detailed code examples, usage guidelines, and best‑practice considerations for each pattern.

Architecture Digest
Architecture Digest
Architecture Digest
Applying Strategy, Factory Method, and Builder Patterns in Spring for Reward Distribution

Design patterns, when applied correctly, make code simpler and more extensible; this article demonstrates how to use the Strategy, Factory Method, and Builder patterns in Spring through a reward‑distribution example.

1. Strategy Pattern – The pattern is illustrated by defining a PrizeSender interface with support() and sendPrize() methods, then providing three concrete implementations ( PointSender , VirtualCurrencySender , CashSender ) that handle different prize types.

public interface PrizeSender {
/** 判断当前实例是否支持当前奖励的发放 */
boolean support(SendPrizeRequest request);
/** 发放奖励 */
void sendPrize(SendPrizeRequest request);
}
// 积分发放
@Component
public class PointSender implements PrizeSender {
@Override
public boolean support(SendPrizeRequest request) {
return request.getPrizeType() == PrizeTypeEnum.POINT;
}
@Override
public void sendPrize(SendPrizeRequest request) {
System.out.println("发放积分");
}
}
// 虚拟币发放
@Component
public class VirtualCurrencySender implements PrizeSender {
@Override
public boolean support(SendPrizeRequest request) {
return PrizeTypeEnum.VIRTUAL_CURRENCY == request.getPrizeType();
}
@Override
public void sendPrize(SendPrizeRequest request) {
System.out.println("发放虚拟币");
}
}
// 现金发放
@Component
public class CashSender implements PrizeSender {
@Override
public boolean support(SendPrizeRequest request) {
return PrizeTypeEnum.CASH == request.getPrizeType();
}
@Override
public void sendPrize(SendPrizeRequest request) {
System.out.println("发放现金");
}
}

Key points: annotate implementations with @Component , provide a boolean support() method to decide applicability, keep business logic in sendPrize() , and pass a request object rather than primitive parameters to allow future extensions.

2. Factory Method Pattern – A factory class selects the appropriate PrizeSender based on the request. The factory iterates over all beans of type PrizeSender injected by Spring and returns the first that supports the request.

@Component
public class PrizeSenderFactory {
@Autowired
private List<PrizeSender> prizeSenders;
public PrizeSender getPrizeSender(SendPrizeRequest request) {
for (PrizeSender prizeSender : prizeSenders) {
if (prizeSender.support(request)) {
return prizeSender;
}
}
throw new UnsupportedOperationException("unsupported request: " + request);
}
}

The client obtains a PrizeSender from the factory and calls sendPrize() , decoupling the caller from concrete implementations. Adding a new prize type only requires a new PrizeSender bean.

@Service
public class ApplicationService {
@Autowired
private PrizeSenderFactory prizeSenderFactory;
public void mockedClient() {
SendPrizeRequest request = new SendPrizeRequest();
request.setPrizeType(PrizeTypeEnum.POINT);
PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request);
prizeSender.sendPrize(request);
}
}

3. Builder Pattern – The article shows a Spring‑managed builder for constructing SendPrizeRequest . The inner static Builder class is annotated with @Component and @Scope("prototype") so each request gets a fresh builder instance that can have state.

public class SendPrizeRequest {
private final PrizeTypeEnum prizeType;
private final int amount;
private final String userId;
public SendPrizeRequest(PrizeTypeEnum prizeType, int amount, String userId) {
this.prizeType = prizeType;
this.amount = amount;
this.userId = userId;
}
@Component
@Scope("prototype")
public static class Builder {
@Autowired
PrizeService prizeService;
private int prizeId;
private String userId;
public Builder prizeId(int prizeId) {
this.prizeId = prizeId;
return this;
}
public Builder userId(String userId) {
this.userId = userId;
return this;
}
public SendPrizeRequest build() {
Prize prize = prizeService.findById(prizeId);
return new SendPrizeRequest(prize.getPrizeType(), prize.getAmount(), userId);
}
}
// getters omitted
}

The builder is obtained from the Spring ApplicationContext to ensure a new prototype instance each time.

@Service
public class ApplicationService {
@Autowired
private PrizeSenderFactory prizeSenderFactory;
@Autowired
private ApplicationContext context;
public void mockedClient() {
SendPrizeRequest request = newPrizeSendRequestBuilder()
.prizeId(1)
.userId("u4352234")
.build();
PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request);
prizeSender.sendPrize(request);
}
public Builder newPrizeSendRequestBuilder() {
return context.getBean(Builder.class);
}
}

The article concludes that using these three patterns together yields a clean, extensible reward‑distribution module where new prize types can be added without modifying existing business logic.

design patternsJavaStrategy PatternSpringdependency injectionFactory MethodBuilder Pattern
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.