什么是策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
-
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
-
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
-
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
-
如何解决:将这些算法封装成一个一个的类,任意地替换。
-
关键代码:实现同一个接口。
-
应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
-
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
-
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
-
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
-
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
策略模式第一种实现方式
讲解
策略模式
通过handler进行动态选择具体处理的实现类
handlerType: 用来标识具体不同的实现。 AbstractHandler: 抽象类,抽象方法,对外提供的统一实现方法。 HandlerContext: 核心处理所有的适配器,初始化完成之后,将所有的适配器保存到该对象中,该对象的单例模式。需要的时候,根据标识去获取对应的handler getInstance: 获取适配器的具体实现类,如果缓存中是空的,那就通过包扫描到的所有适配器名字,获取到对应的类,然后获取类上面的注解。注解内容当做key,将类保存到缓存中 HandlerProcessor: 该类继承了BeanFactoryPostProcessor,项目启动的时候,扫描指定包下面的所有类,获取有指定注解的所有类,然后将类放到list中,然后将list保存在HandlerContext MyClassPathDefinitonScanner: 自定义的包扫描器 ScannerUtil: 包扫描工具类,使用该方法进行扫描包 SpringContextUtil: spring 的上下文对象工具类 TestHandler: 测试具体实现的handler适配器
运行顺序,方式:
1、HandlerProcessor 执行该类,调用包扫描工具类 ScannerUtil 扫描指定包路径下的所有包。
2、ScannerUtil 工具类里面使用自定义的扫描工具 MyClassPathDefinitonScanner 进行扫描
3、将扫描的内容整合,获取到java bean的名字,保存到map中,返回给 HandlerProcessor
4、HandlerProcessor 将内容整合成list,将list保存到 HandlerContext 中。
5、单元测试中,自动装载单例的HandlerContext
6、HandlerContext通过传过来的type值进行获取到对应具体的TestHandler实现
6.1、HandlerContext判断缓存是否为空
6.1.1、如果为空,遍历list,获取到所有的bean对象的className
6.1.2、使用spring上下文对象工具类,通过className获取到具体的bean
6.1.3、读取bean对象的类注解。
6.1.4、注解值为key,bean为v,创建缓存对象。
6.2、根据传过来的值,去缓存中获取具体的bean对象。
6.3、将bean对象以父对象的形式返回出去。
7、获取到AbstractHandler父对象,然后调用抽象方法,自动调用到子类实现。
直接上干货
HandlerProcessor
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Map;
@Component
@SuppressWarnings("unchecked")
@Lazy
public class HandlerProcessor implements BeanFactoryPostProcessor {
private static final Logger log = LoggerFactory.getLogger(HandlerProcessor.class);
private static final String HANDLER_PACKAGE = "com.geek45.exampleall*";
public static String test = "1";
/**
* 扫描@HandlerType , 初始化HandlerContext, 将其注册到spring容器
* @param configurableListableBeanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
log.info(test);
List<String> handlerList = Lists.newArrayListWithExpectedSize(3);
//TODO 扫描指定的包,获取类上面的注解 根据注解不同,将不同的类放到map中
Map<String, Object> result = ScannerUtil.scanner(HandlerType.class, HANDLER_PACKAGE);
int size = Integer.valueOf(result.get("size").toString());
log.info("扫描到{}个包", size);
List<String> data = (List<String>) result.get("data");
for (String beanName : data) {
handlerList.add(beanName);
}
log.info("增加{}个handler", handlerList.size());
HandlerContext handlerContext = new HandlerContext(handlerList);
configurableListableBeanFactory.registerSingleton(HandlerContext.class.getName(), handlerContext);
}
}
ScannerUtil
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.GenericApplicationContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ScannerUtil {
private static final Logger log = LoggerFactory.getLogger(ScannerUtil.class);
public static Map<String, Object> scanner(Class annotation, String... basePackage) {
Map<String, Object> result = new HashMap<>();
try {
GenericApplicationContext context = new GenericApplicationContext();
MyClassPathDefinitonScanner myClassPathDefinitonScanner = new MyClassPathDefinitonScanner(context, annotation);
// 注册过滤器
myClassPathDefinitonScanner.registerTypeFilter();
int beanCount = myClassPathDefinitonScanner.scan(basePackage);
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
List<String> data = new ArrayList<>();
for (int i = 0; i < beanDefinitionNames.length; i++) {
if (!beanDefinitionNames[i].startsWith("org.springframework.context")) {
data.add(beanDefinitionNames[i]);
}
}
result.put("beanCount", beanCount);
result.put("beanDefinitionNames", beanDefinitionNames);
result.put("size", data.size());
result.put("data", data);
} catch (Exception e) {
log.error(JSON.toJSONString(e.getStackTrace()));
}
return result;
}
}
MyClassPathDefinitonScanner
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import java.lang.annotation.Annotation;
public class MyClassPathDefinitonScanner extends ClassPathBeanDefinitionScanner {
private Class type;
public MyClassPathDefinitonScanner(BeanDefinitionRegistry registry, Class<? extends Annotation> type){
super(registry,false);
this.type = type;
}
/**
* 注册 过滤器
*/
public void registerTypeFilter(){
addIncludeFilter(new AnnotationTypeFilter(type));
}
}
HandlerContext
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HandlerContext {
private static final Logger log = LoggerFactory.getLogger(HandlerContext.class);
private Map<String, Class> handlerMap;
private List<String> handlerList;
public HandlerContext(List<String> handlerList) {
log.info("初始化HandlerContext");
this.handlerList = handlerList;
}
public AbstractHandler getInstance(String type) {
if (handlerMap == null) {
log.info("handlerMap为空,初始化");
handlerMap = new HashMap<>();
for (String beanName : handlerList) {
Class clazz = SpringContextUtil.getBean(beanName).getClass();
handlerMap.put(getAnnotation(clazz), clazz);
}
}else{
log.info("handlerMap不为空,不需要初始化");
}
Class clazz = handlerMap.get(type);
if (clazz == null) {
throw new RuntimeException("没有找到该实例");
}
return (AbstractHandler) SpringContextUtil.getBean(clazz);
}
public static String getAnnotation(Class clazz) {
HandlerType handlerType = (HandlerType) clazz.getAnnotation(HandlerType.class);
if (handlerType != null) {
return handlerType.value();
} else {
return "";
}
}
}
AbstractHandler
public abstract class AbstractHandler {
public abstract String handler(String type);
}
HandlerType
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {
String value() default "1";
}
SpringContextUtil
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 获取bean实例工具类
* @author Qian Zhilei
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {
/**
* 上下文对象实例
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取Bean.
*
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name以及Class返回指定的Bean
*
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
/**
* 获取指定类型所有子类映射
*
* @param clazz
* @param <T>
* @return
*/
public static <T> Map<String, T> getBeanOfType(Class<T> clazz) {
return getApplicationContext().getBeansOfType(clazz);
}
}
TestHandler
import org.springframework.stereotype.Component;
@HandlerType("ace")
@Component
public class TestHandler extends AbstractHandler {
@Override
public String handler(String type) {
return type + ":Ace";
}
}
ApplicationTests
import com.geek45.exampleall.aop.demo1.AbstractHandler;
import com.geek45.exampleall.aop.demo1.HandlerContext;
import com.geek45.exampleall.aop.demo2.HandlerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class ApplicationTests {
@Resource
private HandlerContext handlerContext;
@Test
void contextLoads() {
AbstractHandler handler = handlerContext.getInstance("ace");
System.err.println(handler.handler("你好"));
}
}
输出结果
你好:Ace
说明
看到这你可能想打我,怎么这么多的代码,不就去掉几个if else吗,这么麻烦!
好了,那我就不藏着掖着了,下面来一套比较实用的,精简版的。
策略模式第二种方式
讲解
使用抽象类,实现接口,并给出统一的返回值。
不同实现集成抽象类,选择性实现。如果抽象类已经实现的,可以不用实现。抽象类没有实现的,属于必须实现的方法。
调用接口的地方,统一装载HandlerService进行调用,HandlerService内部去获取不同的实现,然后给出结果。运行顺序,方式:
项目运行的时候,HandlerConfig自动装载配置文件中的配置,将类型对应的实现类名字自动装载进来。
接口装载HandlerService的实现:HandlerServiceImpl,根据@Qualifier("default")来精准装载,否则有多个实现会装载错误。
调用HandlerServiceImpl里面的实现
HandlerServiceImpl通过type去获取对应的实现类名字。
判断缓存是否为空
如果为空,通过spring上下文对象工具类,获取AbstractHandler抽象类下的所有子类,并自动返回service名字为key,bean对象为v的map对象。
通过实现类的名字,从缓存中拿到具体的实现转化为接口,返回给调用者
调用者拿到具体的接口实现,调用对应的方法,然后获取到具体的内容。
干货来了
HandlerConfig
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@ConfigurationProperties(prefix = "handler")
public class HandlerConfig {
private Map<String, String> type;
public Map<String, String> getType() {
return type;
}
public void setType(Map<String, String> type) {
this.type = type;
}
}
application.properties
handler.type.ace = aceService
handler.type.qian = qianService
HandlerService
public interface HandlerService {
String handler(String type);
}
HandlerServiceImpl
import com.geek45.exampleall.aop.demo1.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
@Service("default")
public class HandlerServiceImpl implements HandlerService {
private Map<String, AbstractHandler> beanMap = null;
@Resource
private HandlerConfig config;
private HandlerService getBeanByType(String type) {
if (StringUtils.isNotBlank(type)) {
if (beanMap == null) {
beanMap = SpringContextUtil.getBeanOfType(AbstractHandler.class);
}
if (config == null) {
return null;
}
HandlerService handlerService = beanMap.get(type);
if (handlerService == null) {
return null;
}
return handlerService;
}
return null;
}
private HandlerService getBean(String name) {
return getBeanByType(config.getType().get(name));
}
@Override
public String handler(String type) {
HandlerService handlerService = getBean(type);
if (handlerService == null) {
return type + ":没有该类型";
}
return handlerService.handler(type);
}
}
AbstractHandler
public abstract class AbstractHandler implements HandlerService{
String message = ":不支持的接口";
@Override
public String handler(String type) {
return type + message;
}
}
TestHandler
import org.springframework.stereotype.Service;
@Service("aceService")
public class TestHandler extends AbstractHandler {
@Override
public String handler(String type) {
return type + ":你好";
}
}
TestHandler2
import org.springframework.stereotype.Service;
@Service("qianService")
public class TestHandler2 extends AbstractHandler {
}
ApplicationTests
import com.geek45.exampleall.aop.demo1.AbstractHandler;
import com.geek45.exampleall.aop.demo1.HandlerContext;
import com.geek45.exampleall.aop.demo2.HandlerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class ApplicationTests {
@Resource
@Qualifier("default")
HandlerService handlerService;
@Test
void testHandler(){
System.err.println(handlerService.handler("ace"));
System.err.println(handlerService.handler("qian"));
System.err.println(handlerService.handler("www"));
}
}
输出结果
ace:你好 qian:不支持的接口 www:没有该类型
说明
这次的比较精简,也是我目前工作中正在使用的
总结
业务驱动于技术,不要让技术去掌控业务。没有最好最强的技术,只有最适合的技术。
通过对业务的理解,找到最合适这个业务的技术,才是一个好的架构师。
让一个初创公司,不过几百上千的业务量,设计分库分表,中间件,大型分布式,dubbo,zk,注册中心啥的。实际的业务内容也就是一个增删改查。没必要!
使用内部链接多好,这个方法调用那个方法,大不了的部署一个多节点不得了了。节省成本,节省开销,等到公司业务量上去了,资金允许了,有更多的开发加入进来,再这么设计重构来得及。
你始终很帅,加油,越来越帅。