【设计模式】Springboot 项目实战中使用策列+工厂模式优化代码中的if...else..

        在开发过程中,经常会遇到判断某个类型字段的多种类型,通常我们会使用if..else if 或者 switch来进行一个个的判断,如果类型越来越多,写的if..else就会越来越多,代码的可读性就会越来越差,时间久了哪怕自己都有点搞不明白了,更不说他人来维护,那么为了解决这一困扰我们可以使用 策略模式 来对代码进行优化。

策略模式的定义与特点

策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的主要优点如下。

  1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。


其主要缺点如下。

  1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  2. 策略模式造成很多的策略类。

代码实现

以下策略实现为项目中经常会用到的通知业务为例子:

定义通知策略接口

/**
 * 消息策略接口
 * @author francis
 *
 */
public interface INoticeStrategy {

	/**
	 * 执行策略
	 * @param msg
	 */
	public void doStrategy(String msg);
}

自定义注解,方便找到具体的策略实现类

/**
 * 消息通知类型注解
 * @author francis
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoticeType {

	NoticeTypeEnum value();
}

消息类型枚举类 

/**
 * 
 * @author francis
 *
 */
public enum NoticeTypeEnum {
	SYSTEM, LIKE, COMMENT;
}

各种通知实现类,每一种通知都需要新建一个类来维护并且继承策略接口

/**
 * 系统通知策略
 * @author francis
 *
 */
@Slf4j
@Component
@NoticeType(NoticeTypeEnum.SYSTEM)
public class SysNoticeStrategyImpl implements INoticeStrategy {

	@Override
	public void doStrategy(String msg) {
		log.info("系统消息:{}", msg);
	}

}
/**
 * 点赞消息通知策略
 * @author francis
 *
 */
@Slf4j
@Component
@NoticeType(NoticeTypeEnum.LIKE)
public class LikeNoticeStrategyImpl implements INoticeStrategy {

	@Override
	public void doStrategy(String msg) {
		log.info("点赞消息:{}", msg);
	}

}
/**
 * 评论消息通知策略
 * @author francis
 *
 */
@Slf4j
@Component
@NoticeType(NoticeTypeEnum.COMMENT)
public class CommentNoticeStrategyImpl implements INoticeStrategy {

	@Override
	public void doStrategy(String msg) {
		log.info("评论消息:{}", msg);
	}

}

定义策略工厂,统一维护各个策略类并且分发消息给指定的实现类

/**
 * 通知策略工厂
 * @author francis
 */
@Component
public class NoticeStrategyFactory {

	private Map<NoticeTypeEnum, INoticeStrategy> strategyMap = new ConcurrentHashMap<>();
	
	/**
	 * Autowired 会自动注入所有的实现NoticeStrategyFactory的bean <br/>
	 * 然后获取类上NoticeType注解的值替换掉 bean初始化的名称 组成一个新的strategyMap
	 * @param strategyMap
	 */
	@Autowired
	public NoticeStrategyFactory(Map<String, INoticeStrategy> strategyMap) {
		this.strategyMap.clear();
		Set<String> strategyKeys = strategyMap.keySet();
		for (String key : strategyKeys) {
			Class<? extends INoticeStrategy> clazz = strategyMap.get(key).getClass();
			NoticeType annotation = clazz.getAnnotation(NoticeType.class);
			if (annotation != null) {
				this.strategyMap.put(annotation.value(), strategyMap.get(key));
			}
		}
	}
	
	/**
	 * 寻找对应得策略处理器
	 */
	public INoticeStrategy getHandler(NoticeTypeEnum type) {
		return strategyMap.get(type);
	}
	
}

大家可以NoticeStrategyFactory 类中打断点调试一下看 map 里是否有所有的实现类。

*******************

注意:如果你的实现类中重写的方法加了事务的,那么这种装配方法就不可用,因为被事务代理的类中获取不到自定义注解。

解决办法:在Spring 中Spring IOC容器给我们提供的一个扩展接口BeanPostProcessor,在这个接口类中给我们提供了两个方法:

// 在bean初始化之前操作
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws 
    BeansException {
	return bean;
}

// 在bean初始化之后操作
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws             
    BeansException {
	return bean;
}

然后我们让策略工厂类 实现BeanPostProcessor 接口 重写里面的postProcessBeforeInitialization方法 在类被事务代理之前获取到实现上的自定义注解:

@Component
public class NoticeStrategyFactory implements BeanPostProcessor{

	private Map<NoticeTypeEnum, INoticeStrategy> strategyMap = new ConcurrentHashMap<>();
	
	/**
	 * 事务代理会影响bean获取其bean上的注解,此处用BeanPostProcessor 在事务代理之前 拿到bean 的注解
	 */
	@Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
		// 只对实现了INoticeStrategy的类做操作
                if(bean instanceof INoticeStrategy ) {
			Class<? extends Object> clazz = bean.getClass();
			NoticeType annotation = clazz.getAnnotation(NoticeType.class);
			this.strategyMap.put(annotation.value(), (INoticeStrategy)bean);
		}
		return bean;
	}
	
	/**
	 * 寻找对应得策略处理器
	 */
	public INoticeStrategy getHandler(NoticeTypeEnum type) {
		return strategyMap.get(type);
	}
	
}

 

以上就是spring 中策略的一种实现(当然,有很多中实现方式),现在我们来写个controller验证一下。

@RestController
@RequestMapping("/strategy")
public class StrategyTestController {

	@Autowired
	private NoticeStrategyFactory noticeStrateyFactory;
	
	@GetMapping("sendSysNotice")
	public void sendSysNotice() {
		noticeStrateyFactory.getHandler(NoticeTypeEnum.SYSTEM).doStrategy("有一条系统消息!!");
	}

	@GetMapping("sendLikeNotice")
	public void sendLikeNotice() {
		noticeStrateyFactory.getHandler(NoticeTypeEnum.LIKE).doStrategy("xxx给你点赞了!!");
	}

	@GetMapping("sendCommentNotice")
	public void sendCommentNotice() {
		noticeStrateyFactory.getHandler(NoticeTypeEnum.COMMENT).doStrategy("xxx评论了你的博客!!");
	}
}

分别调用以上接口,控制台打印:

源码传送门: strategy-demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值