此文系转载,版权归原作者所有。原文从此处获取。
你知道“God Class”(全能类)是什么吗?就是在巨大的文件里,有一个类充满了各种分类、方法和变量。你的App里极有可能就有一些这样的“God Class”(全能类),甚至你自己都不知道。你知道“God Class”(全能类)是什么了吗?你感觉到它会对你的代码库造成毁灭性的影响了吗?往下看,我会告诉你怎么解决这些问题。
“God Class”(全能类)到底是什么?
一般来说,它存在于你的ViewController里,如果你使用的是MVC编程模式的话;如果你使用MVVM,则会在你的ViewModel里。让我来看一个MVVM编程模式下,“God Class”(全能类)的经典例子。
我们假设你正在构建一个约会App,你可能会需要一个UserViewModel
来存储相关数据和处理业务逻辑
struct User {
let id: UUID
var name: String?
var phone: String?
var email: String?
}
class UserViewModel {
private(set) var user: User?
init(id: String) {
self.user = fetchUser(with: id)
}
func fetchUser(with id: String) -> User? {
//fetch user data from the CoreData and optionally return a User object
}
func verifyPhone() {
if let phone = user?.phone {
// Perform phone number verification
}
}
func verifyEmail() {
if let email = user?.email {
// Perform email verification
}
}
func update(name: String) {
user?.name = name
}
func update(email: String) {
user?.email = email
verifyEmail()
}
}
复制代码
内容非常直接了当:
- 这个类有一个
User
对象; - 有一个初始化方法
init(id: String)
; - 有一个
func fetchUser(with id: String) -> User?
方法,用来从数据库读取用户信息; - 有两个验证方法
verifyPhone
和verifyEmail
用来验证用户信息; - 有两个更新方法
update(name: String)
和update(email: String)
用来更新用户的信息。
如你所见,这个类有三种业务逻辑,混杂在一个类里:
- 读取信息
- 验证信息
- 更新信息
甚至还会有更多复杂的业务逻辑。
这是一个典型的“God Class”(全能类),因为它什么都干。随着工程的不断迭代,“God Class”(全能类)很快就会变得十分臃肿巨大。
接下来,让我们看看面相协议编程能够怎样分解这些逻辑。
面向协议编程到底是什么?
根据Swift documentation的定义:
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.
协议定义了实现一个功能或者特定任务所需的方法、属性以及其它一些必须条件的蓝图。协议能够被类、结构体,以及枚举类型继承,并提供、实现这些必须的条件。任意满足这些协议必须条件的类型就被称之为遵守这个协议。
命名规则
建议使用形容词来命名协议,通常采用后缀ble或者ing。
创建以协议为基础的分类和方法
1. UserIdentifiable
// Identify User
protocol UserIdentifiable {
var user: User?
init(id: String)
}
复制代码
这个协议包含一个user
对象和一个init(id: String)
初始化方法。
2. UserFetchable
// Fetch data from the database
protocol UserFetchable {
func fetchUser(with id: String) -> User?
}
复制代码
这个协议有一个方法fetchUser(with id: String)
用来返回可选User
类型对象。
3. UserVerifiable
// Verify user info
protocol UserVerifiable {
func verifyPhone()
func verifyEmail()
}
复制代码
这个协议包含两个验证方法:verifyPhone
和verifyEmail
。
4. UserUpdatable
// Update user info
protocol UserUpdatable {
mutating func update(name: String)
mutating func update(email: String)
}
复制代码
这个协议包含两个更新方法:update(name: String)
和 update(email: String)
。因为我们会更新其它协议里定义的值,所以我们需要mutating
关键字来修饰这些方法。
用协议来拆解这个“God Class”(全能类)
注意: 协议可以继承其它的协议以此来获取访问其属性和方法的能力。
实现协议
我们可以简单的在协议下方写一个扩展来实现fetchUser
方法
protocol UserFetchable {
func fetchUser(with id: String) -> User?
}
extension UserFetchable {
func fetchUser(with id: String) -> User? {
//fetch user data from the CoreData and optionally return a User object
}
}
复制代码
对于UserVerifiable
协议,因为我们需要使用User对象来取得用户信息,所以我们需要继承UserIdentifiable
协议。
protocol UserVerifiable: UserIdentifiable {
func verifyPhone()
func verifyEmail()
}
extension UserVerifiable {
func verifyPhone() {
if let phone = user?.phone {
// Perform phone number verification
}
}
func verifyEmail() {
if let email = user?.email {
// Perform email verification
}
}
}
复制代码
实现UserUpdatable
协议时也一样,因为我们需要调用UserVerifiable
协议定义的verifyEmail
方法,所以我们需要继承UserVerifiable
协议。因为UserVerifiable
已经继承了UserIdentifiable
,所以UserUpdatable
可以访问用户信息。
protocol UserUpdatable: UserVerifiable {
mutating func update(name: String)
mutating func update(email: String)
}
extension UserUpdatable {
mutating func update(name: String) {
user?.name = name
}
mutating func update(email: String) {
user?.email = email
verifyEmail()
}
}
复制代码
这样我们基本上就完成了。最后,我们让UserViewModel
继承于UserFetchable
和UserUpdatable
。
由于我们还没有实现UserIdentifiable
协议,而且,把初始化方法放在类定义中也是好的做法。
所以,我们这样实现
struct User {
let id: UUID
var name: String?
var phone: String?
var email: String?
}
class UserViewModel: UserFetchable, UserUpdatable {
var user: User?
required init(id: String) {
self.user = fetchUser(with: id)
}
}
复制代码
总结
经过以上所有的重构之后,UserViewModel
变得更加清晰了。所有的方法和属性都按照他们的功能进行了对应的分组。如果你想要添加,删除或者更新这些方法和属性,你只需要直接去协议中进行修改。