Java 函数式接口实现策略模式

567 阅读2分钟

在使用策略模式时,我想小伙伴们经常有这样的不满,我的业务逻辑就3 4 行,你给我整一大堆类定义?有必要这么麻烦吗?我看具体的业务逻辑还需要去不同的类中,简单点行不行。

其实我们所不满的就是策略模式带来的缺点:1、策略类会增多 2、业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑

针对传统策略模式的缺点,在这分享一个实现思路,这个思路已经帮我解决了多个复杂if else的业务场景,理解上比较容易,代码上需要用到Java8的特性——利用Map与函数式接口来实现。

策略方法调用类,业务代码中注入此类调用doTask()方法,传入相关参数即可

package com.zwb.blog.admin.test;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
@RequiredArgsConstructor
@Slf4j
public class TestLambdaContext {
    /**
     * 封装策略方法 key:策略名称 value:对应策略方法
     * Function<泛型1,泛型2> 泛型1:function.apply()方法的入参类型,同时也是策略方法的入参类型  泛型2:function.apply()方法的返参类型,同时也是策略方法的返参类型
     * 可根据不同业务场景使用不同的函数式接口 如 UnaryOperator Consumer Predicate等 参考网址:https://www.runoob.com/java/java8-functional-interfaces.html
     */
    private static final Map<String, Function<Object, Object>> map = new HashMap<>();

    /**
     * 此处采用构造注入,可替换为其他注入方式
     */
    private final @NotNull TestLambdaStrategy testLambdaStrategy;

    /**
     * 初始化策略map对象
     */
    @PostConstruct
    public void init() {
        map.put("策略名称一", testLambdaStrategy::testOne);
        map.put("策略名称二", testLambdaStrategy::testTwo);
        map.put("策略名称三", testLambdaStrategy::testThree);
        map.put("策略名称四", testLambdaStrategy::testFour);
    }

    /**
     * 根据入参key从map中获取不同的策略方法执行
     *
     * @param key   策略名称
     * @param param 策略方法所需入参
     * @return
     */
    public static Object doTask(String key, Object param) {
        //根据key从map中获取对应的策略方法
        Function<Object, Object> function = map.get(key);
        if (function != null) {
            //传入策略方法所需参数,执行对应的策略方法
            return function.apply(param);
        }
        return null;
    }

}

策略方法类,该类中一个方法对应一个策略

package com.zwb.blog.admin.test;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
@Slf4j
public class TestLambdaStrategy {

    public Object testOne(Object s) {
        //执行业务方法......
        return "策略模式1,接受参数为:" + s;
    }

    public Object testTwo(Object s) {
        //执行业务方法......
        return "策略模式2,接受参数为:" + s;
    }

    public Object testThree(Object s) {
        //执行业务方法......
        return "策略模式3,接受参数为:" + s;
    }

    public Object testFour(Object s) {
        //执行业务方法......
        return "策略模式4,接受参数为:" + s;
    }

}

模拟调用

package com.zwb.blog.admin.test;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/blogs/test")
public class TestController {

    @GetMapping("/testMethod")
    public void testMethod() {
        String doTask = (String) TestLambdaContext.doTask("策略名称一", "123");
        System.out.println(doTask);

        doTask = (String) TestLambdaContext.doTask("策略名称二", "456");
        System.out.println(doTask);

        doTask = (String) TestLambdaContext.doTask("策略名称三", "789");
        System.out.println(doTask);

        doTask = (String) TestLambdaContext.doTask("策略名称四", "10 11 12");
        System.out.println(doTask);
    }

}

输出结果

image.png