13、Java 策略模式从入门到实战

Java 策略模式从入门到实战(后端必看,附案例+面试考点)

前言:策略模式(Strategy Pattern)是Java设计模式中最实用的"行为型模式"之一,核心是"将算法/行为封装成独立策略类,让算法可动态切换、复用,且不影响客户端代码"。

很多Java后端开发者在开发中会遇到这样的问题:面对多种相似的业务逻辑(如多种支付方式、多种排序算法、多种校验规则),习惯用大量if-else或switch-case判断,导致代码臃肿、难以维护、扩展性差;面试时被问到"如何优化大量if-else""策略模式的核心是什么""策略模式在框架中的应用",常常答不全面。

本文从入门到实战,用极简语言拆解策略模式核心,结合可直接复制运行的入门案例、真实业务实战(Spring Boot环境),以及高频面试考点,带你吃透策略模式------新手能快速上手,中级开发者能落地项目,面试时能轻松应对追问,看完就能用在实际开发中。

一、为什么Java后端必须掌握策略模式?(痛点直击)

先看3个Java后端开发中最常见的场景,你一定遇到过,这也是策略模式的核心应用场景,更是面试中高频提及的"if-else优化"典型场景:

  • 场景1:电商支付系统,支持支付宝、微信支付、银联支付3种方式,不同支付方式的逻辑不同,若用if-else判断"支付类型",后续新增"京东支付""PayPal支付",需修改原有代码,违背开闭原则;

  • 场景2:后台管理系统的排序功能,支持按时间排序、按金额排序、按名称排序,不同排序逻辑独立,若用switch-case整合,代码会随着排序方式增加而变得冗长,难以维护;

  • 场景3:接口参数校验,不同接口的校验规则不同(如登录校验手机号+密码,注册校验手机号+验证码+密码),若将所有校验逻辑写在一个方法里,会导致方法臃肿,复用性极差。

这些场景的共性问题:多种相似算法/行为耦合在一起,扩展性差、维护成本高、代码冗余

而策略模式的核心价值,就是"解耦"------将每种算法/行为封装成独立的"策略类",客户端通过统一接口调用不同策略,无需关心策略的具体实现,新增策略时无需修改原有代码,完美符合"开闭原则"。

简单说,策略模式就是"把不同的做法(策略)分开装,想用哪个就用哪个,不用改原来的代码"。比如支付系统,新增京东支付,只需新增一个京东支付策略类,无需修改支付核心逻辑,极大提升开发效率和代码可维护性。

核心结论:策略模式不是"花里胡哨"的设计,而是后端开发的"刚需优化手段"------初级开发者用它优化if-else,中级开发者用它设计可扩展的业务架构,高级开发者用它理解框架底层设计(如Spring的BeanPostProcessor、MyBatis的插件机制),面试时更是高频考点(中高级岗位必问)。

二、策略模式核心概念(极简入门,无需死记硬背)

策略模式的本质很简单:将一组可替换的算法封装成独立的策略类,通过一个统一的上下文类,动态切换不同的策略,客户端只需与上下文交互,无需直接操作具体策略

就像电商支付:支付行为(算法)有支付宝、微信、银联3种,每种支付方式都是一个"策略类";上下文类(支付管理器)负责接收客户端的支付请求,动态选择对应的支付策略,客户端只需告诉上下文"要哪种支付方式",无需关心支付的具体实现。

2.1 核心角色(4个核心,必记,区分角色是掌握策略模式的关键)

  1. 抽象策略角色(Strategy):定义所有具体策略的统一接口,声明策略的核心方法(如支付、排序、校验),是策略模式的"规范",确保所有具体策略都有统一的调用方式;

  2. 具体策略角色(ConcreteStrategy):实现抽象策略接口,封装具体的算法/行为(如支付宝支付逻辑、按时间排序逻辑),是策略模式的"具体实现";

  3. 上下文角色(Context):维护一个抽象策略的引用,提供统一的接口供客户端调用,负责动态切换策略、传递参数,是客户端与具体策略之间的"桥梁";

  4. 客户端(Client):负责创建具体策略对象和上下文对象,通过上下文调用具体策略,无需直接操作具体策略类,降低耦合。

