【Java】还不懂策略模式?一文带你玩明白策略模式

简单说两句

✨ 正在努力的小新~
💖 超级爱分享,分享各种有趣干货!
👩‍💻 提供:模拟面试 | 简历诊断 | 独家简历模板
🌈 感谢关注,关注了你就是我的超级粉丝啦!
🔒 以下内容仅对你可见~

作者:不正经小新CSDN后端领域新星创作者 |阿里云专家博主

CSDN个人主页不正经小新

🔎GZH不正经小新

🎉欢迎关注🔎点赞👍收藏⭐️留言📝



在这里插入图片描述

👻1 看了脑瓜子疼的概念

老板们,我们先来看下在维基百科上面说的概念:
策略模式作为一种软件设计模式,指对象有某个行为 ,但是在不同的场景中,该行为有不同的实现算法。你是一个演员,每天要根据不同的剧本扮演不同的角色。这就是策略模式,你的表演技巧就是那些算法,而每个角色就是不同的场景。

策略模式

  • 定义了一族算法(业务规则);
  • 封装了每个算法;
  • 这族的算法可互换代替(interchangeable)

💬【Tips】 这段话的来源是:维基百科
💬【Tips】 因为这个面向业务开发,所以需要具备Java基础和Spring框架的基础额~
💬【Tips】 本文的具体业务逻辑并没有实现,是通过打印日志假装实现的额(重点在于理解策略模式),老板们阔以自行发挥~

🥰2 可爱的产品经理提需求了

产品经理: 这个小可爱,来对接下需求~

小可爱: 我手上的需求都没做完呢,你又来什么需求?

产品经理: 很简单,就是我们的用户忘记密码了,要找回密码,可以让用户选择不同的方式,比如:手机验证码验证、密保问题验证,就先做这两个吧~

小可爱: 不行,得加钱

产品经理: 哎呀,少废话,赶紧做,下午给你买杯咖啡

🤪3 憨憨的你框框堆💩代码(未用策略模式)

想要快点喝到美味咖啡的你,打开你的IDE后,经过几秒的思考后,说,这还不简单~
在这里插入图片描述

我们定义一个接口,通过一个枚举参数来判断是发送短信还是APP弹窗噻,写两个If判断不就OK了么~
在这里插入图片描述

随后你便框框的敲上了代码~
先说说这个堆山版本的总体思路

  • 定义枚举类VerifyEnums和请求参数
  • controller层代码(请求入口)
  • service层代码(模拟实现)

VerifyEnums枚举类 代码清单

public enum VerifyEnums {
    /**
     * 手机验证码
     */
    PHONE("phone", "手机验证码"),
    /**
     * 密保问题验证
     */
    CONFIDENTIAL("confidential", "密保问题验证")
    ;
    
    private final String type;
    
    private final String desc;

    VerifyEnums(String type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}

VerifyParam 代码清单

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class VerifyParam {
    /**
     * 用户名
     */
    private String userName;

    /**
     * 用户ID
     */
    private String userId;

    /**
     * 手机号
     */
    private String phoneNumber;

    /**
     * 密保问题和答案 - 这里简单处理
     */
    private JSONObject questionAndAnswer;

    /**
     * 验证类型
     */
    private String verifyType;
}

PhoneService手机验证码服务 代码清单

@Service
@Slf4j
public class PhoneService {

    public String verify(VerifyParam param) {
        //校验
        Assert.isTrue(StringUtils.isNotEmpty(param.getUserId()), "用户ID不能为空");
        Assert.isTrue(StringUtils.isNotEmpty(param.getPhoneNumber()), "手机号不能为空");
        // 业务逻辑
        log.info("进行手机号验证码验证逻辑~");
        // 这里简单处理,直接返回
        return "手机验证码验证成功";
    }
}

ConfidentialService密保问题服务 代码清单

@Service
@Slf4j
public class ConfidentialService {

    public String verify(VerifyParam param) {
        //校验
        Assert.isTrue(StringUtils.isNotEmpty(param.getUserId()), "用户ID不能为空");
        Assert.isTrue(MapUtils.isNotEmpty(param.getQuestionAndAnswer()), "手机号不能为空");
        // 业务逻辑
        log.info("进行密保问题验证逻辑~");
        // 这里简单处理,直接返回
        return "密保问题验证成功";
    }
}

VerifyService验证Service 代码清单

@Service
@Slf4j
@RequiredArgsConstructor
public class VerifyService {
    private final PhoneService phoneService;
    private final ConfidentialService confidentialService;

