DRY原则

什么是DRY原则? DRY(Don’t Repeat Yourself)原则是由 Andy Hunt 和 Dave Thomas 在《The Pragmatic Programmer》一书中提出的软件开发原则。 Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. 系统中的每一项知识都必须有一个单一、明确、权威的表示。 DRY 原则的核心不仅仅是"不要复制粘贴代码",而是确保知识和逻辑在系统中只存在一处。 核心思想 知识的单一来源 DRY 原则强调的是"知识"的不重复,而非简单的"代码"不重复。知识包括: 业务规则:如"订单金额超过100元免运费" 数据结构定义:如用户模型的字段 算法逻辑:如价格计算公式 配置信息:如 API 地址、超时时间 // 违反 DRY:同一个业务规则在多处定义 class OrderService { func calculateShipping(orderAmount: Double) -> Double { if orderAmount > 100 { // 业务规则:100元以上免运费 return 0 } return 10 } } class CartViewController { func updateShippingLabel() { if cartTotal > 100 { // 重复的业务规则! shippingLabel.text = "免运费" } else { shippingLabel.text = "运费:¥10" } } } class CheckoutViewModel { func getShippingFee() -> Double { return totalAmount > 100 ? 0 : 10 // 又是重复! } } // 遵循 DRY:业务规则只定义一次 struct ShippingPolicy { static let freeShippingThreshold: Double = 100 static let standardShippingFee: Double = 10 static func calculateFee(for orderAmount: Double) -> Double { return orderAmount > freeShippingThreshold ? 0 : standardShippingFee } static func isFreeShipping(for orderAmount: Double) -> Bool { return orderAmount > freeShippingThreshold } } // 所有地方都使用这个单一来源 class OrderService { func calculateShipping(orderAmount: Double) -> Double { return ShippingPolicy.calculateFee(for: orderAmount) } } class CartViewController { func updateShippingLabel() { if ShippingPolicy.isFreeShipping(for: cartTotal) { shippingLabel.text = "免运费" } else { shippingLabel.text = "运费:¥\(ShippingPolicy.standardShippingFee)" } } } DRY vs WET WET 是 DRY 的反义词,常见的解释有: ...

May 2, 2026

KISS原则