核心原则:抽象策略统一接口,具体策略实现细节,上下文管理策略,客户端调用上下文。这是策略模式的灵魂,也是它与其他行为型模式(如模板方法模式)的关键区别。

核心注意点:策略模式的核心是"算法/行为的封装与切换",解决的是"多种相似算法耦合"的问题,与装饰器模式的"功能增强"、组合模式的"树形结构管理"有本质区别。

2.2 策略模式的核心流程(一句话看懂)

客户端创建具体策略对象 → 上下文对象持有该策略对象 → 客户端通过上下文调用策略的核心方法 → 如需切换策略,只需给上下文重新设置新的策略对象,无需修改其他代码。

三、策略模式入门实现(附可复制代码,新手必练)

以"电商支付系统"为入门案例,模拟3种支付方式(支付宝、微信、银联),用策略模式实现"支付方式动态切换",对比"普通if-else实现"和"策略模式实现"的差异,一看就懂、一练就会。

3.1 普通实现:if-else耦合的反例(痛点凸显)

若不使用策略模式,直接用if-else判断支付类型,代码会随着支付方式的增加而变得臃肿,扩展性极差,后续新增支付方式需修改原有代码。

java 复制代码
// 支付工具类(if-else耦合实现)
public class PaymentUtil {
    // 支付方法,根据支付类型选择不同支付逻辑
    public void pay(String paymentType, double amount) {
        if ("alipay".equals(paymentType)) {
            // 支付宝支付逻辑
            System.out.println("使用支付宝支付,金额:" + amount);
            // 实际开发中,这里会有复杂的签名、请求调用逻辑
        } else if ("wechat".equals(paymentType)) {
            // 微信支付逻辑
            System.out.println("使用微信支付,金额:" + amount);
        } else if ("unionpay".equals(paymentType)) {
            // 银联支付逻辑
            System.out.println("使用银联支付,金额:" + amount);
        } else {
            throw new UnsupportedOperationException("不支持该支付方式");
        }
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        PaymentUtil paymentUtil = new PaymentUtil();
        // 支付宝支付
        paymentUtil.pay("alipay", 100.0);
        // 微信支付
        paymentUtil.pay("wechat", 200.0);
        
        // 痛点:新增京东支付,需修改PaymentUtil的pay方法,新增else if判断
        // 违背开闭原则,代码臃肿,维护成本高
    }
}

【运行结果】:

text 复制代码
使用支付宝支付,金额:100.0
使用微信支付,金额:200.0

【缺点极其明显】:

  • 代码耦合严重:所有支付逻辑都写在一个方法里,if-else过多,代码冗长,可读性差;

  • 扩展性差:新增支付方式(如京东支付),需修改原有PaymentUtil类,违背"开闭原则";

  • 复用性差:支付逻辑无法复用,若其他地方需要单独调用支付宝支付,需重复编写代码;

  • 维护成本高:若某一种支付逻辑需要修改,需在冗长的if-else中找到对应代码,容易出错。

3.2 策略模式实现:解耦的优雅代码(正例)

用策略模式重构支付系统,将每种支付方式封装成独立策略类,通过上下文类统一管理,客户端无需关心具体支付逻辑,新增支付方式只需新增策略类,无需修改原有代码。

java 复制代码
// 1. 抽象策略角色:定义支付策略的统一接口
public interface PaymentStrategy {
    // 核心方法:支付
    void pay(double amount);
}

// 2. 具体策略角色1:支付宝支付
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // 模拟支付宝支付的复杂逻辑(签名、请求第三方接口等)
        System.out.println("支付宝支付 - 签名验证通过");
        System.out.println("使用支付宝支付,金额:" + amount);
        System.out.println("支付宝支付成功\n");
    }
}

