設計模式之策略模式

設計模式之策略模式算法

1、策略模式定義:就是定義一組算法,每種算法都封裝起來,而且使他們之間能夠相互替換。設計模式

策略模式使用的是面向對象的繼承和多態的機制架構

策略模式中三個角色:Context封裝角色、Strategy抽象策略角色、ConcreteStrategy具體策略角色app

  * Context 封裝角色:上下文角色,主要就是起到封裝的做用。屏蔽高層模塊對算法、策略的直接訪問,封裝可能存在的變化ide

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

  * ConcreteStrategy :實現抽象策略中的操做,該類具備具體的算法工具

2、策略模式類圖:post

  

 

  

通用源碼格式,寫起來也很簡單學習

a.抽象的策略角色:this

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

3、策略模式實現案例一:

註解+反射 的方式實現策略模式

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

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

運行執行結果

根據類型實現了策略之間的相互轉換

 

4、策略模式的優缺點

  優勢:

    1.算法之間能夠自由切換

    2.避免多重條件判斷

      若是不適用策略模式,若是一個接口功能,業務類型有20多種,一下子要用A策略,一下子要用B策略。使用多重if語句,多重條件不容易維護,並且代碼寫的臃腫不堪,可讀性極差,出錯機率極大。

      想一想都是恐怖,我在公司看着別人寫的代碼,幾千行的方法,嵌套個不停,我真是心累的很,深有體會

    3.擴展性良好

      體如今新增長一個策略,只要實現接口就能夠了。其餘的都不用修改,相似於一個可反覆拆卸的插件

  缺點:

    1.策略類數量較多

    2.全部的策略類都須要對外暴露

5、策略類的使用場景

  1.多個類只有在算法或者行爲上稍有不一樣的場景

  2.算法須要自由切換的場景

    算法的選擇是使用者來決定的。或者算法始終在進化。特別是一些技術前沿的行業。連業務規則都沒法告訴你這樣的業務規則能用多長時間。在這種狀況下使用策略模式

  3.須要屏蔽算法規則的場景

    太多的算法,只須要記住一個名字就行了,傳遞想關的數據key,返回一個結果,就行了

注意:若是一個策略類家族中具體的策略數量超過4個,就要考慮解決類膨脹、和對外暴露的問題。要否則往後維護的時候,就難受成皮皮蝦了

 

誒!項目組的成員,架構師寫的代碼真心很優秀,羨慕。通常的開發寫的代碼,真是臃腫,若是不是可讀性、可維護性、可擴展性太差,讓我接手的時候,難受不已,我可能也不會從新學習設計模式,我可能也就是完成功能實現,不報錯,就完事了。真是被逼的。

相關文章
相關標籤/搜索