在SpringBoot中實現策略模式


首先定義一個Strategy接口來表示一個策略:

public interface Strategy {
    String flag();
    void process();
}

其中flag方法返回當前策略的唯一標識,process則是該策略的具體執行邏輯。

下面是Strategy接口的兩個實現類:

public class StrategyImpl1 implements Strategy {
    @Override
    public String flag() {
        return "s1";
    }

    @Override
    public void process() {
        System.out.println("strategy 1");
    }
}

public class StrategyImpl2 implements Strategy {
    @Override
    public String flag() {
        return "s2";
    }

    @Override
    public void process() {
        System.out.println("strategy 2");
    }
}

然后定義一個StrategyRunner接口用來表示策略的調度器:

public interface StrategyRunner {
    void run(String flag);
}

run方法內部通過判斷flag的值來決定具體執行哪一個策略。

下面是一個簡單的StrategyRunner

public class StrategyRunnerImpl implements StrategyRunner {
    private static final List<Strategy> STRATEGIES = Arrays.asList(new StrategyImpl1(), new StrategyImpl2());
    private static final Map<String, Strategy> STRATEGY_MAP;

    static {
        STRATEGY_MAP = STRATEGIES.stream()
                .collect(Collectors.toMap(Strategy::flag, s -> s));
    }

    @Override
    public void run(String flag) {
        STRATEGY_MAP.get(flag).process();
    }
}

StrategyRunnerImpl內部,定義了一個STRATEGIES列表來保存所有Strategy實現類的實例,以及一個叫做STRATEGY_MAPMap來保存flagStrategy實例之間的對應關系,static塊中的代碼用於從STRATEGIES列表構造STRATEGY_MAP。這樣,在run方法中就可以很方便地獲取到指定flagStrategy實例。

這個實現雖然簡單,但是它有個很大的缺點,想象一下,如果我們想添加新的Strategy實現類,我們不僅需要添加新的實現類,還要修改STRATEGIES列表的定義。這樣就違反了“對擴展開放,對修改關閉”的原則。

借助於Spring的IOC容器和SpringBoot的自動配置,我們可以以一種更加優雅的方式實現上述策略模式。

首先,我們繼續使用StrategyImpl1StrategyImpl2這兩個實現類。不過,為了將它們注冊進Spring的IOC容器,需要給他們標注上Component注解:

@Component
public class StrategyImpl1 implements Strategy {
    ...
}

@Component
public class StrategyImpl2 implements Strategy {
    ...
}

然后,寫一個StrategyConfig配置類,用於向容器中注冊一個StrategyRunner

@Configuration
public class StrategyConfig {
    @Bean
    public StrategyRunner strategyRunner(List<Strategy> strategies) {
        Map<String, Strategy> strategyMap = strategies.stream()
                .collect(Collectors.toMap(Strategy::flag, s -> s));
        return flag -> strategyMap.get(flag).process();
    }
}

仔細看strategyRunner方法的實現,不難發現,其中的邏輯與之前的StrategyRunnerImpl幾乎完全相同,也是根據一個List<Strategy>來構造一個Map<String, Strategy>。只不過,這里的strategies列表不是我們自己構造的,而是通過方法參數傳進來的。由於strategyRunner標注了Bean注解,因此參數上的List<Strategy>實際上是在SpringBoot初始化過程中從容器獲取的(還記得之前我們把兩個Strategy的實現類注冊進了容器嗎)。

這樣,我們再也無需操心系統中一共有多少個Strategy實現類,因為SpringBoot的自動配置會幫我們把它們全部收集起來。我們只需編寫自己的Strategy實現類,然后將它注冊進容器,並在任何需要的地方注入StrategyRunner

@Autowired
private StrategyRunner strategyRunner;

然后直接使用strategyRunner就行了:

strategyRunner.run("s1");
strategyRunner.run("s2");

控制台輸出如下:

strategy 1
strategy 2

也就是說,當我們想添加新的Strategy實現類時,我們只需添加新的代碼,而無需修改任何現有的代碼,這樣就完美地實現了“對擴展開放,對修改關閉”的目標。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM