策略模式的理解与实践
由三部分组成
上下文、策略接口、具体策略类
第一步:先定义策略接口(能不能做support、具体怎么做handle)
第二步:实现策略接口的具体类
第三步:上下文类持有所有的的策略类,遍历找到能匹配执行的方法
1.直接注入方法
public interface MessageNotifier {
/**
* 是否支持改类型的通知的方式
*
* @param type 0:短信 1:app
* @return
*/
boolean support(int type);
/**
* 通知
*
* @param user
* @param content
*/
void notify(User user, String content);
}
public class AppMessageNotifier implements MessageNotifier {
@Override
public boolean support(int type) {
return type == 1;
}
@Override
public void notify(User user, String content) {
//调用通知app通知的api
}
}
@Component
public class SMSMessageNotifier implements MessageNotifier {
@Override
public boolean support(int type) {
return type == 0;
}
@Override
public void notify(User user, String content) {
//调用短信通知的api发送短信
}
}
public class Main {
@Resource
private List<MessageNotifier> messageNotifiers;
public void notifyMessage(User user, String content, int notifyType) {
for (MessageNotifier messageNotifier : messageNotifiers) {
if (messageNotifier.support(notifyType)) {
messageNotifier.notify(user, content);
}
}
}
}
2.通过实现ApplicationContentAware或BeanFeactoryAware接口
public enum MemLevel {
NORMAl(0, "普通会员"),
VIP(1, "普通会员");
private Integer level;
private String desc;
MemLevel(Integer level, String desc) {
this.level = level;
this.desc = desc;
}
public Integer getLevel() {
return level;
}
}
public interface IDisCount {
/**
* 获取当前策略的类型
* @return
*/
MemLevel getType();
/**
* 具体的打折操作
*/
void operation();
}
/**
* 普通顾客
*/
@Component
public class NormalDisCount implements IDisCount {
@Override
public MemLevel getType() {
return MemLevel.NORMAl;
}
@Override
public void operation() {
// 逻辑操作
System.out.println("原价");
}
}
/**
* VIP 顾客
*/
@Component
public class VipDiscount implements IDisCount {
@Override
public MemLevel getType() {
return MemLevel.VIP;
}
@Override
public void operation() {
// 逻辑操作
System.out.println("九折");
}
}
@Service
public class ShoppingService implements ApplicationContextAware {
Map<Integer, IDisCount> strategy;
/**
* 根据总共消费的金额来判断打折的方式
* @param
*/
public void shopping(Integer level) {
// 获取相应的策略
IDisCount iDisCount = strategy.get(level);
// 执行策略操作
iDisCount.operation();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 获取ioc容器中的所有IDisCount组件, 加入到 Map<MemLevel, IDisCount> strategy;
Map<String, IDisCount> iDisCountMap = applicationContext.getBeansOfType(IDisCount.class);
HashMap<Integer, IDisCount> map = new HashMap<>();
iDisCountMap.forEach((k, v) -> map.put(v.getType().getLevel(), v));
this.strategy = map;
}
}
public class StrategyTest {
AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(Config.class);
@Test
public void test() {
// 会员等级
Integer memLevel = 1;
// 从ioc容器中获取ShoppingService
ShoppingService shoppingService = ioc.getBean(ShoppingService.class);
shoppingService.shopping(1);
}
}
Spring支持自动注入接口相应所有的实现类到Collection(List、Set)、Array、Map中
SpringMVC中的策略模式使用
-
参数解析器
1. 策略接口:HandlerMethodArgumentResolver,包含两个方法:supportsParameter(是否支持)、resolveArgument(真正解析参数) 2. 具体策略类:RequestResponseBodyMethodProcessor、RequestParamMapMethodArgumentResolver.... 3. 上下文持有所有的策略类:HandlerMethodArgumentResolverComposite注:HandlerMethodArgumentResolverComposite还做了缓存操作
-
返回值解析器
1. 策略接口:HandlerMethodReturnValueHandler,包含两个方法:supportsReturnType(是否支持)、handleReturnValue(真正处理返回值干活) 2. 具体策略类:RequestResponseBodyMethodProcessor.... 3. 上下文持有所有的策略类:HandlerMethodReturnValueHandlerComposite -
处理器适配器
1. 策略接口:HandlerAdapter,包含两个方法:supports(是否支持)、handle(真正适配处理逻辑) 2. 具体策略类:AbstractHandlerMethodAdapter、HandlerFunctionAdapter.... 3. 上下文持有所有的策略类:DispatcherServlet -
视图解析器同样使用了策略模式
其中一块逻辑使用了缓存,使用缓存double check模式、类似与防止缓存击穿加分布式锁的效果
Object cacheKey = getCacheKey(viewName, locale); View view = this.viewAccessCache.get(cacheKey); // 1.第一次检查缓存数据是否存在 if (view == null) { // 2.加锁 synchronized (this.viewCreationCache) { // 3.再次从缓存中查数据是否存在 view = this.viewCreationCache.get(cacheKey); if (view == null) { // 4.不存在最终创建视图 view = createView(viewName, locale); if (view == null && this.cacheUnresolved) { view = UNRESOLVED_VIEW; } // 5.放入缓存中 if (view != null && this.cacheFilter.filter(view, viewName, locale)) { this.viewAccessCache.put(cacheKey, view); this.viewCreationCache.put(cacheKey, view); } } } }
其他重要的内容
不循环调用数据库
不要在循环中访问数据库,这样会严重影响数据库性能。
比如需要查询一批人员的信息,人员的信息存在基本信息表和扩展表中,错误的代码如下:
public List<PersonVO> selectPersons(List<Long> personIds) {
List<PersonVO> persons = new ArrayList<>(personIds.size());
List<Person> personList = personMapper.selectByIds(personIds);
for (Person person : personList) {
PersonVO vo = new PersonVO();
PersonExt personExt = personExtMapper.selectById(person.getId());
// 组装数据
persons.add(vo);
}
return persons;
}
遍历每个人员的基本信息,去数据库查找。
正确的方法应该先批量查出来,然后转成map:
public List<PersonVO> selectPersons(List<Long> personIds) {
List<PersonVO> persons = new ArrayList<>(personIds.size());
List<Person> personList = personMapper.selectByIds(personIds);
//批量查询,转换成Map
List<PersonExt> personExtList = personExtMapper.selectByIds(person.getId());
Map<String, PersonExt> personExtMap = personExtList.stream().collect(Collectors.toMap(PersonExt::getPersonId, Function.identity()));
for (Person person : personList) {
PersonVO vo = new PersonVO();
//直接从Map中查找
PersonExt personExt = personExtMap.get(person.getId());
// 组装数据
persons.add(vo);
}
return persons;
}

浙公网安备 33010602011771号