策略模式在AIBOT项目中的实际应用

发布时间:2024年01月21日

原文链接icon-default.png?t=N7T8https://www.jylt.cc/#/detail?activityIndex=2&id=8d1912358fa1c1d8db1b44e2d1042b70AIBOT 你想 我来做AIBOTicon-default.png?t=N7T8https://chat.jylt.top/

定义

策略模式(Strategy Pattern:Define a family of algorithms,encapsulate each one,and make them interchangeable.)中文解释为:定义一组算法,然后将这些算法封装起来,以便它们之间可以互换,属于一种对象行为型模式。总的来说策略模式是一种比较简单的模式,听起来可能有点费劲,其实就是定义一组通用算法的上层接口,各个算法实现类实现该算法接口,封装模块使用类似于 Context 的概念,Context 暴漏一组接口,Context 内部接口委托到抽象算法层。

是不是每个字都能看懂,拼到一块不知所以然了。最初看到这句话时也不知道啥意思,下面有一个类图供大家看下:

组成角色很简单,对于新手来说,实现起来可能会毫无思路。其实主要使用的就是多态的特性。

实现

了解了基本概念之后,可能对策略模式仅仅是一个了解的状态,动手实操可能还没有什么思路。下面以我的AI项目AIBOT中图片生成功能来说一下策略模式是如何落地的。

先看一下具体的功能点:

https://oss.jylt.cc/img/content/32ef85ebfc3b3eb8ef83903ee3757cc0/609cf46a-27ff-4447-aa08-4a19d25e52c4.png

目前AI作图只有两个功能,文生图和人像重绘,如何实现这两个功能呢?

普通实现方式

最简单高效的方法是平铺直叙的写代码,通过 if-else 来区分具体是哪个功能,例如:

    @PostMapping("/genImg")
    public ResponseKit<Object> genImg(int type) {
        if (type == 1) {
            // 文生图逻辑
        } else if (type == 2) {
            // 人像重绘逻辑
        } else if (type == ...) {
            // 其他图像生成逻辑
        }
    }
优点
  • 代码实现方便,不需要做过多设计
  • 代码条理比较清晰,自上而下很容易找出来哪块逻辑实现的是什么功能
缺点
  • 会增加类的复杂性,这些逻辑虽都是图片生成,但具体实现逻辑不是同一类,都放到同一个类中,会使这个类变得大而全,与?职责单一?的设计规范相冲突
  • 如果日后需要增加其他图像生成逻辑,需要频繁的修改这个类,而这个类中又有太多其他生成规则的逻辑,修改、新增逻辑可能会对其他已有逻辑造成意料之外的影响,为了避免这种影响可能引入的错误,我们就要对受影响的逻辑都要进行回归测试。这样无形间增加了测试的工作量,与?开闭原则?设计规范相冲突
策略模式实现

依据上面的类图,我们先一步步通过策略模式来实现功能,然后来看一下策略模式有什么优缺点

首先我们需要先定义一个通用接口:

public interface GenImgStrategy {
    ResponseKit<Object> genImg();
}

接口很简单,就一个抽象方法:生成图片;然后我们定义一下 文生图 和 人像重绘 的具体实现逻辑。这两个逻辑都统一实现?GenImgStrategy?接口:

public class TextToImgStrategyImpl implemets GenImgStrategy {
    @Override
    public ResponseKit<Object> gen() {
  	// 文生图具体实现逻辑
    }
}

public class GenImgStyleStrategyImpl implemets GenImgStrategy {
    @Override
    public ResponseKit<Object> gen() {
  	// 人像重绘具体实现逻辑
    }
}

定义好了接口和实现类之后,我们如何通过 API 接口中的 type 参数来确定需要执行 文生图 的逻辑还是执行 人像重绘 的逻辑呢?这时候我们需要一个辅助类:

public class GenImgStrategyContext {

    private static final Map<Integer, GenImgStrategy> map = new HashMap<>();

    static {
	// 初始化
        map.put(1, new TextToImgStrategyImpl());
        map.put(2, new GenImgStyleStrategyImpl());
  	map.put(..., ...);
    }

    // type=1:执行文生图逻辑;type=2:执行人像重绘逻辑
    public ResponseKit<Object> doGen(Integer type) {
        GenImgStrategy genImgStrategy = map.get(type);
        if (genImgStrategy == null) {
            log.error("类型异常,type={}", type);
            return ResponseKit.error("参数异常");
        }
        return genImgStrategy.gen();
    }
}

这里使用多态的特性,在执行?doGen?方法的时候,通过 type 值来获取不同策略的具体实现类,最后执行?gen?方法的时候,其实执行的是具体实现类中的方法

API 接口只需要改为以下形式,就可以轻松调用不同的图像生成逻辑了:

    @PostMapping("/genImg")
    public ResponseKit<Object> genImg(int type) {
       GenImgStrategyContext context = new GenImgStrategyContext();
       return context.doGen(type);
    }

这样改造之后,我们就通过 策略模式 来实现了图片生成的两种实现逻辑,后续如果有新的图片生成逻辑,只需要再增加一个实现类,在 context 类的 map 中再增加一种类型即可,对现有的两种实现逻辑实现了零侵入,做功能验证的时候只需要验证新增的逻辑即可,无需担心对现有功能的其他影响。

综合以上,我们看一下策略模式的优缺点:

优点
  • 极大的提高了程序的扩展性
  • 使程序更加的 高内聚,低耦合
  • 提高了后续功能扩展的效率
缺点
  • 增加了程序的复杂性
  • 如果策略较多,会有很多的策略实现类

看到这里之后,有些小伙伴可能还是懵懵懂懂的状态,毕竟纸上得来终觉浅,需要动手实操,最好是在自己的项目中实践一下,立即会有柳暗花明又一村的感觉。

这些都是我的真实感受,我在2020年就去有意识的学习了各种设计模式,但一直没有使用场景,也就只停留在了解的层面,知道有这么个概念,直到两个月前,在团队项目中实际看到了同事使用策略模式来解决实际问题的代码,瞬间豁然开朗,原来是这么用的。然后就在图像生成的功能上使用了,好处确实很明显。

除此之外,AIBOT?中在支付功能也是用了策略模式,来实现 会员、图像、单次充值的不同场景。

总结

策略模式虽然好处多多,但技术没有银弹,合适的才是最好的。对于经常变更策略的场景,策略模式确实能够更好的选择,但是如果不加选择的使用策略模式,会不自觉陷入过度设计的陷阱

文章来源:https://blog.csdn.net/wuchenxiwalter/article/details/135727815
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。