在Swift中进行功能性思考
如上图所示,从服务器解析JSON响应需要进行两次转换:
- 从原始HTTP响应转换为数据的JSON表示形式。
- 从JSON表示形式到模型。
在第一步和第二步之间,我们将使用数据的JSON表示形式。 这种JSON表示形式使我们能够对服务器响应进行增量转换。 通过在模型之前解析为中间JSON表示,我们的代码变得更加可组合。
JSON的表示方式对于理解本文中的功能编程概念并不是必不可少的。 但是,为了完整起见,让我们现在对其进行定义。
如果阅读JSONSerialization
的文档,您会注意到有效JSON的规则之一是:
所有对象都是
NSString
,NSNumber
,NSArray
,NSDictionary
或NSNull
。
由于案例数量有限,因此使用枚举似乎是一个好情况。
定义了JSONObject
类型后,我们现在知道第一次转换之前,第一次与第二次转换之间以及第二次转换之后的数据类型。 它们是Data
-> JSONObject
> Model
(我们稍后会定义模型。)
功能方法
有一个核心概念将通过此练习来推动我们的思考过程。 我们将过程中的每个步骤都视为独立于函数外部变量的转换 。
让我们看一下如何将这种思考过程应用于上面概述的两个转换。
转换1:将数据转换为JSON对象
我们将从定义一个新类型Deserialize
开始,该类型将Data
转换为JSONObject
。 在函数式编程中,类型由其方法签名定义。 要使用此类型,我们编写了一个函数,该函数返回新的Deserialize
类型。
我想强调关于JSON()
函数的几件事。
- 该函数返回一个闭包。 以面向对象的思维方式看待这个问题可能看起来很奇怪,但这是标准的函数式编程。
- 请注意,从
JSON()
返回的闭包完全独立于该函数外部可能存在的任何状态。 这与我们非常依赖状态的面向对象编程形成了鲜明的对比。
转换2:将JSON对象转换为模型
正如我们在第一个转换中所做的那样,让我们定义一个新类型,该类型接受一个JSONObject
作为参数并返回类型T
的模型:
typealias Decode =(JSONObject?)->(T?)
注意:我们使 Decode
函数通用,因此我们可以解码各种模型。
由于我们尚未定义任何模型,因此这是我们开始从JSON创建模型所需的所有样板代码! 下一步是定义我们的模型以及可以解析JSON的函数。
全部放在一起
让我们导出一个简单的示例,以显示与该代码交互的外观。 对于此示例,我们将期望服务器向我们发送有关用户的信息。 我们将定义User
模型,并编写一个函数decodeUser()
,以执行从JSONObject
到User
的转换。
正如他们所说的那样,证明就在布丁里,所以让我们看看我们的功能代码是什么样的:
让userJSON:[String:Any] = [
“ FirstName”:“ Tyler”,
“ AvatarURL”:“ https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/William_Howard_Taft_1909b.jpg/1200px-William_Howard_Taft_1909b.jpg”
]
让userData =尝试JSONSerialization.data(withJSONObject:userJSON,选项:[])
让用户= encodeUser()(JSON()(userData))
print(“ \(user!.firstName)”)
与面向对象方法进行比较和对比
如果您有兴趣将面向对象的方法与功能方法进行比较,可以在这里找到面向对象的方法,也可以在此处找到功能的方法。
概述了这两种不同的方法后,让我们探讨功能方法的优缺点。
优点
- 我们的功能代码是完全不变的 -我们不需要在函数中维护任何引用或状态。 这使我们的代码更容易推理。
- 功能代码是纯净的 -给定相同的输入,每次都会产生相同的输出。
- 纯函数使我们的代码更易于测试 -如果给定输入期望相同的输出,则可以概述所有边缘情况并确保我们的代码经过了全面测试。
- Swift鼓励使用函数式编程 -语言中已经内置了许多函数式编程概念。 诸如optionals,
map
和reduce
类的东西都是函数概念,那么为什么不学习函数式编程呢? - 它更具可组合性 -通过将逻辑代码分成单独的函数,我们可以开始将多个函数链接在一起,以达到程序所需的特定粒度级别。
- 更少的代码行 —我们针对此问题的实用方法大约是50行代码。 同时,面向对象的方法大约需要80行代码。
缺点
- 从面向对象的角度看时,代码很难阅读-功能性编程概念和构造最初可能很难使您的头脑发head。
- 语法看起来很奇怪 -在函数式编程中,如果使用动词,则命名更有意义。 如果您来自面向对象的背景(通常使用名词来命名),这是违反直觉的。 例如:
//使用动词
让用户= encodeUser()(JSON()(userData))
//与使用名词
让用户= userDecoder()(JSONDecoder()(userData))
- 语法看起来很奇怪,第二部分 -认真地说,用括号括起来的这一切是什么?
其他资源
上面提供了带有上述代码的Swift游乐场。
如果您想了解有关函数式编程的更多信息,这里有一些我发现特别有趣/有用的资源。
– https://purelyfunctional.tv
– https://fsharpforfunandprofit.com/video/
– https://www.destroyallsoftware.com/talks/boundaries
– http://2017.funswiftconf.com
– http://λπω.com
– http://learnyouahaskell.com