策略模式获取算法族(行为)的方式

今天总结一下策略模式使用过程当中获取算法族(行为)的方式,内容纯属我的观点,欢迎指正。java

1、策略模式的场景及难点

一、策略模式的定义及说明在此就不提了,没有基础的推荐你们一本书《Head First 设计模式》,图文及场景的选择让读者很容易理解。
我的的使用场景:算法

项目中有多个执行器,每一个执行器执行不一样的业务处理,但愿将这些执行器作统一的入口,根据入口的行为参数决定使用哪一个执行器去执行相应的任务,在策略模式的使用中,我的以为这也是难点所在,想到的作法有三种,经过Spring getBean、Java的反射以及另外一种在getBean的基础上增长Spring Annotation,下面逐一介绍三种方式的使用。

2、策略模式获取处理类的方式

一、Spring getBean

a.执行器的入口

/**
 * @author xiaokaige
 */
@RestController
@RequestMapping(value = "/test")
public class TestController {

    @Autowired
    private ExecutorStrategyFactory strategyFactory;

    @GetMapping("strategy")
    public void strategy(){
        //建立具体的执行策略,并执行为
        ExecutorStrategyInterface strategy = strategyFactory.createStrategy("executorB");
        strategy.doExecutorAction();
    }
}

b.建立具体的执行策略

一、经过@PostConstruct修饰init(),在服务器加载Servle的时候运行,而且只会被服务器执行一次。
二、经过applicationContext.getBeansOfType(ExecutorStrategyInterface.class)获取全部实现ExecutorStrategyInterface的Bean,将其放入内存EXECUTOR_BEANS。
三、遍历实现ExecutorStrategyInterface的Bean并返回与参数对应的实例。设计模式

/**
 * @author xiaokaige
 */
@Component
public class ExecutorStrategyFactory {
    private static final Map<String,ExecutorStrategyInterface> EXECUTOR_BEANS = Maps.newConcurrentMap();
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 根据不一样的执行器建立不一样的策略
     * @return
     */
    public ExecutorStrategyInterface createStrategy(String executor) {
        Optional<ExecutorStrategyInterface> strategyOptional =
                EXECUTOR_BEANS
                        .entrySet()
                        .stream()
                        .map(e -> {
                            if (Objects.equals(e.getKey(),executor)) {
                                return e.getValue();
                            }
                            return null;
                        }).filter(Objects::nonNull).findAny();
        if(strategyOptional.isPresent()){
            System.out.println(strategyOptional.get());
            return strategyOptional.get();
        }
        throw new RuntimeException("策略得到失败");
    }

    /**
     * 初始化策略列表
     */
    @PostConstruct
    private void init() {
        EXECUTOR_BEANS.putAll(applicationContext.getBeansOfType(ExecutorStrategyInterface.class));
    }

}

c.执行器接口

/**
 * @author xiaokaige
 */
public interface ExecutorStrategyInterface {
    /**
     * 执行器接口
     */
    void doExecutorAction();

}

d.执行器接口的实现

/**
 * @author xiaokaige
 */
@Slf4j
@Service
public class ExecutorA implements ExecutorStrategyInterface {

    @Override
    public void doExecutorAction() {
        log.info("I entered through Strategy A.");
    }
}
/**
 * @author xiaokaige
 */
@Slf4j
@Service
public class ExecutorB implements ExecutorStrategyInterface {

    @Override
    public void doExecutorAction() {
        log.info("I entered through Strategy B.");
    }
}

e.请求接口

请求接口能够看到控制台输出:I entered through Strategy B.服务器

clipboard.png

补充一点,此方式也可以使用@Service("别名"),此时applicationContext.getBeansOfType(ExecutorStrategyInterface.class)拿到的是“别名”app

二、Java反射

这种方式很是简单,不须要策略工厂类,直接在调用的时候经过全类名及方法名利用java反射调用方法就行了。ide

/**
 * @author xiaokaige
 */
@RestController
@RequestMapping(value = "/test")
public class TestController {

    @GetMapping("strategy")
    public void strategy(){
        try {
            Class c = Class.forName("com.xiaokaige.demo.strategy.ExecutorB");
            Method m = c.getMethod("doExecutorAction");
            m.invoke(c.newInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台输出结果以下:spa

clipboard.png

三、Spring Annotation

a.定义 @Annotation

首先经过@interface Annotation{ } 定义一个注解 @Annotation设计

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecutorStrategyAnnotation {
    /**
     * 执行器id,默认值1
     */
    int executorId() default 1;
}

b.为实现ExecutorStrategyInterface接口的类添加一个Id

/**
 * @author xiaokaige
 */
@Slf4j
@Service
@ExecutorStrategyAnnotation(executorId = ExecutorStrategyConstants.A)
public class ExecutorA implements ExecutorStrategyInterface {

    @Override
    public void doExecutorAction() {
        log.info("I entered through Strategy A.");
    }
}
/**
 * @author xiaokaige
 */
@Slf4j
@Service
@ExecutorStrategyAnnotation(executorId = ExecutorStrategyConstants.B)
public class ExecutorB implements ExecutorStrategyInterface {

    @Override
    public void doExecutorAction() {
        log.info("I entered through Strategy B.");
    }
}
/**
 * @author xiaokaige
 */
public class ExecutorStrategyConstants {
    public static final int B = 2;
    public static final int A = 3;
    public static final int C =4;
}

c.执行器的入口

这里建立策略的参数有执行器的名称换为策略的IDcode

/**
 * @author xiaokaige
 */
@RestController
@RequestMapping(value = "/test")
public class TestController {

    @Autowired
    private ExecutorStrategyFactory strategyFactory;

    @GetMapping("strategy")
    public void strategy(){
        //建立具体的执行策略,并执行为
        ExecutorStrategyInterface strategy = strategyFactory.createStrategy(3);
        strategy.doExecutorAction();
    }
}

d.策略工厂获实现

ExecutorStrategyInterface的每一个Bean的Id,返回与传参对应的策略blog

/**
 * @author xiaokaige
 */
@Component
public class ExecutorStrategyFactory {
    private static final Map<String,ExecutorStrategyInterface> EXECUTOR_BEANS = Maps.newConcurrentMap();
    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 根据不一样的执行器建立不一样的策略
     * @return
     */
    public ExecutorStrategyInterface createStrategy(int executor) {
        Optional<ExecutorStrategyInterface> strategyOptional =
                EXECUTOR_BEANS
                        .entrySet()
                        .stream()
                        .map(e -> {
                            ExecutorStrategyAnnotation validExecutor = e.getValue().getClass().getDeclaredAnnotation(ExecutorStrategyAnnotation.class);
                            if (Objects.equals(validExecutor.executorId(),executor)) {
                                return e.getValue();
                            }
                            return null;
                        }).filter(Objects::nonNull).findFirst();
        if(strategyOptional.isPresent()){
            System.out.println(strategyOptional.get());
            return strategyOptional.get();
        }
        throw new RuntimeException("策略得到失败");
    }

    /**
     * 初始化策略列表
     */
    @PostConstruct
    private void init() {
        EXECUTOR_BEANS.putAll(applicationContext.getBeansOfType(ExecutorStrategyInterface.class));
    }

}

ExecutorStrategyInterface类无变化

e.运行程序控制台输出:

clipboard.png

到此三种方式介绍结束。

3、总结

第一种方式调用方只须要提供执行器的名称,还能够经过别名灵活配置,可是要实现策略工厂。第二种经过java反射最为简单,不须要策略工厂类,可是参数须要提供类名及方法名,须要设置两个参数。第三种方式与第一种相似,只是多了Id的映射,适合的场景与第一种方式不一样。我的建议使用第一种,具体看你们的使用了。

相关文章
相关标签/搜索