UserProfile
每个用户都有 name
、 firstName
、 lastName
有些用户还有 email
、phoneNumber
。模型可能会是这样:
|
|
可能我们每天都会看到这样的模型,也习惯了这样的模型,但仔细思考一下。email
有没有把信息完整的表示出来呢?还有phoneNumber
? 他俩仅仅只是字符串吗?
如果他有在父类中没有的其他行为,则引入一个新的类型。 — Martin Fowler
make a type if it will have some special behavior in its operations that the base type doesn’t have. by Martin Fowler
在之后的代码里面,我们可能就会用到这两个属性。比如说打电话,或者是给用户发送邮件。但是从模型里面,我们只能推导出来这两个东西是 String?
当然,我们可以在每次使用的时候都做一次校验,或者在初始化方法里面校验,但是这都不是类型层面上的东。如果有其他人要用这段代码的时候,他们知道这两个东西需要做一次校验吗?加一段注释:// 这个属性需要做一次校验
是最有效的方法吗?又如果项目中还有一个类Contact
也需要用到 phoneNumber
呢?校验的代码又复制过去?
|
|
我们至少能用到 typealias
来让他们知道, 这些东西跟 String 还是有一点区别的。
不知道你有没有发现 TimeInterval
、CLLocationDegrees
这个两个类型,实际上都是 Double
的类型别名。为什么要这么做呢?答案是: 为了上下文。当我们看到 CLLocationDegrees
的时候,我们就能知道这个值的范围在 [-180,180] 之间。如果这个值是不可能是 1000 的。同样,手机号也不可能是一个类似 “HelloWorld” 的字符串。
当我们使用 typealias
来声明自定义类型的时候,我们可以在今后的版本中轻易的给他添加更多的上下文,而不用去修改很多的代码。比如说 CGFloat
,在早期的 Swift 版本中,这个属性只是 Double 的一个类型别名,而现在他已经是一个完整的数据类型了。
现在我们来试试怎样把刚刚的类型别名改成一个完整的数据类型:
|
|
现在的代码就更能表示出 UserFrofile.email
具体是什么东西了,这样的代码甚至能够避免以后会出现的问题。
比如下面的问题。比如说我们并没有重写 UserPrifile
,现在我们需要用户的全名。
|
|
处于一些原因,我们错误的把 cellNumber
拼在了后面(正确的应该是 lastName
)。编译器不会发现这其实是个错误,因为对编译器来说他们都是 String
。这个问题有可能在运行时才会被发现出来,甚至是测试阶段。但是如果使用 PhoneNumber
这个类型的话,这个问题在编译期就能够被发现了。
User
随着 App 的发展,我们肯能会用到 OTP 作为 App 的登录方法。这时候可能还会引入一个新的类型 User
|
|
如果完全按照上文的方法,代码会变成这样子:
|
|
如果我们接着写下去,代码会变得很啰嗦。PhoneNumber
还有 Email
还有一些意义, 而 UserID
还有 UserProfile
在 User
之外没有任何意义。所以再把代码整理一下:
|
|
如果要使用 UserProfile
的时候,我们可以使用 User.Profile
来替代。同样 UserID
也用 User.ID
替代。这样就更有意义了。
但是这样仍然还有问题。 对 profile
还有 isRegisterted
来说仍然还有可能会有问题。要知道,如果用户已经注册,就一定会有用户资料。反之,如果未注册,就肯定没有用户资料。但是从这个类的声明讲,这并没有体现出来这个逻辑。我们使用枚举和模式匹配来做这件事情:
|
|
现在我们就能确定只有当用户已注册的时候才会有 Profile
了。
是否使用枚举,完全取决于 App 的上下文。
如果我们有一个新属性 isEmailVerified
。用来表示 email 是否在服务端做过验证。这就不需要使用枚举,因为是否验证跟这个值是否存在没有关系。
JSON & Codable
首先我们需要知道 JSON 并不是类型安全的。他只支持基本数据类型,数组还有字典。
JSON 包含了很多的上下文,因为在 JSON 文件中, email 和 phoneNumber 都是字符串。我们应该知道如何去处理它。
下面是一个 User 的 JSON 示例
|
|
首先我们来处理 PhoneNumber
和 Email
。 Codable 有一个方法 singleValueContainer
用来表示 JOSN 的这部分数据就是它本身,我们并不需要为了它而做更多的事情。用这个方法可以讲 JSON 文件中的字符串直接转换成 Model。
|
|
我们不需要为 Profile 实现 Codable 的方法,因为它所包含的所有类型都是 Codable 的,编译器会自动帮我们生成这部分代码。我们需要做的只是修改 CodingKey
因为 JSON 中的 key 跟我们需要的 key 不一样。
|
|
但是对于 User,我们还有一些很复杂的事情要做,因为在 JONS 中没有 status 这个东西。在这部分,代码就有点丑了。
|
|
现在,所有的模型都是类型安全的了,不需要知道什么额外的信息,所有的信息代码都能告诉我们了。
现在仍然能够像之前一样有 isRegistered
和 profile
两个属性。这两个信息我们并不需要存起来,只需要看看 User
的实现,我们很容易就能够实现:
|
|
用户信息只是建立模型的一个非常基本的例子,这样来做,我们就可以省去将来可能会犯下的错误。