Swift-SOLID编程原则的实践

SOLID 设计原则概述

SOLID 是 面向对象程序设计中,非常重要的原则,遵循此原则进行编程,意在提高代码的可维护性、可扩展性、可测试性,并减少耦合。

缩写 名称 含义
S Single Responsibility Principle 单一职责
O Open/Closed Principle 开闭原则
L Liskov Substitution Principle 里氏替换
I Interface Segregation Principle 接口隔离
D Dependency Inversion Principle 依赖倒置

单一职责

一个类只负责一项职责。也就是说,一个类应该只有一个引起它变化的原因。

  • 反面教材🚫
swift 复制代码
class UserManager {

    func registerUser(username: String, password: String) {
        // 注册逻辑
        print("注册一个用户: \(username)")
        
        // 发送邮件(这属于另一种职责)
        sendWelcomeEmail(to: username)
    }

    private func sendWelcomeEmail(to username: String) {
        print("发邮件给: \(username)")
    }
}

这里 ,UserManager 同时负责"注册用户"和"发送邮件",职责不单一。另外,发送邮件的业务 原则上只能通过调用sendWelcomeEmail来实现。而这里,调用registerUser也能触发 邮件的发送。不合理。

  • 正确示例✅
swift 复制代码
class UserRegistrationService {
    func register(username: String, password: String) {
        print("User registered: \(username)")
    }
}

class EmailService {
    func sendWelcomeEmail(to username: String) {
        print("Welcome email sent to \(username)")
    }
}

// 使用时
let registrationService = UserRegistrationService()
let emailService = EmailService()
registrationService.register(username: "Tom", password: "123")
emailService.sendWelcomeEmail(to: "Tom")

开闭原则

类、模块、函数应该对扩展开放,对修改关闭。即:不要修改已有代码来适配新需求,而是通过扩展实现。

  • 反面教材🚫
swift 复制代码
class PaymentProcessor {
    func pay(amount: Double, method: String) {
        if method == "wechat" {
            print("微信支付: \(amount)")
        } else if method == "alipay" {
            print("支付宝支付: \(amount)")
        } else {
            print("其他支付")
        }
    }
}

这样写,每新增一种支付方式,都要修改 pay方法。

  • 正确示例✅
swift 复制代码
protocol PaymentMethod {
    func pay(amount: Double)
}

class WeChatPay: PaymentMethod {
    func pay(amount: Double) {
        print("微信支付: \(amount)")
    }
}

class Alipay: PaymentMethod {
    func pay(amount: Double) {
        print("支付宝支付: \(amount)")
    }
}

class PaymentProcessor {
    func process(amount: Double, using method: PaymentMethod) {
        method.pay(amount: amount)
    }
}

// 使用
let processor = PaymentProcessor()
processor.process(amount: 100, using: WeChatPay())
processor.process(amount: 100, using: Alipay())

这样,扩展支付方式时,只需新增类,不改旧代码。

在面向对象编程中,一个类要做什么,考虑扩展需要,最好设计为接口(protocol),而不是直接设计为函数及实现。这也是面向协议编程的思想。

里氏替换

子类必须能够替代父类使用,且不影响程序的正确性。

  • 反面教材🚫
swift 复制代码
class Bird {
    func fly() {
        print("鸟 在飞")
    }
}

class Penguin: Bird {
    override func fly() {
        fatalError("企鹅不能飞")
    }
}

这里, 子类通过重载写自己的业务,但是修改了父类方法实现。这样写代码不好。

  • 正确示例✅
swift 复制代码
protocol Bird {
    func eat()
}

protocol FlyingBird: Bird {
    func fly()
}

class Sparrow: FlyingBird {
    func eat() { print("麻雀 吃") }

    func fly() { print("麻雀 飞") }
}

class Penguin: Bird {
    func eat() { print("企鹅 吃") }
}

通过继承接口,按需得到相应的能力/做需要的事情

协议的继承

接口隔离

不应强迫客户端依赖它不使用的方法。也就是:设计接口 应精简、专一。外界按需继承。

  • 反面教材🚫
swift 复制代码
protocol Worker {
    func work()
    func eat()
}

class Robot: Worker {
    func work() { print("机器人工作") }
    
    func eat() { }   // 不需要
}
  • 正确示例✅
swift 复制代码
protocol Workable {
    func work()
}

protocol Eatable {
    func eat()
}

class Human: Workable, Eatable {
    func work() { print("人 工作") }
    func eat() { print("人 吃饭") }
}

class Robot: Workable {
    func work() { print("机器人工作") }
}

依赖倒置

高层模块不应依赖低层模块,两者都应依赖抽象。换句话说:依赖接口,不依赖实现。(解耦)

  • 反面教材🚫
swift 复制代码
class MySQLDatabase {
    func save(data: String) {
        print("保存到 MySQL: \(data)")
    }
}

class UserManager {
    let db = MySQLDatabase()
    
    func saveUser(name: String) {
        db.save(data: name)
    }
}

这样写,类 UserManager 直接依赖具体数据库实现,耦合严重。

  • 正确示例✅
swift 复制代码
protocol Database {
    func save(data: String)
}

class MySQLDatabase: Database {
    func save(data: String) {
        print("保存到 MySQL: \(data)")
    }
}

class SQLiteDatabase: Database {
    func save(data: String) {
        print("保存到 SQLite: \(data)")
    }
}

class UserRepository {
    private let database: Database
    
    init(database: Database) {
        self.database = database
    }
    
    func saveUser(name: String) {
        database.save(data: name)
    }
}

// 使用时 可自由替换数据库
let repo = UserRepository(database: SQLiteDatabase())
repo.saveUser(name: "Tom")

依赖倒置让代码更易于扩展、测试与切换实现。

通过公共协议 来实现类与类的交互,实现解耦

相关推荐
美狐美颜sdk1 天前
跨平台直播美颜sdk集成攻略:Android、iOS与Web的统一方案
android·前端·ios
2501_915106322 天前
“HTTPS Everywhere” 的工程化实践,从全面加密到排查与真机取证
网络协议·http·ios·小程序·https·uni-app·iphone
Digitally2 天前
如何在 iPhone 上录制屏幕 - 三大方法
ios·cocoa·iphone
非专业程序员Ping2 天前
HarfBuzz 实战:五大核心API 实例详解【附iOS/Swift实战示例】
android·ios·swift
00后程序员张2 天前
Web 前端工具全流程指南 从开发到调试的完整生态体系
android·前端·ios·小程序·uni-app·iphone·webview
VirusTarget2 天前
探索 Xcode String Catalog:现代化 iOS 应用国际化指南
ios
pop_xiaoli2 天前
SQLite3语句以及FMDB数据存储初步学习
学习·ios·sqlite·objective-c·cocoa
磊怀2 天前
iOS 中的引用计数
ios
游戏开发爱好者82 天前
iOS 崩溃日志分析工具全指南,多工具协同构建稳定性分析体系
android·macos·ios·小程序·uni-app·cocoa·iphone
00后程序员张3 天前
如何提高 IPA 安全性 多工具组合打造可复用的 iOS 加固与反编译防护体系(IPA 安全 iOS 加固 无源码混淆 Ipa Guard 实战)
android·安全·ios·小程序·uni-app·iphone·webview