Swift遗留代码重构:高级技术

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

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

开始课程

引言:为什么重构Swift遗留代码是一门艺术

处理Swift遗留代码是iOS/macOS开发者面临的最复杂、最负责任的任务之一。遗留项目通常包含“上帝对象”(God objects)、对UIKit的过度依赖、缺乏测试以及混乱的架构。高级重构不仅仅是重命名变量,而是在保持100%功能的前提下对代码库进行系统性改造。在本文中,我们将探讨有助于将混乱转变为可维护架构的技术。



1. 模块化:将单体拆分为组件

迈向规范化代码的第一步是提取独立模块。不要创建一个巨大的目标,而是创建独立的框架或Swift Package Manager模块。



定义模块边界

使用“高内聚,低耦合”原则。示例:提取网络请求层、业务逻辑层和UI层。



// 之前:所有代码都在一个ViewController文件中class LegacyViewController: UIViewController {    func fetchData() {        // URLSession代码在这里    }    func parseData() {        // JSON解析代码在这里    }}

// 之后:Network模块public class NetworkService { public func fetchData(completion: @escaping (Result<Data, Error>) -> Void) { // 纯净的网络层 }}


通过外观模式进行迁移

不要破坏现有代码:创建一个外观,将调用委托给新模块。逐步替换旧调用。



class LegacyNetworkFacade {    private let newService = NetworkService()        @objc func oldFetchMethod() {        newService.fetchData { result in            // 适配旧API        }    }}


2. 依赖注入(DI)替代全局单例

遗留代码常常受困于单例:UserManager.shared、APIClient.shared。这使得测试变得不可能。我们转向依赖注入。



通过初始化器进行手动注入

// 问题class OldViewModel {    let api = APIClient.shared    let user = UserManager.shared}

// 解决方案protocol APIClientProtocol { /* ... */ }protocol UserManagerProtocol { /* ... */ }

class NewViewModel { private let api: APIClientProtocol private let userManager: UserManagerProtocol init(api: APIClientProtocol, userManager: UserManagerProtocol) { self.api = api self.userManager = userManager }}


集成DI容器(Swinject/Resolver)

对于大型项目,使用库。以Resolver为例:

import Resolver

extension Resolver: ResolverRegistering { public static func registerAllServices() { register { NetworkService() as NetworkServiceProtocol } register { UserDefaultsStorage() as StorageProtocol } }}

class RefactoredViewModel { @Injected private var network: NetworkServiceProtocol @Injected private var storage: StorageProtocol}


3. 测试:在测试保护下进行重构

没有测试的重构就像玩俄罗斯轮盘赌。我们为旧代码创建“测试框架”(特征测试)。



针对遗留代码的特征测试

将当前行为记录为测试,即使它并不完美。

class LegacyCalculatorTests: XCTestCase {    func testLegacyMultiply() {        // 记录当前行为        let result = LegacyCalculator().multiply(2, 3)        XCTAssertEqual(result, 6) // 即使逻辑奇怪    }}


使用黄金主版本进行并行重构

比较旧实现和新实现的输出。使用XCTAssertEqual验证一致性。



func testRefactoredMatchesL                

博客

书籍推荐