在Swift中进行功能性思考

如上图所示,从服务器解析JSON响应需要进行两次转换:

  1. 从原始HTTP响应转换为数据的JSON表示形式。
  2. 从JSON表示形式到模型。

在第一步和第二步之间,我们将使用数据的JSON表示形式。 这种JSON表示形式使我们能够对服务器响应进行增量转换。 通过在模型之前解析为中间JSON表示,我们的代码变得更加可组合。

JSON的表示方式对于理解本文中的功能编程概念并不是必不可少的。 但是,为了完整起见,让我们现在对其进行定义。

如果阅读JSONSerialization的文档,您会注意到有效JSON的规则之一是:

所有对象都是NSStringNSNumberNSArrayNSDictionaryNSNull

由于案例数量有限,因此使用枚举似乎是一个好情况。

定义了JSONObject类型后,我们现在知道第一次转换之前,第一次与第二次转换之间以及第二次转换之后的数据类型。 它们是Data -> JSONObject > Model (我们稍后会定义模型。)

功能方法

有一个核心概念将通过此练习来推动我们的思考过程。 我们将过程中的每个步骤都视为独立于函数外部变量的转换

让我们看一下如何将这种思考过程应用于上面概述的两个转换。

转换1:将数据转换为JSON对象

我们将从定义一个新类型Deserialize开始,该类型将Data转换为JSONObject 。 在函数式编程中,类型由其方法签名定义。 要使用此类型,我们编写了一个函数,该函数返回新的Deserialize类型。

我想强调关于JSON()函数的几件事。

  1. 该函数返回一个闭包。 以面向对象的思维方式看待这个问题可能看起来很奇怪,但这是标准的函数式编程。
  2. 请注意,从JSON()返回的闭包完全独立于该函数外部可能存在的任何状态。 这与我们非常依赖状态的面向对象编程形成了鲜明的对比。

转换2:将JSON对象转换为模型

正如我们在第一个转换中所做的那样,让我们​​定义一个新类型,该类型接受一个JSONObject作为参数并返回类型T的模型:

  typealias Decode  =(JSONObject?)->(T?) 

注意:我们使 Decode 函数通用,因此我们可以解码各种模型。

由于我们尚未定义任何模型,因此这是我们开始从JSON创建模型所需的所有样板代码! 下一步是定义我们的模型以及可以解析JSON的函数。

全部放在一起

让我们导出一个简单的示例,以显示与该代码交互的外观。 对于此示例,我们将期望服务器向我们发送有关用户的信息。 我们将定义User模型,并编写一个函数decodeUser() ,以执行从JSONObjectUser的转换。

正如他们所说的那样,证明就在布丁里,所以让我们看看我们的功能代码是什么样的:

 让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, mapreduce类的东西都是函数概念,那么为什么学习函数式编程呢?
  • 它更具可组合性 -通过将逻辑代码分成单独的函数,我们可以开始将多个函数链接在一起,以达到程序所需的特定粒度级别。
  • 更少的代码行 —我们针对此问题的实用方法大约是50行代码。 同时,面向对象的方法大约需要80行代码。

缺点

  • 从面向对象的角度看时,代码很难阅读-功能性编程概念和构造最初可能很难使您的头脑发h​​ead。
  • 语法看起来很奇怪 -在函数式编程中,如果使用动词,则命名更有意义。 如果您来自面向对象的背景(通常使用名词来命名),这是违反直觉的。 例如:
  //使用动词 
 让用户= 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