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

相关推荐
kyriewen111 小时前
项目做了一半想重写?这套前端架构让你少走3年弯路
前端·javascript·chrome·架构·ecmascript·html5
空中海2 小时前
第十二章:安卓实战架构与最佳实践
android·架构
visual_zhang2 小时前
Swift 方法派发机制深度解析 —— 兼与 Objective-C `objc_msgSend` 对比
objective-c·swift
一个有温度的技术博主5 小时前
Spring Cloud 入门与实战:从架构拆分到核心组件详解
spring·spring cloud·架构
小江的记录本5 小时前
【系统设计】《2026高频经典系统设计题》(秒杀系统、短链接系统、订单系统、支付系统、IM系统、RAG系统设计)(完整版)
java·后端·python·安全·设计模式·架构·系统架构
Mintopia5 小时前
接口为什么越写越难改:从一开始就能避免的设计问题
架构
预知同行8 小时前
Planning Agent 架构深度解析:从 ReAct 到 Plan-and-Execute 与 Reflexion 的工程实践
架构
用户79457223954138 小时前
【SwiftyJSON】拯救你的 as? [String: Any]——链式 JSON 访问的正确姿势
swiftui·objective-c·swift
用户79457223954138 小时前
【Moya】为什么你的 Alamofire 代码需要再封装一层?
swiftui·objective-c·swift