什么是KISS原则? KISS(Keep It Simple, Stupid)原则是软件工程中最重要的设计原则之一。它的核心理念是:简单的解决方案比复杂的解决方案更好。 核心思想 简单不等于简陋 KISS 原则强调的"简单"是指: 易于理解:代码的意图一目了然 易于修改:改动时不会牵一发而动全身 易于调试:出问题时容易定位原因 易于测试:测试用例简单直接 简单不意味着功能缺失或代码草率,而是用最直接的方式解决问题。 复杂性的代价 不必要的复杂性会带来: 更高的学习成本:新成员需要更长时间理解代码 更多的 bug:复杂逻辑更容易出错 更难维护:修改一处可能影响多处 更慢的开发速度:需要考虑更多的边界情况 graph LR A[复杂性增加] --> B[理解成本上升] A --> C[Bug 概率上升] A --> D[维护难度上升] B --> E[开发效率下降] C --> E D --> E iOS开发中的KISS实践 1. 选择简单的架构 // 过度设计:小项目使用复杂的 VIPER 架构 // 每个模块都有 View, Interactor, Presenter, Entity, Router // 一个简单的列表页面需要创建 5+ 个文件 // KISS:根据项目规模选择合适的架构 // 小项目:MVC 就足够了 class UserListViewController: UIViewController { private var users: [User] = [] override func viewDidLoad() { super.viewDidLoad() loadUsers() } private func loadUsers() { Task { users = try await UserService.shared.fetchUsers() tableView.reloadData() } } } // 随着项目增长,再逐步引入更复杂的架构 2. 避免过早优化 // 违反 KISS:过早引入复杂的缓存机制 class ImageLoaderOverEngineered { private let memoryCache = NSCache<NSString, UIImage>() private let diskCache: DiskCache private let networkQueue = DispatchQueue(label: "imageLoader", attributes: .concurrent) private let cacheQueue = DispatchQueue(label: "cacheQueue") private var pendingRequests: [URL: [(UIImage?) -> Void]] = [:] private let requestLock = NSLock() func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) { // 复杂的多级缓存、请求合并、线程同步逻辑... // 对于大多数 App 来说,这可能是过度设计 } } // KISS:从简单开始 class SimpleImageLoader { private let cache = NSCache<NSString, UIImage>() func loadImage(from url: URL) async -> UIImage? { let key = url.absoluteString as NSString // 先检查缓存 if let cached = cache.object(forKey: key) { return cached } // 从网络加载 guard let (data, _) = try? await URLSession.shared.data(from: url), let image = UIImage(data: data) else { return nil } // 存入缓存 cache.setObject(image, forKey: key) return image } } // 当真正遇到性能问题时,再逐步优化 3. 简化条件逻辑 // 违反 KISS:复杂的嵌套条件 func processOrder(_ order: Order) -> Result<Receipt, OrderError> { if order.items.count > 0 { if order.customer != nil { if order.customer!.isVerified { if order.paymentMethod != nil { if order.shippingAddress != nil { if order.totalAmount > 0 { // 终于可以处理订单了... return .success(Receipt()) } else { return .failure(.invalidAmount) } } else { return .failure(.noShippingAddress) } } else { return .failure(.noPaymentMethod) } } else { return .failure(.unverifiedCustomer) } } else { return .failure(.noCustomer) } } else { return .failure(.emptyOrder) } } // KISS:使用 Guard 早期返回 func processOrderSimple(_ order: Order) -> Result<Receipt, OrderError> { guard !order.items.isEmpty else { return .failure(.emptyOrder) } guard let customer = order.customer else { return .failure(.noCustomer) } guard customer.isVerified else { return .failure(.unverifiedCustomer) } guard order.paymentMethod != nil else { return .failure(.noPaymentMethod) } guard order.shippingAddress != nil else { return .failure(.noShippingAddress) } guard order.totalAmount > 0 else { return .failure(.invalidAmount) } return .success(Receipt()) } 4. 使用标准库而非自己实现 // 违反 KISS:自己实现数组去重 func removeDuplicatesManual<T: Hashable>(from array: [T]) -> [T] { var seen = Set<T>() var result = [T]() for element in array { if !seen.contains(element) { seen.insert(element) result.append(element) } } return result } // KISS:使用标准库 let uniqueArray = Array(Set(array)) // 或保持顺序 let uniqueOrderedArray = array.reduce(into: [T]()) { result, element in if !result.contains(element) { result.append(element) } } // 更简单:使用 Swift Algorithms import Algorithms let unique = array.uniqued() 5. 避免过度泛型化 // 违反 KISS:过度泛型,难以理解 protocol DataProviding { associatedtype DataType associatedtype ErrorType: Error func fetch() async throws -> DataType } protocol DataTransforming { associatedtype Input associatedtype Output func transform(_ input: Input) -> Output } class GenericDataLoader< Provider: DataProviding, Transformer: DataTransforming > where Provider.DataType == Transformer.Input { // 复杂的泛型约束... } // KISS:简单直接的实现 class UserLoader { func loadUsers() async throws -> [User] { let data = try await APIClient.shared.fetch("/users") return try JSONDecoder().decode([User].self, from: data) } } // 当真正需要复用时,再考虑抽象 6. 简化 API 设计 // 违反 KISS:过多的配置选项 func createButton( title: String, titleColor: UIColor = .white, titleFont: UIFont = .systemFont(ofSize: 16), backgroundColor: UIColor = .blue, cornerRadius: CGFloat = 8, borderWidth: CGFloat = 0, borderColor: UIColor = .clear, shadowColor: UIColor = .black, shadowOffset: CGSize = .zero, shadowRadius: CGFloat = 0, shadowOpacity: Float = 0, contentInsets: UIEdgeInsets = .init(top: 12, left: 24, bottom: 12, right: 24), isEnabled: Bool = true, // ... 更多参数 ) -> UIButton { // ... } // KISS:提供简单的默认实现和预设样式 enum ButtonStyle { case primary case secondary case destructive case text } func createButton(title: String, style: ButtonStyle = .primary) -> UIButton { let button = UIButton() button.setTitle(title, for: .normal) switch style { case .primary: button.backgroundColor = .systemBlue button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 case .secondary: button.backgroundColor = .systemGray5 button.setTitleColor(.systemBlue, for: .normal) button.layer.cornerRadius = 8 case .destructive: button.backgroundColor = .systemRed button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 8 case .text: button.backgroundColor = .clear button.setTitleColor(.systemBlue, for: .normal) } return button } // 使用简单 let primaryButton = createButton(title: "Submit") let cancelButton = createButton(title: "Cancel", style: .secondary) 7. 选择合适的数据结构 // 违反 KISS:用复杂的数据结构解决简单问题 class UserStatusManager { private var statusTree: RedBlackTree<String, UserStatus> private var statusIndex: [String: TreeNode<String, UserStatus>] // 复杂的树结构来管理用户状态... } // KISS:简单的字典就够了 class SimpleUserStatusManager { private var userStatus: [String: UserStatus] = [:] func setStatus(_ status: UserStatus, for userId: String) { userStatus[userId] = status } func getStatus(for userId: String) -> UserStatus? { return userStatus[userId] } } 如何平衡简单与功能 渐进式复杂度 遵循"简单开始,按需增长"的原则: ...

