JAVA:策略模式的实战使用

1、前言

什么是策略模式?

在处理一个东西时,如果有多种不同的处理方式,这是我们可以定义一个接口类,用于规范处理的步骤,而具体的处理方法交由实现类去解决,这就是策略模式。

在使用策略模式后,只需要将父类引用指向具体的子类对象即可,可以避免大量代码的重复实现。

2、策略模式的简单示例

打个比方:现在要将土豆做成土豆泥

先定义接口类,仅需定义在单一步骤中处理方式可不同的步骤

java 复制代码
public interface MakeTuDou {
    //切小
    void cut();
    //变熟
    void cooked();
    //变成土豆泥
    void ni();
}

定义一个土豆泥的简单做法

java 复制代码
public class TuDouToolEasy implements MakeTuDou{
    @Override
    public void cut() {
        System.out.println("用刀将土豆切成小块");
    }
    @Override
    public void cooked() {
        System.out.println("将土豆蒸熟");
    }
    @Override
    public void ni() {
        System.out.println("将土豆捣成土豆泥");
    }
}

定义一个土豆泥的进阶做法

java 复制代码
public class TuDouToolDifficulty implements MakeTuDou{
    @Override
    public void cut() {
        System.out.println("用削皮刀将土豆削皮");
        System.out.println("用刀将土豆切成片后,再切成条");
    }
    @Override
    public void cooked() {
        System.out.println("将土豆蒸熟");
    }
    @Override
    public void ni() {
        System.out.println("用破壁机将土豆打成土豆泥");
    }
}

这样仅需切换定义的子类,就可以实现土豆泥做法的切换,可以避免大量if else结构,让代码更简单易懂。

java 复制代码
public class Demo {
    public static void main(String[] args) {
        TuDouToolEasy td = new TuDouToolEasy();
//        TuDouToolEasy td = new TuDouToolDifficulty();
        System.out.println("清洗土豆,洗去泥土");
        td.cut();
        System.out.println("将切小的土豆放在水中侵泡一会儿");
        td.cooked();
        System.out.println("将土豆放凉");
        td.ni();
        System.out.println("加入调味料");
    }
}

运行结果:

3、策略模式的进阶实战

在上面的示例中,为大家展示了策略模式的简单用法,帮助大家对策略模式有了一个初步的了解。

相信大家也可以看出,在上述的示例中有一个极为明显的问题,就是在代码中很生硬的直接创建了对象实例。

一个两个还好,如果更多就会出现大量的if else或switch,以极其生硬的方式呈现出来,每多一种实现方式都会在上面增加一层,这是不被允许的。

接下来,我会将spring的ioc与策略模式相结合,教给大家一种可用于实际开发的策略模式。

3.1 定义注解类

假设A是被Component标记的注解,那么所有被A标记的类都会被收集到spring的ioc容器中。

注解中的value属性,就是区分不同实现类的依据。

java 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface DataInfo {
    String value() default "";
}

3.2 定义接口类

这里为做某一项事,定义步骤

java 复制代码
public interface DataProcess {
    String toClean(String s);
    void toShow(String s);
}

3.3 定义实现类

这里的实现类必须被DataInfo注解标记,好被注册到ioc容器中。

java 复制代码
@DataInfo("1")
public class MakeBig implements DataProcess {
    @Override
    public String toClean(String s) {
        s += "big";
        return s;
    }
    @Override
    public void toShow(String s) {
        System.out.println("我是big:" + s);
    }
}
java 复制代码
@DataInfo("2")
public class MakeSmall implements DataProcess {
    @Override
    public String toClean(String s) {
        s += "small";
        return s;
    }
    @Override
    public void toShow(String s) {
        System.out.println("我是small:" + s);
    }
}

3.4 定义收集类

在实现类被注册到ioc后,我们可以将不同接口的实现类收集到不同的区域中。

但因为在实际开发中通常会有多个场景需要使用到策略模式,为了避免每用一次策略模式就重写一个收集类,所以采用了一个收集父类,多个收集子类的模式。