// 2. 具体策略角色2:微信支付
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付 - 公众号授权通过");
        System.out.println("使用微信支付,金额:" + amount);
        System.out.println("微信支付成功\n");
    }
}

// 2. 具体策略角色3:银联支付
public class UnionPayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("银联支付 - 银行卡验证通过");
        System.out.println("使用银联支付,金额:" + amount);
        System.out.println("银联支付成功\n");
    }
}

// 3. 上下文角色:管理策略,提供统一调用接口
public class PaymentContext {
    // 持有抽象策略的引用
    private PaymentStrategy paymentStrategy;

    // 构造方法:初始化策略
    public PaymentContext(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    // 动态切换策略
    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    // 统一调用策略的支付方法
    public void pay(double amount) {
        paymentStrategy.pay(amount);
    }
}

// 4. 客户端调用(策略模式实现)
public class Client {
    public static void main(String[] args) {
        // 1. 创建具体策略对象
        PaymentStrategy alipay = new AlipayStrategy();
        PaymentStrategy wechat = new WechatPayStrategy();
        PaymentStrategy unionpay = new UnionPayStrategy();

        // 2. 创建上下文对象,传入初始策略(支付宝)
        PaymentContext context = new PaymentContext(alipay);
        // 调用支付方法
        context.pay(100.0);

        // 3. 动态切换策略(微信支付)
        context.setPaymentStrategy(wechat);
        context.pay(200.0);

        // 4. 动态切换策略(银联支付)
        context.setPaymentStrategy(unionpay);
        context.pay(300.0);

        // 5. 新增支付方式(如京东支付),无需修改原有代码
        PaymentStrategy jdPay = new JdPayStrategy(); // 新增策略类
        context.setPaymentStrategy(jdPay);
        context.pay(400.0);
    }
}

// 新增:京东支付策略(无需修改原有任何代码)
class JdPayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("京东支付 - 账户验证通过");
        System.out.println("使用京东支付,金额:" + amount);
        System.out.println("京东支付成功\n");
    }
}

【运行结果】:

text 复制代码
支付宝支付 - 签名验证通过
使用支付宝支付,金额:100.0
支付宝支付成功

微信支付 - 公众号授权通过
使用微信支付,金额:200.0
微信支付成功

银联支付 - 银行卡验证通过
使用银联支付,金额:300.0
银联支付成功

京东支付 - 账户验证通过
使用京东支付,金额:400.0
京东支付成功

【代码优势极其明显】:

  • 解耦彻底:每种支付逻辑封装成独立策略类,无if-else耦合,代码简洁、可读性强;

  • 扩展性极强:新增支付方式,只需新增策略类实现抽象接口,无需修改原有代码,符合"开闭原则";

  • 复用性高:支付策略可在多个地方复用(如订单支付、退款支付),无需重复编写逻辑;

  • 维护成本低:修改某一种支付逻辑,只需修改对应策略类,不影响其他策略和上下文,不易出错;

  • 动态切换:客户端可通过上下文的set方法,动态切换支付策略,灵活适配不同业务场景。

【核心总结】:策略模式的核心不是"新增功能",而是"解耦与复用"------通过抽象策略统一接口,将具体算法封装成独立策略类,由上下文管理策略的切换和调用,让客户端与具体算法解耦,从而提升代码的可扩展性、可维护性和复用性。

四、策略模式实战(真实业务场景,可直接复用)

结合Java后端最常见的"后台管理系统 - 接口参数校验"场景,用策略模式实现"不同接口的动态校验",贴合真实项目开发(Spring Boot环境),代码可直接复制到项目中使用,解决实际开发中的if-else校验痛点。

4.1 实战场景说明

场景:后台管理系统有3个核心接口(登录接口、注册接口、修改密码接口),每个接口的参数校验规则不同:

  • 登录接口:校验手机号(非空、11位)、密码(非空、6-18位);

  • 注册接口:校验手机号(非空、11位)、验证码(非空、6位)、密码(非空、6-18位)、确认密码(与密码一致);

  • 修改密码接口:校验原密码(非空)、新密码(非空、6-18位)、确认新密码(与新密码一致)。

