设计模式(Swift) - 策略模式

定义

Strategy 策略模式

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。

动机

  • 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担。

  • 如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?

案例

有这样一个案例,有一个销售单,需要计算汇率,但是不同国家的税法是不一样的,所以税费也不一样。所以我们根据需求可以这样做:

方案一

swift 复制代码
enum RegionType {
    case CN, US, DE, FR
}

class SalesOrder {
    var taxType: RegionType
    
    init(taxType: RegionType) {
        self.taxType = taxType
    }
    
    func calculateTax() {
        if (taxType == .CN) {
            // CN***********
        } else if (taxType == .US) {
            // US***********
        } else if (taxType == .DE) {
            // DE***********
        } else if (taxType == .FR) {
            // FR***********
        }
    }
}

这种写法有什么不好呢?如果RegionType增加一种,那么需要修改calculateTax方法里的判断语句,这里因为代码简单看着没有什么,但是如果代码复杂的话,那么基本上需要在所有用到RegionType枚举值的地方都需要修改代码。

还有一个缺点,一般发行一个APP在哪个国家是确定的,比如说这个APP是在中国发行的,那为什么要把其他国家的税法编译进来,不就是增大包体积了么?

所以我们看改进后的代码

方案二

swift 复制代码
class Context {
    // 表示一些上下文
}

protocol TaxStrategy {
    func calculate(context: Context) -> Double
}

class CNTax: TaxStrategy {
    func calculate(context: Context) -> Double {
        // calculate CN tax
        return 0
    }
}

class USTax: TaxStrategy {
    func calculate(context: Context) -> Double {
        // calculate US tax
        return 0
    }
}

class DETax: TaxStrategy {
    func calculate(context: Context) -> Double {
        // calculate DE tax
        return 0
    }
}

class SalesOrder {
    private var taxStrategy: TaxStrategy
    
    init(taxStrategy: TaxStrategy) {
        self.taxStrategy = taxStrategy
    }
    
    func calculateTax() {
        func calculate() -> Double {
            let context = Context()
            return taxStrategy.calculate(context: context)
        }
    }
}

在方案二中,如果我们想新增国家法国,那么我们只要新增一个FRTax类就行了:

swift 复制代码
class FRTax: TaxStrategy {
    func calculate(context: Context) -> Double {
        // calculate FR tax
        return 0
    }
}

这个设计模式经典的遵守了开闭原则,只对扩展开发,对修改封闭。

还有上面说的包体积的问题,我们可以通过宏定义或者指定文件编译(不同类应该是要写在不同的文件里的),就可以在不同国家的APP里编译不同的代码逻辑了。

要点总结

  • Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。

  • Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式(并不绝对)。

  • 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。

相关推荐
FeelTouch Labs2 小时前
Clawdbot (OpenClaw): 架构与实现解析
架构
掘根3 小时前
【即时通讯系统】项目框架与微服务拆分设计
微服务·云原生·架构
灵感菇_3 小时前
详细解析 MVC/MVP/MVVM/MVI 架构
架构·mvc·mvvm·mvp·mvi
code_li3 小时前
Android 16KB页面大小适配
java·架构·android-studio
听麟3 小时前
HarmonyOS 6.0+ PC端多人联机游戏开发实战:Game Service Kit深度集成与跨设备性能优化
游戏·华为·性能优化·架构·harmonyos·ai-native
知识即是力量ol3 小时前
深度解析:基于 JWT + Redis 白名单的双令牌高安全认证架构
redis·安全·架构
要做一个小太阳4 小时前
华为Atlas 900 A3 SuperPoD 超节点网络架构
运维·服务器·网络·华为·架构
vx-bot5556664 小时前
企业微信接口在混合云环境下的集成架构与网络互联方案企业微信接口在混合云环境下的集成架构与网络互联方案
网络·架构·企业微信
JMchen12317 小时前
现代Android图像处理管道:从CameraX到OpenGL的60fps实时滤镜架构
android·图像处理·架构·kotlin·android studio·opengl·camerax
Jing_jing_X20 小时前
CPU 架构:x86、x64、ARM 到底是什么?为什么程序不能通用?
arm开发·架构·cpu