收集父类:负责收集实现类的代码实现。

收集子类,负责启动收集这一过程,收集哪些实现类,获取区分依据。

3.4.1 收集父类

java 复制代码
@Data
public abstract class DemoRegister <k,v extends Annotation>{
//    这个是接口类
    private Class<k> dataInfoClass;
//    这个是标注的注解,用于收集和区分实现类
    private Class<v> annotation;
//    这是ioc容器,需要从这里获取实现类
    private ApplicationContext applicationContext;
//    采用map存放实现类,是因为在获取实现类时,要有区分依据
    private Map<String,k> map = new HashMap<>();
    public DemoRegister(Class<k> dataInfoClass,Class<v> annotation,ApplicationContext applicationContext){
        this.dataInfoClass = dataInfoClass ;
        this.annotation = annotation ;
        this.applicationContext = applicationContext ;
        init();
    }
    private void init(){
//        根据注解从容器中获取实现类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(annotation);
        beansWithAnnotation.values().stream().forEach(
                s-> {
//                    获取实现类上的注解
                    v annotationData = AnnotationUtils.findAnnotation(s.getClass(), annotation);
//                    获取区分字段
                    String key = getKey(annotationData);
//                    根据注解上的区分字段,来存放实现类对象
                    putData(key,(k)s);
                }
        );
    }
    public String getKey(v annotation){
//        因为这里采用了泛型,所以获取不到注解上的属性,这里是交给子类去获取
        throw new RuntimeException("本方法没有被实现");

    }
    public void putData(String key,k value){
        this.map.put(key,value);
    }
    public k getData(String key){
        k o = this.map.get(key);
        return o;
    }
}

3.4.2 收集子类

java 复制代码
@Component
public class DataInfoRegister extends DemoRegister<DataProcess, DataInfo> {
//    这里因为只有一个构造器,所以spring会自动传入applicationContext
    public DataInfoRegister(ApplicationContext applicationContext) {
        super(DataProcess.class, DataInfo.class,applicationContext);
    }
    @Override
    public String getKey(DataInfo dataInfo){
//        这里由子类自行处理,返回区分依据
        return dataInfo.value();
    }

}

3.5 运行演示

java 复制代码
@SpringBootTest
class DataInfoRegisterTest {
    @Autowired
    public DataInfoRegister dataInfoRegister;
    @Test
    public void test(){
        BeansManagement.getApplicationContext();

        DataProcess data = dataInfoRegister.getData("1");
        String clean = data.toClean("1");
        data.toShow(clean);

        DataProcess data1 = dataInfoRegister.getData("2");
        String clean1 = data1.toClean("2");
        data1.toShow(clean1);
    }
}

运行结果:

4、结语

在策略模式的进阶用法上,有几点要注意:

  1. 注解类、接口类、收集子类和实现类之间的对应关系是一对一对一对多。
  2. 注解类是区分实现类的重要依据,所以注解类上的区分字段的值一定是唯一的。
  3. 收集父类可以有多个子类,每个子类都代表着一个策略模式的使用。
  4. 注解类的属性值是在收集子类中被获取的。
相关推荐
就叫_这个吧3 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡3 小时前
AI 应用系统设计
java·开发语言·人工智能
城管不管3 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅3 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
AI绘画哇哒哒3 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent
最强小杰4 小时前
gpt-5.6-sol 频繁报 503 怎么办?区分容量熔断和限速 429 的排查方法 + 可复用 retry wrapper
java·人工智能·gpt·ai
今天AI了吗4 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·数据库·人工智能·python·sql·深度学习·机器学习
TELL5215 小时前
selenium webdriver 第二次初始化的异常
开发语言·python
动词ing5 小时前
【C语言】自定义函数+指针入门
c语言·开发语言·算法
重生之后端学习6 小时前
283. 移动零[简单]✅
开发语言·数据结构·算法·leetcode·职场和发展