1. 策略模式
从本质上讲,策略模式就是一个接口下有多个实现类,而每种实现类会处理某一种情况。
我们以发奖励为例进行讲解,比如我们在抽奖系统中,有多种奖励方式可供选择,比如积分,现金等。在存储时,我们必然会使用一个类似于type的字段用于表征这几种发放奖励的,那么这里我们就可以使用多态的方式进行奖励的发放。比如我们抽象出一个PrizeSender的接口,其声明如下:
public interface PrizeSender {
/**
* 用于判断当前实例是否支持当前奖励的发放
*/
boolean support(SendPrizeRequest request);
/**
* 发放奖励
*/
void sendPrize(SendPrizeRequest request);
}
该接口中主要有两个方法:support()和sendPrize(),其中support()方法主要用于判断各个子类是否支持当前类型数据的处理,而sendPrize()则主要是用于进行具体的业务处理的,比如这里奖励的发放。下面就是我们三种不同类型的奖励发放的具体代码:
// 积分发放
@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 CashSender implements PrizeSender {
@Override
public boolean support(SendPrizeRequest request) {
return PrizeTypeEnum.CASH == request.getPrizeType();
}
@Override
public void sendPrize(SendPrizeRequest request) {
System.out.println("发放现金");
}
}
这里可以看到,在每种子类型中,我们只需要在support()方法中通过request的某个参数来控制当前request是否是当前实例能够处理的类型,如果是,则外层的控制逻辑就会将request交给当前实例进行处理。关于这个类的设计,有几个点需要注意:
-
使用@Component注解对当前类进行标注,将其声明为Spring容器所管理的一个bean;
-
声明一个返回boolean值的类似于support()的方法,通过这个方法来控制当前实例是否为处理目标request的实例;
-
声明一个类似于sendPrize()的方法用于处理业务逻辑,当然根据各个业务的不同声明的方法名肯定是不同的,这里只是一个对统一的业务处理的抽象;
-
无论是support()方法还是sendPrize()方法,都需要传一个对象进行,而不是简简单单的基本类型的变量,这样做的好处是后续如果要在Request中新增字段,那么就不需要修改接口的定义和已经实现的各个子类的逻辑;
2. 工厂方法模式
上面我们讲解了如何使用Spring来声明一个策略模式,那么如何为不同的业务逻辑来注入不同的bean呢,或者说外层的控制逻辑是什么样的,这里我们就可以使用工厂方法模式了。
所谓的工厂方法模式,就是定义一个工厂方法,通过传入的参数,返回某个实例,然后通过该实例来处理后续的业务逻辑。一般的,工厂方法的返回值类型是一个接口类型,而选择具体子类实例的逻辑则封装到了工厂方法中了。通过这种方式,来将外层调用逻辑与具体的子类的获取逻辑进行分离。如下图展示了工厂方法模式的一个示意图:
(通过Factory获取具体的实例:beanA,BeanB,BeanC...)
可以看到,工厂方法将具体实例的选择逻辑进行了封装,而客户端,也就是我们的调用方只需要调用工厂的方法获取到具体的实例即可,而不需要管如何拿到这个具体实例的,也不用管具体的实例的实现是什么。
上面我们讲解了Spring中是如何使用策略模式声明处理逻辑的,而没有讲如何选择具体的策略,这里我们就可以使用工厂方法模式。
如下是我们声明的一个PrizeSenderFactory:
@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);
}
}
这里我们声明一个了一个工厂方法getPrizeSender(),其入参就是SendPrizeRequest,而返回值是某个实现了PrizeSender接口的实例,可以看到,通过这种方式,我们将具体的选择方式下移到了具体的子类中的,因为当前实现了PrizeSender的bean是否支持当前request的处理,是由具体的子类实现的。
在该工厂方法中,我们也没有任何与具体子类相关的逻辑,也就是说,该类实际上是可以动态检测新加入的子类实例的。这主要是通过Spring的自动注入来实现的,主要是因为我们这里注入的是一个List,也就是说,如果有新的PrizeSender的子类实例,只要其是Spring所管理的,那么都会被注入到这里来。下面就是我们编写的一段用于测试的代码来模拟调用方的调用:
@Service
public class ApplicationService {
@Autowired
private PrizeSenderFactory prizeSenderFactory;
public void testDemo() {
SendPrizeRequest request = new SendPrizeRequest();
request.setPrizeType(PrizeTypeEnum.POINT); // 这里的request一般是根据数据库或外部调用来生成的
PrizeSender prizeSender = prizeSenderFactory.getPrizeSender(request);
prizeSender.sendPrize(request);
}
}
在客户端代码中,首先通过PrizeSenderFactory获取一个PrizeSender实例,然后通过其sendPrize()方法发放具体的奖励,通过这种方式,将具体的奖励发放逻辑与客户端调用进行了解耦。而且根据前面的讲解,我们也知道,如果新增了一种奖励方式,我们只需要声明一个新的实现了PrizeSender的bean即可,而不需要对现有代码进行任何修改。
refer:https://mp.weixin.qq.com/s/XQDZ9M0qLBz0xo-7O6t2oA