【91期】面试官:Spring 用了哪些设计模式?说三种即可
阅读本文大概需要 7 分钟。
来自:网络
1. 策略模式
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("发放现金");
}
}
使用@Component注解对当前类进行标注,将其声明为Spring容器所管理的一个bean; 声明一个返回boolean值的类似于support()的方法,通过这个方法来控制当前实例是否为处理目标request的实例; 声明一个类似于sendPrize()的方法用于处理业务逻辑,当然根据各个业务的不同声明的方法名肯定是不同的,这里只是一个对统一的业务处理的抽象; 无论是support()方法还是sendPrize()方法,都需要传一个对象进行,而不是简简单单的基本类型的变量,这样做的好处是后续如果要在Request中新增字段,那么就不需要修改接口的定义和已经实现的各个子类的逻辑;
2. 工厂方法模式
@Component
public class PrizeSenderFactory {
@Autowired
private ListprizeSenders;
public PrizeSender getPrizeSender(SendPrizeRequest request) {
for (PrizeSender prizeSender : prizeSenders) {
if (prizeSender.support(request)) {
return prizeSender;
}
}
throw new UnsupportedOperationException("unsupported request: " + request);
}
}
@Service
public class ApplicationService {
@Autowired
private PrizeSenderFactory prizeSenderFactory;
public void mockedClient() {
SendPrizeRequest request = new SendPrizeRequest();
request.setPrizeType(PrizeTypeEnum.POINT); // 这里的request一般是根据数据库或外部调用来生成的
PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request);
prizeSender.sendPrize(request);
}
}
3. Builder模式
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);
}
}
public PrizeTypeEnum getPrizeType() {
return prizeType;
}
public int getAmount() {
return amount;
}
public String getUserId() {
return userId;
}
}
在Builder类上必须使用@Scope注解来标注该实例为prototype类型,因为很明显,我们这里的Builder实例是有状态的,无法被多线程共享; 在Builder.build()方法中,我们可以通过传入的参数和注入的bean来进行一定的业务处理,从而得到构建一个SendPrizeRequest所需要的参数; Builder类必须使用static修饰,因为在Java中,如果内部类不用static修饰,那么该类的实例必须依赖于外部类的一个实例,而我们这里本质上是希望通过内部类实例来构建外部类实例,也就是说内部类实例存在的时候,外部类实例是还不存在的,因而这里必须使用static修饰; 根据标准的Builder模式的使用方式,外部类的各个参数都必须使用final修饰,然后只需要为其声明getter方法即可。
@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);
}
}
4. 小结
【90期】面试官:说一下使用 Redis 实现大规模的帖子浏览计数的思路
【89期】面试官 5 连问一个 TCP 连接可以发多少个 HTTP 请求?
【88期】面试官问:你能说说 Spring 中,接口的bean是如何注入的吗?
微信扫描二维码,关注我的公众号
朕已阅