Swift遗留代码重构:从A到Z

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Swift遗留代码重构:从A到Z

\n

处理遗留代码(legacy code)是iOS/macOS开发者不可避免的一部分。这些代码通常是用早期版本的Swift甚至Objective-C编写的,缺乏测试,包含“魔法数字”和庞大的UIViewController。在本文中,我们将逐步分析重构此类代码的策略,将其转变为现代、可维护且可测试的项目。

\n\n

1. 准备与安全:在触碰代码之前

\n

重构最重要的规则是:不要破坏正在运行的功能。没有测试,任何更改都如同跳入未知领域。

\n
    \n
  • 特性测试(Characterization Tests):编写能够固定当前行为的测试。即使代码看起来不正确,也要先记录其工作方式。
  • \n
  • 版本控制系统:提交当前状态的代码。使用功能分支(feature branch)。
  • \n
  • 工具:使用Xcode的Edit > Refactor(重命名、提取方法),以及代码检查工具(如SwiftLint)来保持统一风格。
  • \n
\n\n
// 冻结旧方法行为的示例\nclass LegacyCalculator {\n    func calculateTotal(items: [Int]) -> Int {\n        // 旧的、难以阅读的逻辑\n        var result = 0\n        for i in items {\n            result += i * 2\n        }\n        return result + 10\n    }\n}\n\n// 固定行为的测试\nfunc testCalculateTotal() {\n    let sut = LegacyCalculator()\n    let result = sut.calculateTotal(items: [1, 2, 3])\n    XCTAssertEqual(result, 22) // (1+2+3)*2 + 10 = 22\n}\n
\n\n

2. “替罪羊”策略:全局变量和单例

\n

遗留代码以其全局状态和单例而臭名昭著。这使得代码高度耦合且不可预测。

\n

问题:

\n
class NetworkManager {\n    static let shared = NetworkManager()\n    var baseURL: String = "http://old.api.com"\n    func fetchData() { ... }\n}\n
\n

解决方案:依赖注入(Dependency Injection)。创建协议并通过初始化器传递依赖。

\n
protocol NetworkService {\n    func fetchData()\n}\n\nclass ModernViewModel {\n    private let network: NetworkService\n    \n    init(network: NetworkService) {\n        self.network = network\n    }\n    \n    func load() {\n        network.fetchData()\n    }\n}\n
\n

这种方法可以轻松地在测试中替换服务,并使代码模块化。

\n\n

3. 对抗“臃肿的控制器”(Massive View Controller)

\n

iOS遗留代码的经典问题:UIViewController负责所有事情——从网络请求到UI格式化。重构通过分离职责(SRP)进行。

\n\n

步骤1:将业务逻辑提取到单独的模型(ViewModel)中。

\n
// 之前在控制器中\nfunc buttonTapped() {\n    let data = fetchData()\n    let total = data.reduce(0, +)\n    label.text = "总和:\\(total)"\n}\n\n// 现在:ViewModel\nclass MyViewModel {\n    func calculateTotal(data: [Int]) -> String {\n        let total = data.reduce(0, +)\n        return "总和:\\(total)"\n    }\n}\n
\n\n

步骤2:将UI配置提取到单独的方法或工厂中。

\n
    \n
  • 为复杂层次结构创建ViewBuilder类。
  • \n
  • 使用UICollectionViewDiffableDataSource替代旧的dataSource。
  • \n
\n\n

4. 现代化:从Objective-C到Swift和新API

\n

遗留代码通常包含Objective-C和Swift的混合。重构时,更新过时的结构很重要:

\n\n
    \n
  • 用throws替换NSError:现代Swift使用异常处理错误。
  • 放弃!(强制解包):使用可选绑定(optional binding)或guard let。
  • 使用Codable替代JSONSerialization:简化JSON解析。
  • 采用async/await:用现代并发模型替换基于闭包的回调。
\n\n
// 旧方式(基于闭包)\nfunc fetchUser(completion: @escaping (User?) -> Void) {\n    // ...\n}\n\n// 新方式(async/await)\nfunc fetchUser() async -> User? {\n    // ...\n}\n
\n\n

5. 逐步重构:小步前进

\n

不要试图一次性重写所有内容。使用“童子军规则”(每次离开时让营地比来时更干净)。

\n
    \n
  • 识别“坏味道”:长方法、重复代码、过度耦合。
  • \n
  • 应用重构技术:提取方法、重命名变量、引入参数对象。
  • \n
  • 持续测试:每次更改后运行测试。
  • \n
\n\n

结论

\n

重构遗留代码不是一次性事件,而是一个持续的过程。从小的安全步骤开始,逐步引入现代实践,您的代码库将变得更健康、更易于维护。记住:完美不是目标,进步才是。

博客

书籍推荐