    public String verify(VerifyParam param) {
        String verifyType = param.getVerifyType();
        if (StringUtils.equals(VerifyEnums.PHONE.getType(), verifyType)) {
            return phoneService.verify(param);
        }
        if (StringUtils.equals(VerifyEnums.CONFIDENTIAL.getType(), verifyType)) {
            return confidentialService.verify(param);
        }
        return "验证失败,请重试~";
    }
}

VerifyController验证控制类 代码清单

@RestController
@RequiredArgsConstructor
@RequestMapping("v1/reset")
public class VerifyController {
    private final VerifyService verifyService;

    @PostMapping("verify")
    public String verify(@RequestBody VerifyParam param) {
        return verifyService.verify(param);
    }
}

OK OK 齐活了
运行SpringBoot项目
我们到apifox这个软件中运行(apifox是API 文档、API 调试、API Mock、API 自动化测试一体化协作平台,推荐大家可以使用下,使用起来感觉还不错~)
在这里插入图片描述
后台的日志
在这里插入图片描述

可以看到验证成功,我们更换verifyType也可以得到对应的结果,友友们可以自己尝试下~

哐哐哐,写完了,咖啡也到了,喝了一小口咖啡,觉得生活美滋滋啊~
在这里插入图片描述

产品经理: 可就当你咖啡喝了一小口,产品经理过来了~

产品经理: 你再加一个功能,让用户还可以通过邮箱验证的方式进行密码找回~

小可爱: 啊,你刚刚为什么不说啊,都写完了

产品经理对你进行了一顿猛夸~,你听着这夸赞心里乐开花了,便接下了这活

你在枚举中添加了一个邮箱验证的枚举
然后你又写了一个MailService
VerifyService中又加了个if语句
写完测试通过后,感觉美滋滋
但是看着这不断增加的if语句,心里很不是个滋味💔

在这里插入图片描述

🤓4 聪明的你决定设计一个优雅的代码

写完代码的你,刷到一条励志鸡汤,然后你像是下定了某种决心,决定开始重构这个代码~
你在网上查询大量资料后,发现有个设计模式可以很好的解决你这个问题~
这个设计模式就是:策略模式
于是乎,你便开始使用策略模式进行重构你的Java代码
重构思路

  • 定义一个策略接口
  • 具体实现类去实现策略接口
  • 定义一个工厂类,这个工厂能够通过实现类在Spring的Ioc容器中的名称来获取实例
  • VerifyService中调用工厂类的方法获取具体的策略实现类,然后调用验证方法

VerifyStrategy策略接口 代码清单

public interface VerifyStrategy<T> {
    String verify(T param);
}

PhoneServiceYes具体策略实现类 代码清单

@Service(value = "phoneStrategy")
@Slf4j
public class PhoneServiceYes implements VerifyStrategy<VerifyParam> {

    @Override
    public String verify(VerifyParam param) {
        //校验
        Assert.isTrue(StringUtils.isNotEmpty(param.getUserId()), "用户ID不能为空");
        Assert.isTrue(StringUtils.isNotEmpty(param.getPhoneNumber()), "手机号不能为空");
        // 业务逻辑
        log.info("进行手机号验证码验证逻辑~");
        // 这里简单处理,直接返回
        return "手机验证码验证成功";
    }

}

ConfidentialServiceYes 具体策略实现类 代码清单

@Service("confidentialStrategy")
@Slf4j
public class ConfidentialServiceYes implements VerifyStrategy<VerifyParam> {

    @Override
    public String verify(VerifyParam param) {
        //校验
        Assert.isTrue(StringUtils.isNotEmpty(param.getUserId()), "用户ID不能为空");
        Assert.isTrue(MapUtils.isNotEmpty(param.getQuestionAndAnswer()), "手机号不能为空");
        // 业务逻辑
        log.info("进行密保问题验证逻辑~");
        // 这里简单处理,直接返回
        return "密保问题验证成功";
    }
}

【Tips】可能细心的老板发现了一个东西,就是我们的Service注解中多了一个东西,这里解释下~
在这里插入图片描述


其实稍微有点Spring框架的基础的老板是能看懂的,这里涅,顺带给各位老板复习下,哈哈哈~


@Service(“confidentialStrategy”) 它用于将类标记为Spring的服务Bean,并为其指定一个名称。它告诉Spring容器这个类是一个服务,应该被实例化并管理为一个Bean。

在我们的代码中,@Service(“confidentialStrategy”) 注解将 ConfidentialServiceYes 类标记为一个名为 “confidentialStrategy” 的服务Bean。这样,在其他地方需要使用这个服务时,可以通过这个名称来获取它的实例。
例如,可以在其他类中使用 @Resource 注解来自动注入这个Bean

StrategyFactory 工厂 代码清单

@Component
@SuppressWarnings("unchecked")
public class StrategyFactory {
    @Resource
    private ApplicationContext applicationContext;

