介绍
在策略模式(Strategy Pattern)中,一个类的行为或其算法(方法)可以在运行时更改。这种类型的设计模式属于 行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
主要解决
在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用
将这些算法封装成一个一个的类,实现同一个接口。
实现(简单)
步骤 1
创建一个接口。
Strategy.java
public interface Strategy {
public int doOperation(int num1, int num2);
}
步骤 2
创建实现接口的实体类。
OperationAdd.java
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
OperationSubtract.java
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
OperationMultiply.java
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
步骤 3
创建 Context 类。
Context.java
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
步骤 4
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
StrategyPatternDemo.java
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
复杂应用(Spring或SpringBoot中使用)
两种方式(未使用策略工厂模式)
两种方式都用到了枚举的方式去创建策略模式,基本上功能一样,实现方式不一样。具体得看自己业务中怎么使用。第一种比较简单,第二种适合复杂的类。
- 在枚举中指定类名
- 使用注解扫描用注解描述的类
代码如下
第一种方式
Strategy.java 策略接口
public interface Strategy {
void doAction(String modelNum);
}
StrategyA.java 策略接口实现类A
//通过指明类名进行扫描类
@Service("strategyA")
public class StrategyA implements Strategy{
@Override
public void doAction(String modelNum) {
System.out.println(modelNum +"开始了!");
}
}
StrategyB.java 策略接口实现类B
//通过指明类名进行扫描类
@Service("strategyB")
public class StrategyB implements Strategy{
@Override
public void doAction(String modelNum) {
System.out.println(modelNum + "开始了!");
}
}
StrategyContent.java 策略容器(上下文)
@Component
public class StrategyContent {
//通过@Autowired注解向map对象中注入以类名为key具体策略类为value的对象
@Autowired
private final Map<String,Strategy> strategyMap = new ConcurrentHashMap<>(3);
public Strategy getModelService(int num){
ModelEnum instance = ModelEnum.getInstance(num);
return strategyMap.get(instance.getModelStr());
}
}
ModelEnum.java 枚举类 注意:这里的枚举中标示了具体的类名字
public enum ModelEnum {
//第二个参数标示了具体的类名字,在策略容器中会自动进行注入
//注入到策略容器Map对象中,这样就可以方便动态的调用那个策略实现类的方法
MODEL_1(1,"strategyA","one"),
MODEL_2(2,"strategyB","two"),
MODEL_3(3,"strategyC","three");
private int modelNum;
private String modelStr;
private String modelDefault;
ModelEnum(int modelNum, String modelStr, String modelDefault) {
this.modelNum = modelNum;
this.modelStr = modelStr;
this.modelDefault = modelDefault;
}
public static ModelEnum getInstance(int modelNum){
for(ModelEnum modelEnum : ModelEnum.values()){
if(modelEnum.modelNum == modelNum){
return modelEnum;
}
}
return null;
}
public String getModelStr(){
return modelStr;
}
public Integer getModelNum(){
return modelNum;
}
public String getModelDefault(){
return modelDefault;
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JebeApplication.class)
public class StrategyModelTest {
//通过spring注入策略容器
@Resource
StrategyContent strategyContent;
@Test
public void test(){
int modelNum_1 = 1;
int modelNum_2 = 2;
int modelNum_3 = 3;
strategyContent.getModelService(modelNum_1).doAction("执行模式1");
}
}
第二种方式
QualifierModel.java 自定义注解类,会在策略实现类中使用
@Documented
@Inherited
@Target(ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface QualifierModel {
ModelEnum value();
}
Strategy.java 策略类接口
public interface Strategy {
void doAction(String modelNum);
}
StrategyOne.java 策略实现类One
//使用自定义的注解类进行标注策略实现类,供监听器扫描
//@Service注解并未指明具体的类名
@Service
@QualifierModel(ModelEnum.MODEL_1)
public class StrategyOne implements Strategy{
@Override
public void doAction(String modelNum) {
System.out.println(modelNum +"开始了");
}
}
StrategyTwo.java 策略实现类Two
@Service
@QualifierModel(ModelEnum.MODEL_2)
public class StrategyTwo implements Strategy{
@Override
public void doAction(String modelNum) {
System.out.println(modelNum +"开始了");
}
}
StrategyContent2.java 策略容器
@Component
public class StrategyContent2 {
//此处并未使用@Autowired进行自动注入,而是使用监听器进行扫描
private final Map<ModelEnum,Strategy> strategyMap = new ConcurrentHashMap<>(3);
public Strategy getModelService(ModelEnum modelEnum){
return strategyMap.get(modelEnum);
}
public void setStrategyMap(ModelEnum modelEnum,Strategy strategy){
strategyMap.put(modelEnum,strategy);
}
}
ModelEnum.java 枚举类
public enum ModelEnum {
MODEL_1(1,"strategyA","one"),
MODEL_2(2,"strategyB","two"),
MODEL_3(3,"strategyC","three");
private int modelNum;
private String modelStr;
private String modelDefault;
ModelEnum(int modelNum, String modelStr, String modelDefault) {
this.modelNum = modelNum;
this.modelStr = modelStr;
this.modelDefault = modelDefault;
}
public static ModelEnum getInstance(int modelNum){
for(ModelEnum modelEnum : ModelEnum.values()){
if(modelEnum.modelNum == modelNum){
return modelEnum;
}
}
return null;
}
public String getModelStr(){
return modelStr;
}
public Integer getModelNum(){
return modelNum;
}
public String getModelDefault(){
return modelDefault;
}
}
ModelListener.java 自定义监听类,在项目启动时通过实现ApplicationListener重写方法,扫描自定义的注解类获取到使用此注解标注的所有策略实现类,然后调用策略容器的Map属性的set方法给其进行赋值初始化。
@Component
public class ModelListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Map<String, Object> beansWithAnnotation = contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(QualifierModel.class);
StrategyContent2 content2 = contextRefreshedEvent.getApplicationContext().getBean(StrategyContent2.class);
beansWithAnnotation.forEach((name,bean) ->{
QualifierModel annotation = bean.getClass().getAnnotation(QualifierModel.class);
content2.setStrategyMap(annotation.value(),(Strategy) bean);
});
}
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = JebeApplication.class)
public class StrategyModelTest {
@Autowired
StrategyContent2 strategyContent2;
@Test
public void test2(){
int modelNum_1 = 1;
int modelNum_2 = 2;
int modelNum_3 = 3;
//使用枚举进行获取具体的策略实现类
strategyContent2.getModelService(ModelEnum.getInstance(modelNum_3)).doAction("执行模式3");
}
}
参考文章:
https://www.runoob.com/design-pattern/strategy-pattern.html
https://blog.csdn.net/wo541075754/article/details/109253234