要求:用策略模式实现校验逻辑,支持动态切换校验策略,新增接口(如忘记密码)时,无需修改原有校验逻辑,只需新增校验策略,贴合Spring Boot实战规范,支持异常统一处理。

  1. 抽象策略:校验策略接口(定义校验方法,返回校验结果);

  2. 具体策略:登录校验策略、注册校验策略、修改密码校验策略;

  3. 上下文:校验上下文(管理校验策略,提供统一校验接口);

  4. 实战亮点:结合Spring依赖注入、自定义注解,实现策略的自动识别和调用,贴合真实项目开发。

4.2 实战代码实现(Spring Boot环境,可直接复用)

java 复制代码
// 1. 公共实体:校验结果封装(统一返回校验状态和提示信息)
@Data
public class ValidationResult {
    // 校验是否通过
    private boolean success;
    // 校验失败提示信息
    private String message;

    // 静态方法:快速创建成功/失败结果
    public static ValidationResult success() {
        ValidationResult result = new ValidationResult();
        result.setSuccess(true);
        return result;
    }

    public static ValidationResult fail(String message) {
        ValidationResult result = new ValidationResult();
        result.setSuccess(false);
        result.setMessage(message);
        return result;
    }
}

// 2. 抽象策略角色:校验策略接口
public interface ValidationStrategy {
    // 核心方法:参数校验(传入请求参数,返回校验结果)
    ValidationResult validate(Map<String, Object> params);
}

// 3. 具体策略角色1:登录接口校验策略
@Component
public class LoginValidationStrategy implements ValidationStrategy {
    @Override
    public ValidationResult validate(Map<String, Object> params) {
        // 1. 校验手机号
        String phone = (String) params.get("phone");
        if (phone == null || phone.isEmpty()) {
            return ValidationResult.fail("手机号不能为空");
        }
        if (!phone.matches("^1[3-9]\\d{9}$")) {
            return ValidationResult.fail("手机号格式不正确(需11位有效号码)");
        }

        // 2. 校验密码
        String password = (String) params.get("password");
        if (password == null || password.isEmpty()) {
            return ValidationResult.fail("密码不能为空");
        }
        if (password.length() < 6 || password.length() > 18) {
            return ValidationResult.fail("密码长度需在6-18位之间");
        }

        // 校验通过
        return ValidationResult.success();
    }
}

// 3. 具体策略角色2:注册接口校验策略
@Component
public class RegisterValidationStrategy implements ValidationStrategy {
    @Override
    public ValidationResult validate(Map<String, Object> params) {
        // 1. 复用手机号校验(可抽取公共方法,此处简化)
        String phone = (String) params.get("phone");
        if (phone == null || phone.isEmpty()) {
            return ValidationResult.fail("手机号不能为空");
        }
        if (!phone.matches("^1[3-9]\\d{9}$")) {
            return ValidationResult.fail("手机号格式不正确(需11位有效号码)");
        }

        // 2. 校验验证码
        String code = (String) params.get("code");
        if (code == null || code.isEmpty()) {
            return ValidationResult.fail("验证码不能为空");
        }
        if (!code.matches("^\\d{6}$")) {
            return ValidationResult.fail("验证码格式不正确(需6位数字)");
        }

        // 3. 校验密码
        String password = (String) params.get("password");
        if (password == null || password.isEmpty()) {
            return ValidationResult.fail("密码不能为空");
        }
        if (password.length() < 6 || password.length() > 18) {
            return ValidationResult.fail("密码长度需在6-18位之间");
        }

        // 4. 校验确认密码
        String confirmPassword = (String) params.get("confirmPassword");
        if (confirmPassword == null || confirmPassword.isEmpty()) {
            return ValidationResult.fail("确认密码不能为空");
        }
        if (!password.equals(confirmPassword)) {
            return ValidationResult.fail("两次密码输入不一致");
        }

        return ValidationResult.success();
    }
}

