设计模式之策略模式

设计模式之策略模式

一、策略模式定义:就是定义一组算法,每种算法都封装起来,并且使他们之间可以相互替换。

策略模式使用的是面向对象的继承和多态的机制

策略模式中三个角色:Context封装角色、Strategy抽象策略角色、ConcreteStrategy具体策略角色

  * Context 封装角色:上下文角色,主要就是起到封装的作用。屏蔽高层模块对算法、策略的直接访问,封装可能存在的变化

  * Strategy抽象策略角色:策略、算法家族的抽象。

  * ConcreteStrategy :实现抽象策略中的操作,该类具有具体的算法

二、策略模式类图:

  

 

  

通用源码格式,写起来也很简单

a.抽象的策略角色:

public interface Strategy {
    // 策略模式的远算法则
    public void doSomething();
}
View Code

b.具体的策略角色

// 具体实现1@Slf4j
public class ConcreteStrategy1 implements Strategy {
    @Override
    public void doSomething() {
        log.info("策略1的实现");
    }
}
// 具体实现2
@Slf4j
public class ConcreteStrategy2 implements Strategy {
    @Override
    public void doSomething() {
        log.info("策略2的实现");
    }
}
View Code

c.封装角色

public class Context {
    // 抽象策略
    private Strategy strategy = null;

    // 构造函数设置具体策略
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    // 执行
    public void exec() {
        strategy.doSomething();
    }
}
View Code

d.高层实现

public class Client {

    public static void main(String[] args) {
        //1.声明一个具体的策略
        Strategy strategy = new ConcreteStrategy1();
        //2.声明一个上下文对象
        Context context = new Context(strategy);
        // 执行封装后的方法
        context.exec();
    }
}
View Code

三、策略模式实现案例一:

注解+反射 的方式实现策略模式

从北京前往河南的出行方式

1.设计一个抽象策略类

public interface TravelStrategy {
    /**
     * 出行方式
     */
    void tripMode();
}
View Code

2.创建绿皮火车、高铁、飞机三种出行方式,不同经济身份的人,做不同的工具进行出行

@Slf4j
@Component
@Strategy(value = "green_trip")
public class GreenTrip implements TravelStrategy{
    @Override
    public void tripMode() {
        log.info("绿皮火车的出行方式");
    }
}
View Code
@Slf4j
@Component
@Strategy(value="high_speed_trip")
public class HighSpeedRailTrip implements TravelStrategy {
    @Override
    public void tripMode() {
        log.info("高铁的出行方式");
    }
}
View Code
@Slf4j
@Component
@Strategy(value = "airplane_trip")
public class AirplaneTrip implements TravelStrategy {
    @Override
    public void tripMode() {
        log.info("飞机的出行方式");
    }
}
View Code

3.创建一个策略注解

@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Strategy {
    String value();
}
View Code

4.创建一个获取对象的工具类

@Component
public class BeanUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanUtils.applicationContext = applicationContext;
    }

    public static Object getBean(Class clazz) {
       return  applicationContext.getBean(clazz);
    }
}
View Code

5.创建一个策略工厂

public class StrategyFactory {

    private static volatile StrategyFactory instance;

    private StrategyFactory() {

    }

    public static StrategyFactory getInstance() {
        if (instance == null) {
            synchronized (StrategyFactory.class) {
                if (instance == null) {
                    instance = new StrategyFactory();
                }
            }
        }
        return instance;
    }

    private static final String TRIP_STRATEGY_PACKAGE = "com.xxx.strategy";

    private static final HashMap<String, Class> strategyMap = new HashMap<>();

    static {
        Reflections reflections = new Reflections(TRIP_STRATEGY_PACKAGE);
        Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Strategy.class);
        typesAnnotatedWith.forEach(aClass -> {
            Strategy annotation = aClass.getAnnotation(Strategy.class);
            strategyMap.put(annotation.value(), aClass);
        });
    }

    /**
     * 根据固定的key,获取固定的策略
     *
     * @param type
     * @return
     */
    public static TravelStrategy getStrategy(String type) {
        Class aClass = strategyMap.get(type);
        if (aClass == null) {
            return null;
        }
        return (TravelStrategy) BeanUtils.getBean(aClass);
    }

}
View Code