    /***
     * 获取策略
     * @param type 类型
     * @return 策略
     */
    public <T> VerifyStrategy<T> getStrategy(String type) {
        return applicationContext.getBean(type + "Strategy", VerifyStrategy.class);
    }
}

这里给大家解释下这个工厂类~

这个类是一个策略工厂,用于根据不同的类型获取相应的策略实例。以下是对这个类的详细解释:

  • @Component 注解: 这是一个Spring框架中的注解,用于将类标记为Spring的组件,使其能够被Spring容器识别和管理。
  • @SuppressWarnings("unchecked") 注解: 这个注解用于抑制编译器警告,通常用于告诉编译器忽略特定类型的警告。在这个例子中,它可能是用来忽略与泛型相关的警告。
  • @Resource 注解: 这是一个Java EE和Spring框架中的注解,用于将Spring容器中的Bean注入到当前类的字段中。在这个例子中,applicationContext 字段被标记为 @Resource,这意味着Spring容器将自动注入一个 ApplicationContext 实例到这个字段中。
  • getStrategy 方法: 这是一个泛型方法,用于根据传入的类型参数获取相应的策略实例。方法的参数是一个字符串 type,表示策略的类型。方法的返回值是一个 VerifyStrategy 类型的实例,其中 T 是一个泛型参数,表示策略处理的数据类型。
  • applicationContext.getBean(type + "Strategy", VerifyStrategy.class): 这行代码使用Spring的 ApplicationContext 来获取一个Bean实例。type + "Strategy" 是要获取的Bean的名称,VerifyStrategy.class 是要获取的Bean的类型。Spring将根据这些信息从容器中查找并返回相应的Bean实例。

这个策略工厂类的主要作用是提供一个统一的接口,用于根据不同的类型获取相应的策略实例,从而实现策略模式。

VerifyService类中添加一个方法

public String verifyYes(VerifyParam param) {
        String verifyType = param.getVerifyType();
        VerifyStrategy<VerifyParam> strategy = strategyFactory.getStrategy(verifyType);
        String verify = strategy.verify(param);
        if (StringUtils.isNotEmpty(verify)) {
            return verify;
        }
        return "验证失败,请重试~";
    }

VerifyController中对应添加一个方法


    @PostMapping("verifyYes")
    public String verifyYes(@RequestBody VerifyParam param) {
        return verifyService.verifyYes(param);
    }

好了,齐活,运行看结果
在这里插入图片描述

如果后面产品经理在加其他需求的话,

  • 我们只需要在VerifyEnums枚举中添加一种类型
  • 然后在写一个具体的实现类

对比下下~
哪个优雅哪个帅气各位老板们肯定有数吧,哈哈哈🤓🤓🤓
在这里插入图片描述
类图如下
在这里插入图片描述

☘️5 谈谈优点和缺点

优点:

  • 可替换性:策略模式使得算法可以相互替换,而不需要修改使用算法的代码。

  • 可扩展性:新增策略时,只需添加新的策略类,无需修改现有代码,符合开闭原则。

  • 解耦:策略模式将算法的具体实现与使用算法的上下文分离,降低了系统的耦合度。

  • 复用性:由于算法被封装在独立的策略类中,可以在不同的上下文中复用这些策略。

  • 减少条件判断:策略模式可以减少代码中的if-else或switch-case等条件判断,使代码更加清晰。

  • 灵活性:可以根据环境或条件的变化动态选择不同的策略。

缺点:

  • 客户端必须了解可用策略:客户端代码需要了解各种策略类的存在和它们之间的差异,以便能够正确地使用它们。

  • 可能增加系统的复杂性:如果存在很多策略,系统可能会变得复杂,尤其是当这些策略之间的差异不大时。

  • 可能增加对象数量:每个策略都需要一个或多个对象,这可能会增加系统的内存消耗。

  • 策略选择的复杂性:在有多种策略可供选择的情况下,选择合适的策略可能会变得复杂,尤其是在策略之间存在细微差别时。

  • 性能考虑:在某些情况下,策略模式可能会引入额外的性能开销,尤其是在策略对象的创建和销毁频繁时。

【Tips】设计模式虽好,可不要贪杯额!

【都看到这了,点点赞点点关注呗,爱你们】😚😚
在这里插入图片描述

💬

✨ 正在努力的小新~
💖 超级爱分享,分享各种有趣干货!
👩‍💻 提供:模拟面试 | 简历诊断 | 独家简历模板
🌈 感谢关注,关注了你就是我的超级粉丝啦!
🔒 以下内容仅对你可见~

作者:不正经小新CSDN后端领域新星创作者 |阿里云专家博主

CSDN个人主页不正经小新

🔎GZH不正经小新

🎉欢迎关注🔎点赞👍收藏⭐️留言📝

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不正经小新

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值