// 3. 具体策略角色3:修改密码接口校验策略
@Component
public class UpdatePasswordValidationStrategy implements ValidationStrategy {
    @Override
    public ValidationResult validate(Map<String, Object> params) {
        // 1. 校验原密码
        String oldPassword = (String) params.get("oldPassword");
        if (oldPassword == null || oldPassword.isEmpty()) {
            return ValidationResult.fail("原密码不能为空");
        }

        // 2. 校验新密码
        String newPassword = (String) params.get("newPassword");
        if (newPassword == null || newPassword.isEmpty()) {
            return ValidationResult.fail("新密码不能为空");
        }
        if (newPassword.length() < 6 || newPassword.length() > 18) {
            return ValidationResult.fail("新密码长度需在6-18位之间");
        }

        // 3. 校验确认新密码
        String confirmNewPassword = (String) params.get("confirmNewPassword");
        if (confirmNewPassword == null || confirmNewPassword.isEmpty()) {
            return ValidationResult.fail("确认新密码不能为空");
        }
        if (!newPassword.equals(confirmNewPassword)) {
            return ValidationResult.fail("两次新密码输入不一致");
        }

        return ValidationResult.success();
    }
}

// 4. 上下文角色:校验上下文(Spring Bean,管理所有校验策略)
@Component
public class ValidationContext {
    // 注入所有校验策略(key:策略名称,value:策略对象)
    private final Map<String, ValidationStrategy> strategyMap;

    // 构造方法注入(Spring自动将所有ValidationStrategy实现类注入到map中)
    public ValidationContext(Map<String, ValidationStrategy> strategyMap) {
        this.strategyMap = strategyMap;
    }

    // 统一校验方法:根据策略名称选择策略,执行校验
    public ValidationResult validate(String strategyName, Map<String, Object> params) {
        // 获取对应策略
        ValidationStrategy strategy = strategyMap.get(strategyName);
        if (strategy == null) {
            return ValidationResult.fail("不支持该接口的校验策略");
        }
        // 执行校验
        return strategy.validate(params);
    }
}

// 5. 业务服务层:接口服务(调用校验上下文,实现业务逻辑)
@Service
public class UserService {
    private final ValidationContext validationContext;

    // 注入校验上下文
    public UserService(ValidationContext validationContext) {
        this.validationContext = validationContext;
    }

    // 登录接口
    public String login(Map<String, Object> params) {
        // 调用校验上下文,使用登录校验策略
        ValidationResult result = validationContext.validate("loginValidationStrategy", params);
        if (!result.isSuccess()) {
            return result.getMessage();
        }
        // 校验通过,执行登录业务逻辑(此处简化)
        return "登录成功";
    }

    // 注册接口
    public String register(Map<String, Object> params) {
        ValidationResult result = validationContext.validate("registerValidationStrategy", params);
        if (!result.isSuccess()) {
            return result.getMessage();
        }
        // 校验通过,执行注册业务逻辑(此处简化)
        return "注册成功";
    }

    // 修改密码接口
    public String updatePassword(Map<String, Object> params) {
        ValidationResult result = validationContext.validate("updatePasswordValidationStrategy", params);
        if (!result.isSuccess()) {
            return result.getMessage();
        }
        // 校验通过,执行修改密码业务逻辑(此处简化)
        return "密码修改成功";
    }
}

// 6. 控制层:接口对外提供访问(Spring Boot Controller)
@RestController
@RequestMapping("/user")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/login")
    public String login(@RequestBody Map<String, Object> params) {
        return userService.login(params);
    }

    @PostMapping("/register")
    public String register(@RequestBody Map<String, Object> params) {
        return userService.register(params);
    }

    @PostMapping("/updatePassword")
    public String updatePassword(@RequestBody Map<String, Object> params) {
        return userService.updatePassword(params);
    }
}

