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

相关推荐
serendipity_hky4 小时前
【微服务 - easy视频 | day03】服务与服务之间的调用
spring boot·spring cloud·微服务·架构
三口吃掉你5 小时前
微服务之Nacos(注册中心、配置中心)
微服务·nacos·架构
失散137 小时前
分布式专题——51 ES 深度分页问题及其解决方案详解
java·分布式·elasticsearch·架构
Lethehong9 小时前
百万迁移费成历史?金仓数据库“零代码”替换Oracle,我们扒了扒它的技术底牌
后端·mysql·架构
门思科技9 小时前
LoRa 与 LoRaWAN 技术解析:物理层原理、网络架构与典型物联网应用场景
网络·物联网·架构
okjohn9 小时前
《架构师修炼之路》——②对架构的基本认识
java·架构·系统架构·软件工程·团队开发
狼爷10 小时前
如何防止重复提交订单?——从踩坑到优雅落地的实战指南
java·架构
卓豪终端管理10 小时前
零信任架构落地难?从终端安全开始破局
安全·架构
m0_4955627811 小时前
Swift-GCD和NSOperation
ios·cocoa·swift
阿廖沙102411 小时前
从 0 到 100 万用户的系统演进之路
架构