这里每天分享一个 iOS 的新知识,快来关注我吧
前言
在软件工程中,SOLID 原则是五个基本原则,旨在促进更好的软件设计:更可维护、更易于理解、更灵活。对于 iOS 开发者来说,掌握和应用这些原则是非常重要的,因为它们可以帮助你构建出高质量的应用程序。今天准备来尝试介绍下我对这些原则的理解,并提供具体的 iOS 开发例子来说明如何在实践中应用这些原则。
单一职责原则 (SRP)
单一职责原则指的是一个类应该只负责一项任务。在 iOS 开发中,遵循此原则可以避免创建庞大的类,每个类都应该专注于单一的功能。
举个例子,比如你有一个个人中心的控制器 UserProfileViewController
,它的职责是展示用户的个人信息,而不应该同时负责从网络获取这些信息。这种职责的分离使得代码更易于维护和测试。
less
class UserProfileService {
func fetchUserProfile(completion: @escaping (UserProfile) -> Void) {
// 实现网络请求获取用户资料
}
}
如果你的这个页面有网络请求相关的代码,可以考虑创建另一个类,比如 UserProfileService
,在这个类中进行页面需要的数据请求,这样我们就使 UserProfileViewController
遵循了单一职责原则,使其变得更加清晰和易于管理。
开放封闭原则 (OCP)
开放封闭原则强调软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。
什么意思呢,一个类一旦设计开发完成,就应该可以独立完成其工作,而不要对类进行任何修改,如果你想要增加功能,应该通过扩展/集成/实现接口等方式。
在 iOS 开发中,这种原则也很常见,比如我要设计一个图像处理功能的协议 ImageFilter
,协议一旦设计完成,就不再更改了,假设这个协议只有一个方法 apply(to:)
输入一个图片,返回一个图片。
swift
protocol ImageFilter {
func apply(to image: UIImage) -> UIImage
}
如果此时我想增加一个模糊滤镜的功能,我可以新建一个类来遵守这个协议,并实现 apply(to:)
方法:
swift
class BlurFilter: ImageFilter {
func apply(to image: UIImage) -> UIImage {
// 应用模糊滤镜...
return image
}
}
这样,任何新的滤镜都可以通过实现ImageFilter
协议来创建,从而遵循了开放封闭原则。
里氏替换原则 (LSP)
里氏替换原则其实是针对继承而言的,要求子类能够替换其基类,而不破坏程序的正确性。这意味着子类应该完全实现父类的行为。
在现在的编程语言中基本都符合这个原则,在 iOS 开发中也不例外,一个简单的例子,如果我们有一个 Bird
类和一个 FlyingBird
子类,那么任何 FlyingBird
的实例都应该能够替换Bird
的实例。
swift
class Bird {
func layEgg() {
print("Laying an egg.")
}
}
class FlyingBird: Bird {
func fly() {
print("Flying.")
}
}
通过确保 FlyingBird
遵循 LSP,我们可以确保我们的代码更加灵活和可靠。
接口隔离原则 (ISP)
接口隔离原则指的是客户端不应该依赖它不使用的接口,这其实跟单一职责有点关系,一个大型接口往往有很多功能,但是继承者往往不需要这么多功能,就需要把大型接口拆分为更小、更具体的接口。
例如,你有一个打印机的类,它拥有打印、扫描、复制等功能,我们可以将打印、扫描和复印功能分解为独立的协议,这样,打印机只需要依赖打印功能、扫描仪就只需要依赖扫描功能,或者选择性的依赖多个功能。
swift
// 打印功能
protocol Printer {
func printDocument(document: Document)
}
// 扫描功能
protocol Scanner {
func scanDocument(document: Document) -> Data
}
// 复印功能
protocol Copier {
func copyDocument(document: Document) -> Document
}
// 只打印,只需要遵守 Printer 协议
class SimplePrinter: Printer {
func printDocument(document: Document) {
}
}
// 办公室多功能打印机,需要遵守 Printer, Scanner, Copier 协议
class OfficePrinter: Printer, Scanner, Copier {
func printDocument(document: Document) {
// 打印
}
func scanDocument(document: Document) -> Data {
// 扫描
return Data()
}
func copyDocument(document: Document) -> Document {
// 复印
return Document()
}
}
这样,实现这些协议的类只需要关心它们真正需要的功能,从而使代码更加清晰和灵活。
依赖倒置原则 (DIP)
依赖倒置原则强调高层模块不应该依赖低层模块,两者都应该依赖抽象。
概念还是太抽象了,我们来举个例子帮助理解,比如你有个从网络获取数据的类 NetworkFetcher
,和一个从数据库获取数据的类 DatabaseFetcher
,然后有个提供数据的工具类 DataProvider
来直接依赖前面两个类,这种做法就违背了依赖倒置原则。
正确的做法是将 NetworkFetcher
和 DatabaseFetcher
抽象出一个新的接口给 DataProvider
依赖,我们可以定义一个 DataFetcher
协议,然后让 NetworkFetcher
和 DatabaseFetcher
类实现这个协议:
less
protocol DataFetcher {
func fetchData(completion: @escaping (Data?) -> Void)
}
class NetworkFetcher: DataFetcher {
func fetchData(completion: @escaping (Data?) -> Void) {
// 从网络获取数据
}
}
class DatabaseFetcher: DataFetcher {
func fetchData(completion: @escaping (Data?) -> Void) {
// 从数据库获取数据
}
}
class DataProvider {
let fetcher: DataFetcher
init(fetcher: DataFetcher) {
self.fetcher = fetcher
}
func fetchData() {
fetcher.fetchData { data in
// 使用数据
}
}
}
这样,DataProvider
类就可以依赖于 DataFetcher
协议,而不是任何具体的数据获取方式,从而提高了代码的灵活性和可测试性。
结论
遵循 SOLID 原则是构建健壮、可维护和灵活的 iOS 应用程序的关键。通过实际的例子,我们可以看到这些原则如何帮助我们改进设计和架构。虽然有些人(或者初学者)可能会觉得遵循这些原则有些麻烦或者困难,但随着经验的不断积累,你就会越来越能感觉到这些原则的重要性,学习更多编程的哲学,这也是你变成大佬的必经之路。
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!