设计模式(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对象,从而节省对象开销。

相关推荐
Jlzn88881 小时前
华东电芯分选通道数怎么配才不堵线
java·人工智能·架构·自动化·制造
CCblandy2 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
阡陌数智2 小时前
Gemini 4 Argon 百万 Token 输出能力解析:原生长输出模型的工程落地与并发成本治理
人工智能·架构
海宇数据5 小时前
零信任架构实战:基于海宇入职背调报告构建自动化即时配送与网约车众包运力准入鉴权网关
运维·人工智能·架构·自动化
天远API6 小时前
零信任架构实战:基于天远公安二要素政务版构建自动化不动产交易与契税核算实名核实网关
人工智能·架构·自动化·php
预知同行7 小时前
深入解析 MoE 混合专家模型:从 Router 到 All-to-All 的架构全景与生产实践
后端·架构
Linye7 小时前
换个会话就忘了方案?我把项目约定写成了一个文件
架构
KylinLab7 小时前
四条主张五条禁令:人不在环内的组织级契约
架构
奔跑的架构师7 小时前
[A-51]ARMv9/v8-SCMI系统架构
linux·arm开发·架构·系统架构·arm
糖果豆豆7 小时前
让 AI 自查三遍,它只会把错的说得更像对的——检查的人和被检查的,是同一个
算法·架构