策略模式
策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装成独立的类,使得它们可以互相替换。策略模式使得算法的变化独立于使用算法的客户端。
策略模式由三个部分组成:策略接口、具体策略类和环境类。策略接口定义了所有具体策略类都需要实现的方法;具体策略类实现了策略接口,并提供不同的算法实现;环境类持有一个策略对象,并利用策略对象执行具体的算法。
策略模式适用情况
-
需要根据不同的条件或参数选择不同的算法或策略。例如,在一个电商网站中,根据用户的购买历史、地理位置等信息,选择不同的优惠策略或物流配送策略。
-
需要封装一组相似的算法,以便在不同的情况下使用不同的算法。例如,在一个游戏中,根据不同的敌人类型和玩家状态选择不同的战斗策略。
-
有一组类似的行为,但是可能会发生变化。通过将每个行为封装为一个具体策略类,可以更灵活地添加、删除或修改行为。例如,在一个日程管理应用中,可以根据用户的不同需求设置不同的提醒策略。
-
不希望暴露复杂的条件语句或逻辑判断代码。使用策略模式可以将条件语句封装在具体策略类中,使得代码更加清晰、易于理解和维护。
总的来说,策略模式适用于需要根据不同的条件选择不同算法或策略,并且希望将算法或策略的实现与使用代码解耦的情况。它能够提供灵活性、扩展性和可维护性,使得代码更具可读性和可重用性。
工厂模式
工厂模式是一种创建型设计模式,它提供了一种创建对象的接口,但具体的对象创建过程由子类或子工厂类来决定。通过使用工厂模式,可以将对象的创建和使用解耦,使得代码更加灵活、可扩展和易于维护。
工厂模式适用情况
- 当需要创建复杂对象或对象之间存在复杂的依赖关系时,可以使用工厂模式来封装对象的创建过程,并隐藏创建细节。
- 当需要创建的对象可能会随着程序的运行时条件变化而变化时,可以使用工厂模式来动态地选择合适的对象创建方式。
- 当希望将对象的创建逻辑集中在一处,以便于维护和管理时,可以使用工厂模式来统一对象的创建过程。
策略 + 工厂模式优化
一、利用 Spring 自动注入
支付接口 IPay 和具体实现类 AliPay
、WeiXinPay
、YiBaoPay
public interface IPay {
String pay();
}
@Service
@Slf4j
public class AliPay implements IPay {
@Override
public String pay() {
log.info("===调用支付宝接口发起支付业务===");
return "调用支付宝接口发起支付业务";
}
}
@Service
@Slf4j
public class WeiXinPay implements IPay {
@Override
public String pay() {
log.info("===调用微信接口发起支付业务===");
return "调用微信接口发起支付业务";
}
}
@Service
@Slf4j
public class YiBaoPay implements IPay {
@Override
public String pay() {
log.info("===调用易宝接口发起支付业务===");
return "调用易宝接口发起支付业务";
}
}
工厂类 PayFactory
@Component
public class PayFactory {
/**
* 利用Spring自动注入的特点。Key为bean的名称、value为IPay接口对应的bean实现类
*/
@Autowired
Map<String, IPay> map;
public IPay getInstance(String type) {
return map.get(type);
}
}
工厂类使用
@RestController
public class PayController {
@Autowired
private PayFactory payFactory;
@GetMapping("/alipay")
public String aliPay(String type) {
IPay pay = payFactory.getInstance(type);
return pay.pay();
}
}
Key 为 bean 的名称、value 为 IPay 接口对应的 bean 实现类。对应的业务类型不好扩展
二、继承 InitializingBean+ 静态工厂方法调用处理
继承 InitializingBean,重写 afterPropertiesSet()
public interface IPay extends InitializingBean {
String pay();
}
@Service
@Slf4j
public class AliPay implements IPay {
@Override
public String pay() {
log.info("===调用支付宝接口发起支付业务===");
return "调用支付宝接口发起支付业务";
}
@Override
public void afterPropertiesSet() throws Exception {
PayFactory.registerHandler("ali", this);
}
}
@Service
@Slf4j
public class WeiXinPay implements IPay {
@Override
public String pay() {
log.info("===调用微信接口发起支付业务===");
return "调用微信接口发起支付业务";
}
@Override
public void afterPropertiesSet() throws Exception {
PayFactory.registerHandler("weixin", this);
}
}
@Service
@Slf4j
public class YiBaoPay implements IPay {
@Override
public String pay() {
log.info("===调用易宝接口发起支付业务===");
return "调用易宝接口发起支付业务";
}
@Override
public void afterPropertiesSet() throws Exception {
PayFactory.registerHandler("yibao", this);
}
}
三、注解 +CommandLineRunner+ApplicationContextAware 处理/ApplicationListener< ContextRefreshedEvent >
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PayCode {
String value();
String name();
}
public interface IPay {
String pay();
}
@PayCode(value = "alia", name = "支付宝支付")
@Service
@Slf4j
public class AliPay implements IPay {
@Override
public String pay() {
log.info("===调用支付宝接口发起支付业务===");
return "调用支付宝接口发起支付业务";
}
}
@PayCode(value = "weixin", name = "微信支付")
@Service
@Slf4j
public class WeiXinPay implements IPay {
@Override
public String pay() {
log.info("===调用微信接口发起支付业务===");
return "调用微信接口发起支付业务";
}
}
@PayCode(value = "yibao", name = "易宝支付")
@Service
@Slf4j
public class YiBaoPay implements IPay {
@Override
public String pay() {
log.info("===调用易宝接口发起支付业务===");
return "调用易宝接口发起支付业务";
}
}
第一种方式
工厂类 PayFactory 实现 Spring 生命周期函数在 run 方法中处理被标记的 @PayCode
的 bean 对象加入到静态容器 Map 中:
CommandLineRunner
:当前 Bean 被 IOC 容器装配完成调用 run()方法
ApplicationContextAware
: 获取容器对象 ApplicationContext
@Component
public class PayFactory implements CommandLineRunner, ApplicationContextAware {
private ApplicationContext applicationContext;
private static final Map<String, IPay> PAY_MAP = new HashMap<>();
/**
* 获取对应实例Bean
* @param type
* @return
*/
public static IPay getInstance(String type) {
return PAY_MAP.get(type);
}
@Override
public void run(String... args) throws Exception {
Map<String, Object> beansWithAnnotation =
applicationContext.getBeansWithAnnotation(PayCode.class);
if (beansWithAnnotation !=null ) {
beansWithAnnotation.forEach((k,v)->{
PayCode payCodeAnn = v.getClass().getAnnotation(PayCode.class);
PAY_MAP.put(payCodeAnn.value(), (IPay)v);
});
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
第二种方式
实现 ApplicationListener<ContextRefreshedEvent>监听事件接口
代替 CommandLineRunner, ApplicationContextAware
。
ContextRefreshedEvent事件:
ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext
接口中使用 refresh()方法来发生。此处的初始化是指:所有的 Bean 被成功装载,后处理 Bean 被检测并激活,所有 Singleton Bean 被预实例化,ApplicationContext 容器已就绪可用。
@Component
public class PayFactory implements ApplicationListener<ContextRefreshedEvent> {
private static final Map<String, IPay> PAY_MAP = new HashMap<>();
/**
* 获取对应实例Bean
* @param type
* @return
*/
public static IPay getInstance(String type) {
return PAY_MAP.get(type);
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(PayCode.class);
if (beansWithAnnotation !=null ) {
beansWithAnnotation.forEach((k,v)->{
PayCode payCodeAnn = v.getClass().getAnnotation(PayCode.class);
PAY_MAP.put(payCodeAnn.value(), (IPay)v);
});
}
}
}
使用
@RestController
public class PayController {
@GetMapping("/alipay")
public String aliPay(String type) {
IPay pay = PayFactory.getInstance(type);
return pay.pay();
}
}
Map+ 函数式接口代替策略模式
用上了 Java8 的新特性 lambda 表达式
- 判断条件放在 key 中
- 对应的业务逻辑放在 value 中
需求:根据优惠券(资源)类型 resourceType 和编码 resourceId 查询派发方式 grantType
@Service
public class QueryGrantTypeService {
@Autowired
private GrantTypeService grantTypeService;
private Map<String, Function<String, String>> grantTypeMap = new HashMap();
/**
* 初始化业务分派逻辑,代替了if-else部分
* key: 优惠券类型
* value: lambda表达式,最终会获得该优惠券的发放方式
*/
@PostConstruct
public void dispatcherInit() {
grantTypeMap.put("红包", resourceId -> grantTypeService.redPaper(resourceId));
grantTypeMap.put("购物券", resourceId -> grantTypeService.shopping(resourceId));
grantTypeMap.put("qq会员", resourceId -> grantTypeService.QQVip(resourceId));
}
public String getResult(String resourceType, String resourceId) {
// 根据 优惠券类型resourceType、编码resourceId 去查询 发放方式grantType
Function<String, String> result = grantTypeMap.get(resourceType);
if (Objects.nonNull(result)) {
//传入resourceId 执行这段表达式获得String型的grantType
return result.apply(resourceId);
}
return "查询不到该优惠券的发放方式";
}
}
如果单个 if 语句块的业务逻辑有很多行的话,我们可以把这些 业务操作抽出来,写成一个单独的 Service,即:
@Service
public class GrantTypeService {
public String redPaper(String resourceId){
//红包的发放方式
return "每周末9点发放";
}
public String shopping(String resourceId){
//购物券的发放方式
return "每周三9点发放";
}
public String QQVip(String resourceId){
//qq会员的发放方式
return "每周一0点开始秒杀";
}
}
使用
@RestController
public class GrantTypeController {
@Autowired
private QueryGrantTypeService queryGrantTypeService;
@GetMapping("/grantType/{resourceType}/{resourceId}")
public String test(@PathVariable String resourceType, @PathVariable String resourceId){
return queryGrantTypeService.getResult(resourceType, resourceId);
}
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于