设为首页 加入收藏

TOP

restapi(6)- do it the functional way, 重温函数式编程(一)
2019-09-03 03:40:13 】 浏览:48
Tags:restapi the functional way 重温 函数 编程

  再次看了看上篇博客的源代码,发现连自己都看不懂了。想是为了赶时间交货不知不觉又回到OOP行令模式了,看看下面这段代码:

       (post &  parameters('pid,'desc.?,'width.as[Int].?,'heigth.as[Int].?)) { (pid, optDesc, optWid, optHgh) => val futCount: Future[Int] = repository.count(pid).value.value.runToFuture.map { eoi => eoi match { case Right(oi) => oi match { case Some(i) => i case None => -1 } case Left(err) => -1 } } val count: Int = Await.result(futCount, 2 seconds) var doc = Document( "pid" -> pid, "seqno" -> count ) if (optDesc != None) doc = doc + ("desc" -> optDesc.get) if (optWid != None) doc = doc + ("desc" -> optWid.get) if (optHgh != None) doc = doc + ("desc" -> optHgh.get) withoutSizeLimit { decodeRequest { extractDataBytes { bytes => val fut = bytes.runFold(ByteString()) { case (hd, bs) => hd ++ bs } onComplete(fut) { case Success(b) => doc = doc + ("pic" -> b.toArray) val futmsg: Future[String] = repository.insert(doc).value.value.runToFuture.map { eoc => eoc match { case Right(oc) => oc match { case Some(c) => count.toString // c.toString()
                            case None => "insert may not complete!" } case Left(err) => err.getMessage } } complete(futmsg) case Failure(err) => complete(err) } } } }

有人能从这段代码里理解它的功能吗?本来作者的目的很简单:前端通过httprequest提交了一张图片及产品编号pid、系统读取MongoDB查找相同pid的数量count,然后将图片和描述包括count写入数据库并在reponse里返回count。把一个简单功能的实现搞的这么复杂都是我的错,可能受OOP荼毒太深。这次希望静下心来用函数式编程模式把这段代码从新实现一次,示范一下函数式编程的代码精炼和高雅特点。首先介绍一下DBResult[A]这个类型:这是一个Monad,为了应付Future[Either[Option[R]]]这样的类型而设计的,是一个表现数据库操作比较全面的类型,但同时它又是造成上面这段代码混乱的元凶。现在我们可以用隐式转换implicit conversion方式进行代码简化重用:

  import monix.execution.Scheduler.Implicits.global
  implicit class DBResultToFuture(dbr: DBOResult[_]){ def toFuture[R] = { dbr.value.value.runToFuture.map { eor => eor match { case Right(or) => or match { case Some(r) => r.asInstanceOf[R] case None => throw new RuntimeException("Operation produced None result!") } case Left(err) => throw new RuntimeException(err) } } } }

 用这个隐式转换类型为任何DBOResult[R]增加一个函数toFuture[R]。现在整个futCount算式可以简化成下面这样:

          val futCount: Future[Int] = repository.count(pid).value.value.runToFuture.map { eoi => eoi match { case Right(oi) => oi match { case Some(i) => i case None => -1 } case Left(err) => -1 } } futCount:Future[Int]=repository.count(pid).toFuture

真正的简单易明。

不知怎么搞的,我尽然在这段代码中间使用了Await.result。从OOP角度分析这很容易理解,下一段程序需要上一段程序的结果来继续运行。在上面的例子里我们需要先获取count然后把count塞进Document再把Document存入数据库。逻辑思路上没问题,不过这样的做法是典型的行令式编程模式。在函数式编程模式里,阶段性的运算结果是在包嵌在Monad中的。Monad本身只是一个运算计划,只有真正运算时才能获取结果。Monad本身是函数组件,可以实现多个Monad的函数组合。在这里可以形象的把Monad函数组合描述为数据库操作步骤:先count、再insert,这两个步骤产生的结果还是留在Monad里的,直到所谓的世界末日,即实际运算完成后才取出,所以Monad是一种典型的程序运算流程管道。假如我们再把insert这段程序写成addPicture(...): DBOResult[_], 如下:

 def addPicuture(pid: String,seqno: Int, optDesc: Option[String] ,optWid:Option[Int],optHgh:Option[Int], bytes: Array[Byte]):DBOResult[Completed] ={ var doc = Document( "pid" -> pid, "seqno" -> seqno, "pic" -> bytes ) if (optDesc != None) doc = doc + ("desc" -> optDesc.get) if (optWid != None) doc = doc + ("desc" -> optWid.get) if (optHgh != None) doc = doc + ("desc" -> optHgh.get) repository.insert(doc) }

好了,现在整篇代码变成了下面这样:

首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scala 函数基础入门 下一篇Scala2.12 从入门到精通实战高端..