设计模式是软件工程中对软件设计中普遍存在的问题提出的模板式解决方案。策略模式(Strategy Pattern)属于设计模式中行为型模式的一种。它的目的是定义一系列算法,并将每一个算法封装起来,使它们之间可以互换,且算法的变化不会影响使用算法的客户。策略模式让算法独立于使用它的客户端而变化,也称为政策模式。
策略模式有三个主要角色:
1. 抽象策略角色(Strategy):这是一个接口或抽象类,定义了算法的基本行为。在Java中,这个角色通常由接口或者抽象类来扮演。
2. 具体策略角色(ConcreteStrategy):实现了抽象策略角色定义的接口或抽象类的实体类。每一个具体策略角色都是一个算法的封装。
3. 封装角色(Context):它持有一个策略角色的引用,并提供一个设置策略的方法(setStrategy),或者一个构造函数(在创建对象的时候就设定策略)。Context角色还需要一个方法来执行策略中的算法。
策略模式在以下场景中特别适用:
- 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
- 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
策略模式的类图通常展示如下结构:
- Strategy:定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法。
- ConcreteStrategy:以Strategy接口实现某具体算法。
- Context:用一个ConcreteStrategy来配置;维护一个对Strategy对象的引用;可能定义一个接口来让Strategy访问它的数据。
下面是策略模式的一个简单示例代码,使用Java编写:
```java
// 抽象策略角色
public interface Strategy {
// 策略模式的运算法则
void doSomething();
}
// 具体策略角色
public class ConcreteStrategy1 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略1的运算法则");
}
}
// 具体策略角色
public class ConcreteStrategy2 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略2的运算法则");
}
}
// 封装角色
public class Context {
// 抽象策略
private Strategy strategy;
// 构造函数设置具体策略
public Context(Strategy strategy) {
this.strategy = strategy;
}
// 封装后的策略方法
public void doAnything() {
this.strategy.doSomething();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 拿到一个具体的策略
Strategy strategy = new ConcreteStrategy1();
// 创建上下文对象
Context context = new Context(strategy);
// 执行封装后的方法
context.doAnything();
}
}
```
在实际开发中,策略模式常常和工厂模式结合使用,以达到更好的解耦和灵活配置。例如,Spring框架中使用依赖注入的方式,可以将策略接口的所有实现类注入到一个工厂类中,通过工厂类来管理这些策略实现,并提供方法获取具体的策略实例。
以下是结合Spring框架的策略模式实现示例代码:
```java
// 定义策略接口
@Component
public interface Strategy {
// 策略模式的运算法则
void doSomething();
}
// 策略接口的实现类1
@Component("concreteStrategy1")
public class ConcreteStrategy1 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略1的运算法则");
}
}
// 策略接口的实现类2
@Component("concreteStrategy2")
public class ConcreteStrategy2 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略2的运算法则");
}
}
// 策略工厂,用于创建具体的策略实例
@Component
public class StrategyFactory {
// Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value为对应的策略实现类
@Autowired
private Map<String, Strategy> strategyMap;
public Strategy getStrategy(String strategyName) {
return strategyMap.get(strategyName);
}
}
// SpringbootDemoApplicationTests
@SpringBootTest
class SpringbootDemoApplicationTests {
@Autowired
private StrategyFactory strategyFactory;
@Test
public void test() {
// 通过策略工厂获取具体的策略实例,并执行其算法
strategyFactory.getStrategy("concreteStrategy1").doSomething();
}
}
```
通过上述代码我们可以看出,在Spring框架中,策略接口的实现类需要使用@Component注解标记为Spring的bean,StrategyFactory类中注入了一个Map,其中key是bean的id(在本例中是具体的策略名称),value是对应的策略实现类。这样策略工厂类就可以在运行时动态地获取具体的策略实例,并返回给调用者。这种方式极大地提高了代码的灵活性和可扩展性,同时降低了客户端与具体策略之间的耦合度。