6.创建一个controller

@RequestMapping("/travel")
@RestController
public class TravelController {

    @RequestMapping("/trip")
    public void trip(@RequestParam String person) {
        TravelStrategy strategy = StrategyFactory.getStrategy(TripModeEnum.getValue(person));
        strategy.tripMode();
    }
}
View Code

7.最后创建一个枚举类

public enum TripModeEnum {
    /**
     * 绿皮火车
     */
    GREENTRAIN("1", "green_trip"),
    HIGH_SPEED_RAIL_TRIP("2", "high_speed_trip"),
    AIRPHONE("3", "airplane_trip");

    private String personType;

    private String strategy;

    private TripModeEnum(String personType, String strategy) {
        this.personType = personType;
        this.strategy = strategy;
    }

    public String getPersonType() {
        return personType;
    }

    public void setPersonType(String personType) {
        this.personType = personType;
    }

    public String getStrategy() {
        return strategy;
    }

    public void setStrategy(String strategy) {
        this.strategy = strategy;
    }

    public static String getValue(String key) {
        TripModeEnum[] values = TripModeEnum.values();
        for (TripModeEnum modeEnum : values) {
            if (modeEnum.getPersonType().equals(key)) {
                return modeEnum.getStrategy();
            }
        }
        return null;
    }
}
View Code

 这种是传统的策略模式实现方式,当时有个缺点就是类会增多。每一个策略都是一个类

 

策略模式实现案例二

利用Map与函数式接口来实现

同样是出行方式的案例

这里使用的是函数式接口,当然也可以自己写的普通的接口。实际业务情况,可能需要自定义,这里使用的BiConsume,我比较懒,直接用现成的接口

1.创建一个出行策略类

@Slf4j
@Service
public class TripModeStrategy {

    @Autowired
    private TripService tripService;

    /**
     * 策略map
     */
    private Map<String, BiConsumer<String, String>> strategyMap = new HashMap<>();

    @PostConstruct
    public void initMap() {
        strategyMap.put(TripDictConstant.GREEN_TRAIN, (person, sex) -> tripService.greenTrip(person, sex));
        strategyMap.put(TripDictConstant.HIGH_SPEED_RAIL_TRIP, (person, sex) -> tripService.highSpeedRail(person, sex));
        strategyMap.put(TripDictConstant.AIRPHONE, (person, sex) -> tripService.airphone(person, sex));
    }

    /**
     * 出行方式实现类
     *
     * @param key    key
     * @param person person
     * @param sex    sex
     */
    public void tripMode(String key, String person, String sex) {
        BiConsumer<String, String> tripStrategy = strategyMap.get(key);
        if (tripStrategy == null) {
            log.info("没有找到出行策略");
            return;
        }
        tripStrategy.accept(person, sex);

    }
}
View Code

2.创建一个具体的策略实现类

@Slf4j
@Service
public class TripService {

    /**
     * 绿皮火车出行方式
     */
    public void greenTrip(String person, String sex) {
        log.info("姓名" + person);
        log.info("性别" + sex);
        log.info("坐车绿皮火车环绕地球了");
    }

    /**
     * 高铁出行方式
     */
    public void highSpeedRail(String person, String sex) {
        log.info("姓名" + person);
        log.info("性别" + sex);
        log.info("坐车高铁。。。。。。");
    }

    /**
     * 飞机的出行方式
     */
    public void airphone(String person, String sex) {
        log.info("姓名" + person);
        log.info("性别" + sex);
        log.info("坐车飞机。。。飞升了");
    }
}
View Code

3.创建一个数据字典类

public class TripDictConstant {
    /**
     * 绿皮火车的出行方式
     */
    public static final String GREEN_TRAIN = "1";
    /**
     * 高铁的出行方式
     */
    public static final String HIGH_SPEED_RAIL_TRIP = "2";
    /**
     * 飞机的出行方式
     */
    public static final String AIRPHONE = "3";
}
View Code

4.创建controller接口类

@RequestMapping("/travel")
@RestController
public class TravelController {

    @Autowired
    private TripModeStrategy tripModeStrategy;

    @RequestMapping("/trip")
    public void trip(@RequestParam String key,@RequestParam  String person,@RequestParam  String sex) {
        tripModeStrategy.tripMode(key,person,sex);
    }
}
View Code

调用postman。执行结果。结果土拨鼠。坐车小飞机,不可思议的飞升了

使用这种方式,实现策略模式,类的使用明显少了。也更加的直观

策略模式实现案例三:    

枚举值使用策略模式

1.创建一个枚举值策略类

@Slf4j
public enum TripEnumStrategy {

    /**
     * 绿皮火车
     */
    GREENTRAIN("1", "green_train") {
        @Override
        public void tripMode(String person, String sex) {
            log.info("姓名" + person);
            log.info("性别" + sex);
            log.info("坐车绿皮火车环绕地球了");
        }
    },
    HIGH_SPEED_RAIL_TRIP("2", "high_speed_trip") {
        @Override
        public void tripMode(String person, String sex) {
            log.info("姓名" + person);
            log.info("性别" + sex);
            log.info("坐车高铁火车环绕地球了");
        }
    },
    AIRPHONE("3", "airplane_trip") {
        @Override
        public void tripMode(String person, String sex) {
            log.info("姓名" + person);
            log.info("性别" + sex);
            log.info("坐者飞机。。。上天了");
        }
    };

    private String personType;

    private String strategy;

    private TripEnumStrategy(String personType, String strategy) {
        this.personType = personType;
        this.strategy = strategy;
    }


    public String getPersonType() {
        return personType;
    }

    public void setPersonType(String personType) {
        this.personType = personType;
    }

    public String getStrategy() {
        return strategy;
    }

    public void setStrategy(String strategy) {
        this.strategy = strategy;
    }

    public static String getValue(String key) {
        TripEnumStrategy[] values = TripEnumStrategy.values();
        for (TripEnumStrategy modeEnum : values) {
            if (modeEnum.getPersonType().equals(key)) {
                return modeEnum.name();
            }

        }
        return null;
    }

    public abstract void tripMode(String personType, String sex);
}
View Code

2.创建一个controller

@RequestMapping("/travel")
@RestController
public class TripController {


    @RequestMapping("/trip")
    public void trip(@RequestParam String key, @RequestParam String person, @RequestParam String sex) {
        String stragegy = TripEnumStrategy.getValue(key);
        TripEnumStrategy.valueOf(stragegy).tripMode(person, sex);
    }
}
View Code

运行执行结果

根据类型实现了策略之间的相互转换

 

四、策略模式的优缺点

  优点:

    1.算法之间可以自由切换

    2.避免多重条件判断

      如果不适用策略模式,如果一个接口功能,业务类型有20多种,一会儿要用A策略,一会儿要用B策略。使用多重if语句,多重条件不容易维护,而且代码写的臃肿不堪,可读性极差,出错概率极大。

      想想都是恐怖,我在公司看着别人写的代码,几千行的方法,嵌套个不停,我真是心累的很,深有体会

    3.扩展性良好

      体现在新增加一个策略,只要实现接口就可以了。其他的都不用修改,类似于一个可反复拆卸的插件

  缺点:

    1.策略类数量较多

    2.所有的策略类都需要对外暴露

五、策略类的使用场景

  1.多个类只有在算法或者行为上稍有不同的场景

  2.算法需要自由切换的场景

    算法的选择是使用者来决定的。或者算法始终在进化。特别是一些技术前沿的行业。连业务规则都无法告诉你这样的业务规则能用多长时间。在这种情况下使用策略模式

  3.需要屏蔽算法规则的场景

    太多的算法,只需要记住一个名字就好了,传递想关的数据key,返回一个结果,就好了

注意:如果一个策略类家族中具体的策略数量超过4个,就要考虑解决类膨胀、和对外暴露的问题。要不然日后维护的时候,就难受成皮皮虾了

 

诶!项目组的成员,架构师写的代码真心很优秀,羡慕。一般的开发写的代码,真是臃肿,如果不是可读性、可维护性、可扩展性太差,让我接手的时候,难受不已,我可能也不会重新学习设计模式,我可能也就是完成功能实现,不报错,就完事了。真是被逼的。

posted @ 2020-07-05 23:42  莹小草  阅读(411)  评论(0)    收藏  举报