// 7. 测试类(模拟接口调用,Spring Boot环境可直接注入测试)
public class ValidationTest {
    public static void main(String[] args) {
        // 模拟Spring容器注入(实际项目中用@Autowired注入)
        LoginValidationStrategy loginStrategy = new LoginValidationStrategy();
        RegisterValidationStrategy registerStrategy = new RegisterValidationStrategy();
        UpdatePasswordValidationStrategy updateStrategy = new UpdatePasswordValidationStrategy();

        // 初始化策略map
        Map<String, ValidationStrategy> strategyMap = new HashMap<>();
        strategyMap.put("loginValidationStrategy", loginStrategy);
        strategyMap.put("registerValidationStrategy", registerStrategy);
        strategyMap.put("updatePasswordValidationStrategy", updateStrategy);

        // 初始化上下文和服务
        ValidationContext context = new ValidationContext(strategyMap);
        UserService userService = new UserService(context);

        // 测试登录接口(参数正确)
        Map<String, Object> loginParams = new HashMap<>();
        loginParams.put("phone", "13800138000");
        loginParams.put("password", "123456");
        System.out.println(userService.login(loginParams)); // 输出:登录成功

        // 测试登录接口(参数错误:手机号格式不正确)
        loginParams.put("phone", "123456");
        System.out.println(userService.login(loginParams)); // 输出:手机号格式不正确(需11位有效号码)

        // 测试注册接口(参数正确)
        Map<String, Object> registerParams = new HashMap<>();
        registerParams.put("phone", "13800138000");
        registerParams.put("code", "123456");
        registerParams.put("password", "123456");
        registerParams.put("confirmPassword", "123456");
        System.out.println(userService.register(registerParams)); // 输出:注册成功
    }
}

【运行结果】:

text 复制代码
登录成功
手机号格式不正确(需11位有效号码)
注册成功

【实战亮点】:

  • 贴合Spring Boot实战:使用@Component、@Service、@RestController等注解,符合真实项目开发规范,可直接复制复用;

  • 策略自动注入:通过Spring的Map注入,自动将所有校验策略放入map,无需手动创建和管理策略对象,简化开发;

  • 扩展性极强:新增接口(如忘记密码),只需新增校验策略类实现ValidationStrategy接口,无需修改原有上下文和服务代码;

  • 代码可维护性高:每种接口的校验逻辑独立,修改某一种校验规则,只需修改对应策略类,不影响其他接口;

  • 异常统一处理:通过ValidationResult封装校验结果,统一返回提示信息,贴合接口开发的实际需求。

补充:真实项目中,可结合自定义注解(如@ValidationStrategy(&#34;login&#34;)),简化策略的调用,无需手动传入策略名称,进一步提升开发效率。

五、策略模式在JDK/框架中的应用(面试必提)

策略模式的核心价值是"算法的封装与切换",这也是它被广泛应用在JDK源码和主流Java框架中的原因,掌握这些应用场景,面试时能加分不少,还能帮助你理解框架底层设计思想。

5.1 JDK 中的策略模式(最常见,面试高频)

JDK中有多个经典的策略模式应用,其中Comparator、ThreadPoolExecutor是面试必问考点,一定要掌握:

5.1.1 Comparator 接口(最典型)

Java中的Comparator接口(比较器),是策略模式的标准实现,用于定义不同的排序策略,客户端可动态切换排序方式:

  • 抽象策略(Strategy):Comparator接口(定义了compare(T o1, T o2)方法,是所有排序策略的统一接口);

  • 具体策略(ConcreteStrategy):Comparator的各种实现类(如按自然顺序排序、按字符串长度排序、自定义排序);

  • 上下文(Context):Collections.sort()方法(接收Comparator对象,调用其compare方法完成排序,动态切换排序策略)。

java 复制代码
public class ComparatorDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("java", "strategy", "pattern", "csdn");

        // 具体策略1:按字符串自然顺序排序(默认策略)
        Collections.sort(list, Comparator.naturalOrder());
        System.out.println("自然顺序排序:" + list);

        // 具体策略2:按字符串长度排序(切换策略)
        Collections.sort(list, Comparator.comparingInt(String::length));
        System.out.println("按长度排序:" + list);

        // 具体策略3:按字符串逆序排序(自定义策略)
        Collections.sort(list, (s1, s2) -> s2.compareTo(s1));
        System.out.println("逆序排序:" + list);
    }
}

