221. 策略模式是一个非常简单且非常实用的一种设计模式
策略模式的核心是选择,动态切换 ,通过条件拿到相应的策略 () , 本质上是对行为的抽象
2. 示例演示 - 定义不同的玩家客服和选择逻辑
2.1 起一个有意义的接口名字时更能反映出接口名字相应的内容
java
package org.example.service;
// 客服的通用接口
public interface CustomerService {
String findCustomer();
}
2.2 定义四种不同的客服的实现,然后都交给Spring容器管理
java
import org.example.controller.SupportUserType;
import org.springframework.stereotype.Service;
@Service
@SupportUserType(CustomerType.BIG)
public class BigCustomerService implements CustomerService {
@Override
public String findCustomer() {
return "BigCustomerService";
}
}
import org.example.controller.SupportUserType;
import org.springframework.stereotype.Service;
@Service
@SupportUserType(CustomerType.SUPER)
public class SuperRCustomerService implements CustomerService {
@Override
public String findCustomer() {
return "SuperRCustomerService";
}
}
import org.springframework.stereotype.Component;
@Service
public class DefaultCustomService implements CustomerService{
@Override
public String findCustomer() {
return "没有找到客服";
}
}
import org.example.controller.SupportUserType;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
@Service
//@Order(4)
@SupportUserType(CustomerType.NORMAL)
public class NormalCustomerService implements CustomerService {
@Override
public String findCustomer() {
return "NormalCustomerService";
}
}
import org.springframework.stereotype.Component;
@Component
public class DefaultCustomService implements CustomerService{
@Override
public String findCustomer() {
return "没有找到客服";
}
}
2.3 定义枚举类 每种不同的客服对应不同的金额,使用函数式接口判断不同的类型。不同的金额对应的类型不同。
java
import java.util.function.IntPredicate;
// 定义 客服类型的一个枚举 字符串
public enum CustomerType {
NORMAL(e-> e>0 && e<1000),
BIG(e-> e>=1000 && e<2000),
SMAIL(e-> e>=2000 && e<3000),
SUPER(e-> e>=3000 && e<4000);
private final IntPredicate support;
CustomerType( IntPredicate support ) {
this.support = support;
}
public static CustomerType typeOf( int reChange ) {
for (CustomerType value : values()) {
if(value.support.test( reChange )) {
return value;
}
}
return null;
}
}
2.4 创建枚举,对每种客服的实现类进行标识。
java
import org.example.service.CustomerType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportUserType {
CustomerType value();
}
2.2 代码有具体实现,亦如下
java
import org.example.controller.SupportUserType;
import org.springframework.stereotype.Service;
@Service
@SupportUserType(CustomerType.BIG)
public class BigCustomerService implements CustomerService {
@Override
public String findCustomer() {
return "BigCustomerService";
}
}
2.5 通过反射的方式都文件进行解析和加载,在analystType方法中加上@Resource注解,在Spring容器初始化阶段 ,将解析到的类型和类型对应的实现放入到一个map中,当用户请求相应的controller接口时,就会将金额解析成为对应的枚举,并通过枚举拿到对应的实例对象,通过对象拿到相应的方法,当然当用户输入的金额是非法无法匹配时则直接会返回一个默认的对象。
java
import jakarta.annotation.Resource;
import org.example.service.CustomerService;
import org.example.service.CustomerType;
import org.example.service.DefaultCustomService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/customer")
public class CustomerController {
private Map<CustomerType,CustomerService> map;
// 手动注入
@Resource
private DefaultCustomService defaultCustomService;
@GetMapping("/customer/{reChange}")
public String controller(@PathVariable(value = "reChange") Integer reChange) {
CustomerType customerType = CustomerType.typeOf(reChange);
CustomerService customerService = map.getOrDefault(customerType , defaultCustomService);
return customerService.findCustomer();
}
// Spring 的 Bean 容器注入时就会执行该方法
@Resource
public void analystType(List<CustomerService> customerServices ) {
map = customerServices.stream()
.filter(customerService -> customerService.getClass().isAnnotationPresent(SupportUserType.class))
.collect(Collectors.toMap(this::findCustomerType, Function.identity()));
// 策略和处理器的数量的一致性
if (this.map.size() != CustomerType.values().length ) {
throw new IllegalStateException("Customer type count mismatch 由用户类型没有对应的策略");
}
}
private CustomerType findCustomerType(CustomerService customerService) {
SupportUserType annotation = customerService.getClass().getAnnotation(SupportUserType.class);
return annotation.value();
}
}