策略模式的理解与实践

由三部分组成

上下文、策略接口、具体策略类

第一步:先定义策略接口(能不能做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. 参数解析器

    1. 策略接口:HandlerMethodArgumentResolver,包含两个方法:supportsParameter(是否支持)、resolveArgument(真正解析参数)
    2. 具体策略类:RequestResponseBodyMethodProcessor、RequestParamMapMethodArgumentResolver....
    3. 上下文持有所有的策略类:HandlerMethodArgumentResolverComposite
    

    注:HandlerMethodArgumentResolverComposite还做了缓存操作

  2. 返回值解析器

    1. 策略接口:HandlerMethodReturnValueHandler,包含两个方法:supportsReturnType(是否支持)、handleReturnValue(真正处理返回值干活)
    2. 具体策略类:RequestResponseBodyMethodProcessor....
    3. 上下文持有所有的策略类:HandlerMethodReturnValueHandlerComposite
    
  3. 处理器适配器

    1. 策略接口:HandlerAdapter,包含两个方法:supports(是否支持)、handle(真正适配处理逻辑)
    2. 具体策略类:AbstractHandlerMethodAdapter、HandlerFunctionAdapter....
    3. 上下文持有所有的策略类:DispatcherServlet
    
  4. 视图解析器同样使用了策略模式

    其中一块逻辑使用了缓存,使用缓存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;
}
posted @ 2025-03-16 21:50  永无八哥  阅读(42)  评论(0)    收藏  举报