May 2, 2026

SOLID原则

概述 SOLID 是面向对象设计的五大基本原则的首字母缩写,由 Robert C. Martin(Uncle Bob)在 2000 年代初期提出。这五个原则是构建可维护、可扩展软件的基础,在 iOS 开发中同样适用。 字母 原则 英文名称 S 单一职责原则 Single Responsibility Principle O 开放封闭原则 Open-Closed Principle L 里氏替换原则 Liskov Substitution Principle I 接口隔离原则 Interface Segregation Principle D 依赖倒置原则 Dependency Inversion Principle 单一职责原则(Single Responsibility Principle, SRP) 定义 A class should have one, and only one, reason to change. 一个类应该只有一个引起它变化的原因。 原则解读 单一职责原则要求每个类只负责一项职责。这里的"职责"可以理解为"变化的原因"——如果你能想到多于一个的原因去改变一个类,那么这个类就有多于一个的职责。 核心思想:将不同的关注点分离,使每个模块只专注于做好一件事。 解决的问题 1. 代码耦合度高 当一个类承担多个职责时,这些职责会相互依赖、相互影响。修改其中一个职责的代码,可能会无意中破坏另一个职责的功能。 2. 难以复用 如果一个类混合了多个职责,当你只想使用其中一个职责时,不得不引入整个类及其所有依赖。 3. 维护困难 职责混杂的类通常代码量大、逻辑复杂,理解和修改都很困难。不同职责的代码交织在一起,修改一处可能产生意想不到的连锁反应。 4. 测试复杂 ...

May 2, 2026

迪米特法则

什么是迪米特法则? 迪米特法则(Law of Demeter, LoD)也称为最少知识原则(Least Knowledge Principle, LKP),由 Ian Holland 在 1987 年提出。 Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. 每个单元应该只对与其紧密相关的单元有有限的了解。 更通俗的表述是:只和直接朋友交流,不要和陌生人说话。 核心思想 什么是"直接朋友"? 一个对象的"直接朋友"包括: 当前对象本身(this/self) 作为参数传入的对象 当前对象创建的对象 当前对象的成员变量 全局对象(如单例,技术上可访问,但应谨慎依赖) class Person { let wallet: Wallet // 成员变量 - 直接朋友 func buyItem(shop: Shop, item: Item) { // 参数 - 直接朋友 let receipt = Receipt() // 自己创建的对象 - 直接朋友 // 以下都是与直接朋友交流 let money = wallet.getMoney() shop.sell(item: item) receipt.record(item: item) } } 什么是"陌生人"? 如果需要通过多层对象去访问另一个对象,那个被间接访问的对象就是"陌生人"。 ...

May 2, 2026