设计模式:Python策略模式实现,真实项目背景

嘉美伯爵

算法 设计模式 286

今天给大家讲解一下设计模式中的策略模式,下面的图片是真实项目中的前端图片,目前接入了三个算法能力,根据产品经理要求,后期还会有不同的算法加入进来,这个时候我们需要考虑一个好的结构对代码进行优化,可能有一些小伙伴会说直接使用if...elif..else分支语句不就可以了,但是从系统的健壮性和可维护性方面我们在这里不能大量的使用if分支结构,因为每一种算法能力的代码量极其大而且算法参数有的多达十几个,加之后期会有其他算法加入,很容易顾此失彼,牵一发而动全身。接下来我们来讲解一下策略模式的使用。

image-20210112111313351

理论

我们以高德地图来讲解,当我们搜索一个位置的时候,高德地图会给我们展示出不同的出行方式,我们这里只是对模型进行了简单的抽象,当然高德地图肯定用到了其他较为复杂的代码模式。就本页面而言,你会选择使用混乱的if多分支语句吗,显然是不合理的,当我们每选择一个路线的时候,后台就会if分支判断一遍,最差的情况是从头判断到尾。

image-20210113111608980

  • if分支最终代码结构

    从商业的角度来说,使用if多分支语句是完全可以正常运行的: 但当后续不断有路线规划算法加入的时候,代码量就会增加一倍,久而久之,你会觉得己没法继续维护这堆代码了。

    无论是修复简单缺陷还是微调算法参数, 对某个算法进行任何修改都会影响整个类, 从而增加在已有正常运行代码中引入错误的风险。

    此外, 团队合作将变得低效。 如果你在应用成功发布后招募了团队成员, 他们会抱怨在合并冲突的工作上花费了太多时间。 在实现新功能的过程中, 你的团队需要修改同一个巨大的类, 这样他们所编写的代码相互之间就可能会出现冲突。

img

解决方案

在讲解了多分支语句可能带来的风险和弊端后,我们引入策略模式。看下图,我们将不同的算法封装到独立的类中,也就是抽象出来一个接口,新加入的算法类必须实现这个接口的策略方法。

img

  • 上下文类

    我们来讲解一下上下文类的作用:__init__方法接受具体的算法类,execute_algorithm方法最终被客户端调用,这样上下文其实不知道具体的算法策略。它会通过同样的通用接口与所有策略进行交互, 而该接口只需暴露一个方法来触发所选策略中封装的算法即可。指向具体策略的引用, 且仅通过策略接口与该对象进行交流。

image-20210113121747411

  • 算法基类接口

    封装一个所有算法都必须实现的抽象方法,以备上下文进行正确的调用。所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。

image-20210113122103609

  • 不同的算法

    每个算法的逻辑在各自的类内修改就可以了,代码维护和修改复杂度大大降低。实现了上下文所用算法的各种不同变体。

image-20210113122149014

  • 客户端代码

    客户端只需要将即将执行的算法类传入上下文中即可,然后上下文便会动态的执行具体的算法对象。

image-20210113122215698

优缺点

优点

  • 代码解耦,便于维护
  • 可以运行时动态切换算法
  • 开闭原则。无须对上下文代码进行修改,就可以添加新的代码

缺点

  • 如果算法逻辑,较为固定,不经常修改,使用策略模式只会增加代码量

总结

下面我们来总结一下,使用策略模式的步骤

structure

  • 将频繁修改的算法进行抽取,独立为具体的算法类
  • 创建抽象基类,实现一个约定的抽象策略方法
  • 所有独立的算法类,必须实现基类中的抽象策略接口
  • 建立上下类,该类可以动态的对算法进行setter,创建调用具体算法的方法,上下文仅可通过该方法与具体的策略交互
  • 客户端进行调用,传入具体的算法类,上下文动态执行具体的算法任务