策略设计模式

一、概述

策略模式,英文全称是 Strategy Design Pattern。在 GoF 的《设计模式》一书中,它是这样定义的:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。

工厂模式是解耦对象的创建和使用,观察者模式是解耦观察者和被观察者。策略模式是将定义、创建、使用这三部分进行解耦。

二、定义、创建、使用

2.1 定义

/**
 * 策略接口
 */
public interface Strategy {
    void executeStrategy();
}

/**
 * 策略枚举
 */
@Getter
public enum StrategyEnum {
    A("A", "A策略模式"),
    B("B", "B策略模式");
    private String type;
    private String desc;
    StrategyEnum(String type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

/**
 * A策略模式
 */
@Slf4j
public class StrategyA implements Strategy {
    @Override
    public void executeStrategy() {
        log.info("执行策略A!");
    }
}

/**
 * B策略模式
 */
@Slf4j
public class StrategyB implements Strategy {
    @Override
    public void executeStrategy() {
        log.info("执行策略B!");
    }
}

2.2 创建

/**
 * 策略工厂
 */
public class StrategyFactory {

    /**
     * 管理所有策略的Map
     */
    private static final Map<StrategyEnum, Strategy> STRATEGY_MAP = new HashMap<>(2);

    /**
     * 静态代码块,启动的时候将所有策略的实现加载进去
     */
    static {
        STRATEGY_MAP.put(StrategyEnum.A, new StrategyA());
        STRATEGY_MAP.put(StrategyEnum.B, new StrategyB());
    }

    /**
     * 对外只提供获取策略的方法
     */
    public Strategy getStrategy(StrategyEnum strategyEnum) {
        if (null == strategyEnum) {
            throw new IllegalArgumentException("This strategyEnum is not exist!");
        }
        return STRATEGY_MAP.get(strategyEnum);
    }
}

2.3 使用

/**
 * 策略使用
 */
@RunWith(JUnit4.class)
public class StrategyTest {

    /**
     * 测试策略的使用
     */
    @Test
    public void testGetStrategy() {
        StrategyFactory.getStrategy(StrategyEnum.A);
        strategy.executeStrategy();
    }
}

这种属于【有状态】的策略模式,当然还有无状态的策略模式。

三、Spring策略模式的应用

使用spring的时候,我们可以使用@PostConstruct注解,在spring加载完毕对象后,进行后置处理的时候,将实现类放入map中。定义部分相同,其他有所改动。

3.1 创建

/**
 * spring的策略管理
 */
@Component
public class StrategySpringFactory {

    /**
     * 管理所有策略的Map
     */
    private Map<StrategyEnum, Strategy> strategyMap = new HashMap<>(2);

    /**
     * 将策略注入
     */
    @Resource
    private Strategy strategyA;
    @Resource
    private Strategy strategyB;

    /**
     * 初始化:后置处理器
     */
    @PostConstruct
    public void init() {
        strategyMap.put(StrategyEnum.A, strategyA);
        strategyMap.put(StrategyEnum.B, strategyB);
    }

    /**
     * 对外只提供获取策略的方法
     */
    public Strategy getStrategy(StrategyEnum strategyEnum) {
        if (null == strategyEnum) {
            throw new IllegalArgumentException("This strategyEnum is not exist!");
        }
        return strategyMap.get(strategyEnum);
    }
}

3.2 使用

/**
 * 策略使用
 */
@SpringBootTest(classes = AppStart.class)
@RunWith(SpringRunner.class)
public class StrategyTest {

    /**
     * 注入工厂
     */
    @Resource
    private StrategySpringFactory strategySpringFactory;

    /**
     * 策略模式使用
     */
    @Test
    public void testGetStrategySpring() {
        Strategy strategy = strategySpringFactory.getStrategy(StrategyEnum.B);
        strategy.executeStrategy();
    }
}

四、策略模式+反射的实际案例

场景描述 :有一个活动(这里设置为招收活动),会有很多的规则,任何人都可以报名,但是必须满足条件,为了方便未来增加新的活动,代码好维护,我们需要使用枚举作为策略,同时使用反射统一化代码。

案例代码如下(省略set.get):

/*
* Person类,父类,任何人,具备基础属性
*/
public class Person {
    /*姓名*/
    private String name;

    /*年龄*/
    private Integer age;

    /*失信率*/
    private Double breakRate;
}

/*
* teacher类,后面可以根据需求继续增加属性或者职业
*/
public class Teacher extends Person {
    /*职级*/
    private Integer level;
}

/*
* student类,后面可以根据需求继续增加属性或者职业
*/
public class Student extends Person {
    /*分数*/
    private Double score;
}

/*
* 活动类,具备规则属性,即招收要求,根据枚举去找对应的比较方式
*/
public class EmployRule {
    /*规则名称*/
    private String ruleName;

    /*规则标识*/
    private String ruleProperty;

    /*规则值*/
    private String ruleValue;
}

/*
* 规则枚举,其中还有枚举,分别是比较类型,以及值类型
*/
public enum  EmployRuleEnum {
    MIN_AGE("最小年龄", "age", RuleTypeEnum.GREATER_EQUAL, DataTypeEnum.INT),
    MIN_LEVEL("最小职级", "level", RuleTypeEnum.GREATER_EQUAL, DataTypeEnum.INT),
    MIN_SCORE("最低分数", "score", RuleTypeEnum.GREATER_EQUAL, DataTypeEnum.DOUBLE),
    MAX_BREAK_RATE("最高失信率", "breakRate", RuleTypeEnum.LESS_EQUAL, DataTypeEnum.DOUBLE);

    /*规则名称*/
    private String ruleName;

    /*规则标识*/
    private String ruleProperty;

    /*规则类型(大于,小于,等于)*/
    private RuleTypeEnum ruleType;

    /*值类型(整数、浮点)*/
    private DataTypeEnum dataType;

    /**
     * 根据property获取枚举类
     * @param property
     * @return
     */
    public static EmployRuleEnum getByProperty(String property){
        if(StringUtils.isEmpty(property)){
            return null;
        }
        for(EmployRuleEnum ruleEnum : EmployRuleEnum.values()){
            if(property.equals(ruleEnum.getRuleProperty())){
                return ruleEnum;
            }
        }
        return null;
    }

    EmployRuleEnum(String ruleName, String ruleProperty, RuleTypeEnum ruleType, DataTypeEnum dataType) {
        this.ruleName = ruleName;
        this.ruleProperty = ruleProperty;
        this.ruleType = ruleType;
        this.dataType = dataType;
    }
}

/*
* 比较类型
*/
public enum RuleTypeEnum {
	/*大于等于*/
    GREATER_EQUAL(1, "大于等于"),
    /*小于等于*/
    LESS_EQUAL(2, "小于等于")
    ;
    /*类型*/
    private int type;
    /*描述*/
    private String desc;

    /**
     * 根据type获取枚举
     * @param type
     * @return
     */
    public static RuleTypeEnum getByType(Integer type){
        if(type == null){
            return null;
        }
        for(RuleTypeEnum typeEnum : RuleTypeEnum.values()){
            if(typeEnum.type == type){
                return typeEnum;
            }
        }
        return null;
    }

    RuleTypeEnum(int type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

/*
* 值类型
*/
public enum DataTypeEnum {
    INT(1, "整数") {
        @Override
        public int compare(String value, Object o) throws Exception {
            if (!(o instanceof Integer)) {
                throw new Exception();
            }
            Integer obj = (Integer) o;
            return Integer.valueOf(value).compareTo(obj); //规则中获取的值-用户传来的数据值
        }
    },
    LONG(2, "长整型") {
        @Override
        public int compare(String value, Object o) throws Exception {
            if (!(o instanceof Long)) {
                throw new Exception();
            }
            Long obj = (Long) o;
            return Long.valueOf(value).compareTo(obj);
        }
    },
    FLOAT(3, "浮点型") {
        @Override
        public int compare(String value, Object o) throws Exception {
            if (!(o instanceof Float)) {
                throw new Exception();
            }
            Float obj = (Float) o;
            return Float.valueOf(value).compareTo(obj);
        }
    },
    DOUBLE(4, "长浮点型") {
        @Override
        public int compare(String value, Object o) throws Exception {
            if (!(o instanceof Double)) {
                throw new Exception();
            }
            Double obj = (Double) o;
            return Double.valueOf(value).compareTo(obj);
        }
    };

    /**
     * 类型标识
     */
    private Integer type;

    /**
     * 描述
     */
    private String desc;

    /**
     * 根据type查询枚举类
     * @param type
     * @return
     */
    public static DataTypeEnum getByDataType(Integer type){
        if(type == null){
            return null;
        }
        for(DataTypeEnum dataTypeEnum : DataTypeEnum.values()){
            if(dataTypeEnum.getType().equals(type)){
                return dataTypeEnum;
            }
        }
        return null;
    }

    /**
     * 比较方法,用于枚举方法重写
     * @param value
     * @param o
     * @return
     * @throws Exception
     */
    public abstract int compare(String value, Object o)throws Exception;
    DataTypeEnum(Integer type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

/**
* service方法实现
*/
public class VerifyServiceImpl implements VerifyService {

    /*json*/
    private static final ObjectMapper objectMaper = new ObjectMapper();

    /*日志*/
    private static Logger logger = Logger.getLogger(VerifyServiceImpl.class.getName());

    @Override
    public void doVerify(Person person, EmployRule rule) throws Exception {
        logger.info(String.format("VerifyServiceImpl.doVerify 入参:person = %s, rule = %s", objectMaper.writeValueAsString(person), objectMaper.writeValueAsString(rule)));
        //校验参数
        String ruleName = rule.getRuleName();//规则名称
        String ruleProperty = rule.getRuleProperty();//规则标识
        String ruleValue = rule.getRuleValue();//规则值
        if (null == person || StringUtils.isEmpty(ruleProperty)
                || StringUtils.isEmpty(ruleValue) || StringUtils.isEmpty(ruleName)) {
            throw new Exception("入参有空值");
        }

        //获取值类型的枚举类
        EmployRuleEnum property = EmployRuleEnum.getByProperty(ruleProperty);
        if (null == property) {
            throw new Exception("数据类型不存在");
        }
        DataTypeEnum dataType = property.getDataType();

        //获取规则类型:大于,小于,等于
        RuleTypeEnum ruleTypeEnum = property.getRuleType();
        if (null != ruleTypeEnum) {
            switch (ruleTypeEnum) {

                //小于等于
                case LESS_EQUAL:
                    if (this.compare(person, ruleProperty, dataType, ruleValue) <= 0) {
                        throw new Exception(String.format("%s 不符合要求", ruleName));
                    }
                    return;

                //大于等于
                case GREATER_EQUAL:
                    if (this.compare(person, ruleProperty, dataType, ruleValue) >= 0) {
                        throw new Exception(String.format("%s 不符合要求", ruleName));
                    }
                    return;

                default:
                    throw new Exception("未知规则类型");
            }
        }
    }

    /**
     * 比较的共用方法,反射实现
     * @param person 传入的的获取,可以获取对应属性的值
     * @param ruleProperty 规则属性标识
     * @param valueType 值类型(int, long, float, double)的枚举
     * @param ruleValue 规则的值
     * @return 比较结果
     * @throws Exception
     */
    private int compare(Person person, String ruleProperty, DataTypeEnum valueType, String ruleValue) throws Exception {

        /*反射处理*/
        Class<? extends Person> clazz = person.getClass();

        try {
            //根据名称获取某个变量的值(私有)
            Field field = null;

            /*Person是父类,传入的可能是其子类,这里分开处理*/
            //子类中获取(私有)
            try {
                field = clazz.getDeclaredField(ruleProperty);
            } catch (Exception e) {
            }

            //父类中获取(私有)
            if (null == field) {
                try {
                    field = clazz.getSuperclass().getDeclaredField(ruleProperty);
                } catch (Exception e) {
                }
            }

            //设置私有变量访问权限
            if (null == field) {
                throw new Exception("获取到的规则值为空");
            }
            field.setAccessible(true);

            //返回指定对象上该field字段对应的值
            Object o = field.get(person);
            if (null == o) {
                throw new Exception("获取不到对象中实际的值");
            }

            //调用数据类型枚举的比较方法进行比较(规则值-对象值)
            return valueType.compare(ruleValue, o);
        } catch (Exception e) {
            logger.info(String.format("compare 比较方法反射获取不到属性, ruleProperty = %s, exceptionMessage = %s",ruleProperty , e.toString()));
            throw new Exception("反射方法异常,获取不到值!");
        }
    }
}

也可以使用java8的函数式接口Comparator接口

public enum DataTypeEnum {

    INT(1, "整型"){
        @Override
        public int compare(String ruleValue, Object o) {
            Integer value = (Integer)o;
            Comparator<Integer> comparator = Integer::compareTo;
            return comparator.compare(Integer.valueOf(ruleValue), value);
        }
    },
    DOUBLE(2, "浮点") {
        @Override
        public int compare(String ruleValue, Object o) {
            Double value = (Double)o;
            Comparator<Double> comparator = Double::compareTo;
            return comparator.compare(Double.valueOf(ruleValue), value);
        }
    }
    ;
    public abstract int compare(String ruleValue, Object o);
    
    private Integer type;
    private String desc;

    DataTypeEnum(Integer type, String desc) {
        this.type = type;
        this.desc = desc;
    }

    public Integer getType() {
        return type;
    }

    public String getDesc() {
        return desc;
    }

    public static DataTypeEnum getDataTypeEnumByType(Integer type){
        if (null == type) {
            return null;
        }
        for (DataTypeEnum bean : DataTypeEnum.values()) {
            if (type.equals(bean.getType())) {
                return bean;
            }
        }
        return null;
    }
}

@Test
public void testIDoNotKnowWhatYouWantToTest() {
    Integer dataType = 2;
    DataTypeEnum dataTypeEnum = DataTypeEnum.getDataTypeEnumByType(dataType);
    int compare;
    switch (dataTypeEnum) {
        case INT:
            compare = dataTypeEnum.compare("20", 50);
            System.out.println(compare);
            break;
        case DOUBLE:
            compare = dataTypeEnum.compare("7.5",7.5);
            System.out.println(compare);
        default:
            break;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值