在Spring环境中使用注解实现策略模式,避免过多的if-else代码

1 适用场景

       平时写代码,if else是经常用到的语法,是针对不同的条件,运行不同的代码。在条件分支不多的情况下,这肯定是绝佳的选择,但如果分支过多,需要写几十甚至上百的if-else,不仅可读性不好,也不利于维护。这时,可以用到策略模式。本篇文章介绍的策略模式不仅可以省去if-else的代码,还可以将不同的策略内容封装到不同的Class中,在调用不同的策略时也十分方便简洁。

 

2 实现思路

       实现思路是这样的,将不同的策略的代码内容封装到不同的Class里面,并声明自定义注解,在每一个策略类的类名上都加上这一注解,不同的策略的注解中给不同的值;在Spring项目启动时,通过类扫描器扫描注解将对应的Class写入Spring中;需要调用具体的策略方式时,再通过从Spring的上下文对象ApplicationContext获取Class去执行不同的策略中的内容。

 

3 code

        直接上代码了。

3.1 自定义注解

        自定义注解用来标在不同的策略类上。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyType {
    String value() default "";
}

3.2 类扫描器

        类扫描器相当于一个工具类,用来扫描指定包中的Class,在网上有许多类扫描器的写法,这里提供一个。

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;
import org.springframework.util.TypeUtils;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 策略扫描器
 */
public class ClassScanner {
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    private final List<TypeFilter> includeFilters = new ArrayList<>();

    private final List<TypeFilter> excludeFilters = new ArrayList<>();

    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    /**
     * 添加包含的Fiter
     * @param includeFilter TypeFilter
     */
    public void addIncludeFilter(TypeFilter includeFilter) {
        this.includeFilters.add(includeFilter);
    }

    /**
     * 扫描指定的包,获取包下所有的Class
     * @param basePackage 包名
     * @param targetTypes 需要指定的目标类型,可以是pojo,可以是注解
     * @return Set<Class<?>>
     */
    public static Set<Class<?>> scan(String basePackage, Class<?>... targetTypes) {
        ClassScanner cs = new ClassScanner();
        for (Class<?> targetType : targetTypes){
            if (TypeUtils.isAssignable(Annotation.class, targetType)) {
                cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
            } else {
                cs.addIncludeFilter(new AssignableTypeFilter(targetType));
            }
        }
        return cs.doScan(basePackage);
    }

    /**
     * 扫描指定的包,获取包下所有的Class
     * @param basePackage 包名
     * @return Set<Class<?>>
     */
    public Set<Class<?>> doScan(String basePackage) {
        Set<Class<?>> classes = new HashSet<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    + ClassUtils.convertClassNameToResourcePath(
                    SystemPropertyUtils.resolvePlaceholders(basePackage))+"/**/*.class";
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (int i = 0; i < resources.length; i++) {
                Resource resource = resources[i];
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    if ((includeFilters.size() == 0 && excludeFilters.size() == 0)|| matches(metadataReader)) {
                        try {
                            classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
                        } catch (ClassNotFoundException ignore) {}
                    }
                }
            }
        } catch (IOException ex) {
            throw new RuntimeException("IO Exception!", ex);
        }
        return classes;
    }

    /**
     * 处理 excludeFilters和includeFilters
     * @param metadataReader MetadataReader
     * @return boolean
     */
    private boolean matches(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return true;
            }
        }
        return false;
    }
}

3.3 实现BeanFactoryPostProcessor接口

        实现Spring提供的这一接口的目的是为了在Spring项目启动时,通过类扫描器去扫描注解,并将对应的Class写入Spring。

import com.bigsea.myannotation.MyType;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class HandlerProcessor implements BeanFactoryPostProcessor {
    /**
     * 扫描的目标类所处的包的路径
     */
    private final static String basePackage = "com.bigsea.strategy";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Map<String, Class> map = new HashMap<>();
        for (Class<?> aClass : ClassScanner.scan(basePackage, MyType.class)) {
            String type = aClass.getAnnotation(MyType.class).value();
            map.put(type,aClass);
        }
        beanFactory.registerSingleton(MyType.class.getName(), map);
    }
}

3.4 从Spring上下文对象中获取Class对象的类

        这个类的作用就是结合之前的功能用来从Spring的上下文中获取对应的策略的Class。