核心逻辑:Comparator接口作为抽象策略,统一了所有排序策略的接口;Collections.sort()作为上下文,接收具体的排序策略,动态切换排序方式,客户端无需关心排序的具体实现,只需传入对应的策略即可。

5.1.2 ThreadPoolExecutor 的拒绝策略

Java线程池ThreadPoolExecutor中,拒绝策略(RejectedExecutionHandler)是策略模式的典型应用,用于处理线程池满时的任务拒绝逻辑:

  • 抽象策略(Strategy):RejectedExecutionHandler接口(定义了rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法);

  • 具体策略(ConcreteStrategy):4种内置拒绝策略(AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy);

  • 上下文(Context):ThreadPoolExecutor(持有拒绝策略引用,线程池满时调用拒绝策略的方法)。

核心逻辑:客户端可在创建线程池时,指定不同的拒绝策略,动态切换线程池满时的处理方式,无需修改线程池的核心逻辑,符合策略模式的设计思想。

5.2 框架中的策略模式

5.2.1 Spring MVC 中的 HandlerAdapter

Spring MVC的HandlerAdapter(处理器适配器),用于适配不同类型的处理器(如Controller、HttpRequestHandler),底层采用策略模式实现:

  • 抽象策略(Strategy):HandlerAdapter接口(定义了supports(Object handler)、handle(HttpServletRequest request, HttpServletResponse response, Object handler)方法);

  • 具体策略(ConcreteStrategy):RequestMappingHandlerAdapter、HttpRequestHandlerAdapter等(适配不同类型的处理器);

  • 上下文(Context):DispatcherServlet(遍历所有HandlerAdapter,找到适配当前处理器的策略,调用其handle方法)。

核心逻辑:通过策略模式,Spring MVC可适配不同类型的处理器,新增处理器类型时,只需新增对应的HandlerAdapter实现类,无需修改DispatcherServlet的核心逻辑,扩展性极强。

5.2.2 MyBatis 中的 Executor

MyBatis的Executor(执行器),用于执行SQL语句,底层采用策略模式实现不同的执行策略:

  • 抽象策略(Strategy):Executor接口(定义了query、update等方法,统一执行SQL的接口);

  • 具体策略(ConcreteStrategy):SimpleExecutor(简单执行器)、ReuseExecutor(复用预处理语句)、BatchExecutor(批量执行器);

  • 上下文(Context):SqlSession(持有Executor引用,动态切换不同的执行策略,执行SQL语句)。

核心逻辑:客户端可通过SqlSession指定不同的Executor策略,动态切换SQL的执行方式(如批量执行、复用语句),无需修改SQL的执行逻辑,提升灵活性。

六、策略模式面试高频考点(必背,避坑)

策略模式是Java后端面试的高频考点(中高级岗位尤为突出),重点考察"核心思想""核心角色""JDK应用""与其他模式的区别",记住以下考点,轻松应对面试。

1. 策略模式的核心作用是什么?(高频)

核心答案(一句话记住,面试直接说):将一组可替换的算法/行为封装成独立的策略类,通过上下文管理策略的动态切换,让客户端与具体算法解耦,提升代码的可扩展性、可维护性和复用性,符合开闭原则

补充:策略模式解决的是"多种相似算法耦合"的问题,核心是"算法的封装与切换",而非"功能增强"或"结构管理"。

2. 策略模式的核心角色有哪些?(必背)

核心答案(一句话记住):抽象策略(统一接口)、具体策略(实现算法)、上下文(管理策略)、客户端(调用上下文)

