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

相关推荐
小雷FansUnion2 小时前
深入理解MCP架构:智能服务编排、上下文管理与动态路由实战
人工智能·架构·大模型·mcp
慌糖3 小时前
微服务介绍
微服务·云原生·架构
June bug4 小时前
【软考中级·软件评测师】下午题·面向对象测试之架构考点全析:分层、分布式、微内核与事件驱动
经验分享·分布式·职场和发展·架构·学习方法·测试·软考
Daniel_Coder6 小时前
Xcode 中常用图片格式详解
ios·xcode·swift
森焱森6 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
帅次7 小时前
Objective-C面向对象编程:类、对象、方法详解(保姆级教程)
flutter·macos·ios·objective-c·iphone·swift·safari
go54631584658 小时前
修改Spatial-MLLM项目,使其专注于无人机航拍视频的空间理解
人工智能·算法·机器学习·架构·音视频·无人机
RyanGo9 小时前
iOS断点下载
ios·swift
凌辰揽月10 小时前
8分钟讲完 Tomcat架构及工作原理
java·架构·tomcat
绝无仅有10 小时前
对接三方SDK开发过程中的问题排查与解决
后端·面试·架构