import com.bigsea.myannotation.MyType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 策略上下文,从Spring中得到Class
 */
@Component
public class HandlerContext {
    @Autowired
    private ApplicationContext applicationContext;

    public AbstractHandler getInstance(String type) {
        Map<String, Class> map = (Map<String, Class>) applicationContext.getBean(MyType.class.getName());
        return (AbstractHandler)applicationContext.getBean(map.get(type));
    }
}

3.5 定义传递值的类

        这一对象用来封装数据,根据type值来判断调用哪一个策略(这一个type对应MyType中的值);并且可以在这一个对象中定义其他属性,传递属性值来实现业务功能。

/**
 * 传递数据的对象
 */
public class MyDetail {
    private String type;

    private String name;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3.6 抽象类

        这一抽象类的作用是让每一个策略类都去继承它,并实现当中的方法,在调用策略处,直接调用它,来执行不同的策略。

/**
 * 抽象类
 */
public abstract class AbstractHandler {
    /**
     * 根据不同的类型处理内容
     * @param orderInfo MyDetail
     */
    public abstract void handle(MyDetail orderInfo);
}

3.7 策略

        策略可以有很多个,写几十、几百,甚至几千个都可以,根据具体的业务需要来。这里我写了一个策略来举例子。

import com.bigsea.myannotation.MyType;
import com.bigsea.strategy.AbstractHandler;
import com.bigsea.strategy.MyDetail;
import org.springframework.stereotype.Component;

/**
 * 策略一
 */
@Component
@MyType("1")
public class DetailOneStrategy extends AbstractHandler {
    @Override
    public void handle(MyDetail myDetail) {
        // todo 这里可以写业务代码,需要的参数内容可以从myDetail中获取
        System.out.println("type:" + myDetail.getType());
        System.out.println("name:" + myDetail.getName());
    }
}

 

4 测试结果

      我们用Junit进行测试,执行的结果是调用了DetailOneStrategy中的handle方法,因为这一个策略类的@MyType给的值是"1",而在测试方法中我给的type的值也是"1"。

import com.bigsea.strategy.AbstractHandler;
import com.bigsea.strategy.HandlerContext;
import com.bigsea.strategy.MyDetail;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class StrategyTest {
    @Autowired
    private HandlerContext handlerContext;

    @Test
    public void testStrategy() {
        // todo 这里可以前端传过来的获取从数据库查出来的数据去构造对象,然后调用策略方法
        MyDetail myDetail = new MyDetail();
        myDetail.setType("1");
        myDetail.setName("一号");
        AbstractHandler handler = handlerContext.getInstance(myDetail.getType());
        handler.handle(myDetail);
    }
}

        打印控制台的结果:

type:1
name:一号

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用策略模式来代替 if-else 分支语句。具体实现方法是定义一个接口和多个实现类,每个实现类对应一个手机号码,并实现具体的业务逻辑。然后在 Controller 通过 Spring 的 @Autowired 注解注入所有实现类,根据用户输入的手机号码,调用对应的实现类来处理请求,最终将计算得到的结果封装在 Model 返回给页面。示例代码如下: ``` // 定义策略接口 public interface PhoneNumberStrategy { void process(String phoneNumber, Model model); } // 实现具体的策略类 @Service("139PhoneNumberStrategy") public class PhoneNumber139Strategy implements PhoneNumberStrategy { @Override public void process(String phoneNumber, Model model) { // TODO: 根据手机号码139处理请求,并将计算结果放入Model } } @Service("188PhoneNumberStrategy") public class PhoneNumber188Strategy implements PhoneNumberStrategy { @Override public void process(String phoneNumber, Model model) { // TODO: 根据手机号码188处理请求,并将计算结果放入Model } } // Controller 注入所有策略类,并通过手机号码调用对应的策略类的处理逻辑 @Controller public class MyController { @Autowired private List<PhoneNumberStrategy> phoneNumberStrategies; @GetMapping("/{phoneNumber}") public String handleRequest(@PathVariable String phoneNumber, Model model) { for (PhoneNumberStrategy strategy : phoneNumberStrategies) { if (strategy.isMatch(phoneNumber)) { strategy.process(phoneNumber, model); break; } } return "result"; } } ``` 注意:以上示例代码仅为参考,具体实现方式还需要根据具体业务逻辑进行适当修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值