1. 程式人生 > 其它 >在SpringBoot中實現策略模式

在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實現類時,我們只需新增新的程式碼,而無需修改任何現有的程式碼,這樣就完美地實現了“對擴充套件開放,對修改關閉”的目標。