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

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

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

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

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

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

1、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.创建具体的执行策略

1、通过@PostConstruct修饰init(),在服务器加载Servle的时候运行,并且只会被服务器执行一次。
2、通过applicationContext.getBeansOfType(ExecutorStrategyInterface.class)获取所有实现ExecutorStrategyInterface的Bean,将其放入内存EXECUTOR_BEANS。
3、遍历实现ExecutorStrategyInterface的Bean并返回与参数对应的实例。


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

    /**
     * 根据不同的执行器创建不同的策略
     * @return
     */
    public ExecutorStrategyInterface createStrategy(String executor) {
        Optional 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)拿到的是“别名”

2、Java反射

这种方式非常简单,不需要策略工厂类,直接在调用的时候通过全类名及方法名利用java反射调用方法就好了。

/**
 * @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();
        }
    }
}

控制台输出结果如下:

clipboard.png

3、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.执行器的入口

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

/**
 * @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,返回与传参对应的策略

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

    /**
     * 根据不同的执行器创建不同的策略
     * @return
     */
    public ExecutorStrategyInterface createStrategy(int executor) {
        Optional 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

到此三种方式介绍结束。

三、总结

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

你可能感兴趣的:(反射,策略模式,设计模式,java)