角色名称 核心职责 示例
抽象策略 定义所有具体策略的统一接口,声明核心方法,规范策略的实现 PaymentStrategy、Comparator
具体策略 实现抽象策略接口,封装具体的算法/行为,是策略的具体实现 AlipayStrategy、自然排序Comparator
上下文 维护抽象策略引用,提供统一调用接口,负责动态切换策略 PaymentContext、Collections.sort()
客户端 创建具体策略和上下文,通过上下文调用策略,无需直接操作具体策略 Client类、业务服务层

3. 策略模式和模板方法模式的区别?(高频)

很多面试官会把这两个行为型模式放在一起问,核心区别(一句话区分):策略模式是"算法的动态切换",所有策略是平等的,可完全替换;模板方法模式是"固定流程+可变步骤",步骤不可替换,只能修改步骤的实现

对比维度 策略模式 模板方法模式
核心目的 动态切换不同的算法/行为,算法可完全替换 定义固定流程,只允许修改流程中的个别步骤实现
关系 策略之间是平等的,无依赖关系,可自由切换 父类定义流程,子类继承父类,修改步骤实现,有继承关系
灵活性 灵活性高,可动态切换策略,新增策略无需修改原有代码 灵活性低,流程固定,只能修改步骤实现,无法改变流程
使用场景 多种相似算法(如支付方式、排序方式),需动态切换 固定流程(如流程审批、数据导入),个别步骤可定制

4. 策略模式的优缺点是什么?(必背)

核心答案(简洁好记,面试直接说):

  • 优点:解耦(客户端与具体算法分离)、扩展性强(新增策略无需修改原有代码)、复用性高(策略可重复使用)、代码简洁(消除if-else耦合);

  • 缺点:策略类数量会增多(每种算法对应一个策略类)、客户端需了解所有策略(才能选择合适的策略)。

5. 如何解决策略模式"策略类过多"的问题?(进阶考点)

核心答案(面试加分):可结合"工厂模式"或"注解+反射"优化,由工厂类统一管理策略的创建和获取,客户端无需直接创建策略对象,只需传入策略标识,由工厂返回对应策略,减少客户端对策略的依赖。

示例:在实战案例中,可新增策略工厂类,根据接口标识(如"login""register"),自动创建并返回对应的校验策略,进一步简化客户端代码。

七、总结(新手必看)

策略模式的核心很简单:将不同的算法/行为分开装,用一个"中间人"(上下文)管理,客户端只需找中间人,无需关心具体算法,新增算法只需新增"包装类",不用改原来的代码

对于新手来说,掌握策略模式的关键是"区分核心角色"和"理解解耦思想"------先学会用策略模式优化简单的if-else场景(如支付、排序),再结合Spring Boot实战案例,理解策略的注入和动态切换;对于中高级开发者,重点掌握策略模式在框架中的应用,以及与其他模式的区别,面试时才能从容应对。

最后记住:策略模式不是"万能的",它只适用于"多种相似算法/行为需要动态切换"的场景,若没有动态切换的需求,无需强行使用(否则会增加代码复杂度)。合理使用策略模式,能让你的代码更简洁、更易维护、更具扩展性,这也是后端开发者从"会写代码"到"会写好代码"的关键一步。

相关推荐
历程里程碑2 小时前
Linux 50 IP协议深度解析:从报头结构到子网划分与NAT
java·linux·开发语言·网络·c++·python·智能路由器
绿豆人2 小时前
go语言的Reflect包
java·开发语言·数据结构
liuyao_xianhui2 小时前
map和set_C++
java·开发语言·数据结构·c++·算法·宽度优先
清心歌2 小时前
ArrayList 深入解析
java
算.子2 小时前
【Spring AI 实战】五、RAG 核心原理:为什么需要检索增强生成?
java·人工智能·spring
XS0301062 小时前
Java基础笔记(一)
java·笔记·python
程序员老邢2 小时前
【产品底稿 05】商助慧 V1.1 里程碑:RAG 文章仿写模块全链路实现
java·spring boot·程序人生·ai·milvus
消失的旧时光-19433 小时前
Spring Boot 实战(三):Service 分层 + 统一返回 + 异常处理(工程级写法)
java·spring boot·接口·解耦