通过 Scala 可以实现更好的社交网络(4)

来源:developerWorks 中国 作者:Ted Neward
  

其中最强大的一处是,它可以针对 Twitter 支持的其他任何格式进行扩展 — fromXml 方法可以在分解节点之前检查它是否保存了 XML、RSS 或 Atom 类型的内容,或者 Status 可以包含 fromXml、fromRss、fromAtom 和 fromJson 方法。实际上,后一种方法是我的优先选择,因为它会平等对待基于 XML 的格式和 JSON(基于文本)格式。

好奇和细心的读者会注意到在 Status 及其内嵌 User 的 fromXml 方法中,我使用的是 XPath 样式的分解方法,而不是之前建议的遍历内嵌元素的方法。现在,XPath 样式的方法看上去更易于阅读,但幸运的是,我后来改变了注意,良好的封装仍然是我的朋友 — 我可以在随后修改它,Scitter 外部的任何人都不会知道。

注意 Status 内部的两个成员如何使用 Option[T] 类型;这是因为这些元素通常排除在 Status 消息外部,并且虽然元素本身会出现,但它们显示为空(类似于 <in_reply_to_user_id></in_reply_to_user_id>)。这正是 Option[T] 的作用所在。当元素为空时,它们将使用 “None” 值。(这表示考虑到基于 Java 的兼容性,访问它们会更加困难,但惟一可行方法是对最终生成的 Option 实例调用 get(),这不太复杂并且能很好地解决 “非 null 即 0” 问题)。

现在已经可以轻而易举地使用公共时间轴:


清单 14. 分解公共时间轴
				
    @Test def simplePublicFeedPullAndDeserialize =
    {
      val publicFeedURL = "http://twitter.com/statuses/public_timeline.xml"
      
      // HttpClient API 101
      val client = new HttpClient()
      val method = new GetMethod(publicFeedURL)
      method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
        new DefaultHttpMethodRetryHandler(3, false))
      val statusCode = client.executeMethod(method)
      val responseBody = new String(method.getResponseBody())
      
      val responseXML = scala.xml.XML.loadString(responseBody)
      val statuses = responseXML \\ "status"

      for (n <- statuses.elements)
      {
        val s = Status.fromXml(n)
        System.out.println("\t'@" + s.user.screenName + "' wrote " + s.text)
      }
    }

显然,这看上去更加简洁,并且易于使用。

将所有这些结合到 Scitter 单一实例中相当简单,仅涉及执行查询、解析各个 Status 元素以及将它们添加到 List[Status] 实例中,如清单 15 所示:


清单 15. Scitter.publicTimeline
				
package com.tedneward.scitter
{
  import org.apache.commons.httpclient._, auth._, methods._, params._
  import scala.xml._

  object Scitter
  {
    // ...
  
    /**
     * Query the public timeline for the most recent statuses.
     *
     * Twitter docs say:
     * public_timeline
     * Returns the 20 most recent statuses from non-protected users who have set
     * a custom user icon.  Does not require authentication.  Note that the
     * public timeline is cached for 60 seconds so requesting it more often than
     * that is a waste of resources.
     * URL: http://twitter.com/statuses/public_timeline.format
     * Formats: xml, json, rss, atom
     * Method(s): GET
     * API limit: Not applicable
     * Returns: list of status elements     
     */
    def publicTimeline : List[Status] =
    {
      import scala.collection.mutable.ListBuffer
    
      val client = new HttpClient()

      val method =
          new GetMethod("http://twitter.com/statuses/public_timeline.xml")

      method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
        new DefaultHttpMethodRetryHandler(3, false))

      client.executeMethod(method)
      
      val statusLine = method.getStatusLine()
      if (statusLine.getStatusCode() == 200)
      {
        val responseXML =
          XML.loadString(method.getResponseBodyAsString())

        val statusListBuffer = new ListBuffer[Status]

        for (n <- (responseXML \\ "status").elements)
          statusListBuffer += (Status.fromXml(n))
        
        statusListBuffer.toList
      }
      else
      {
        Nil
      }
    }
  }
}

在实现功能全面的 Twiter 客户机之前,我们显然还有很长的路要走。但到目前为止,我们已经实现基本的行为。

结束语

构建 Scitter 库的工作进展顺利;目前,Scitter 测试实现相对比较简单,与产生 Scitter API 的探索测试相比时尤为如此。外部用户不需要担心 Twitter API 或者它的各种格式的复杂性,虽然目前测试 Scitter 库有点困难(对单元测试而言,依赖网络并不是个好方法),但我们会及时解决此问题。

注意,我故意在 Twitter API 中维持了面向对象的感觉,秉承了 Scala 的精神 — 因为 Scala 支持大量功能特性并不表示我们要放弃 Java 结构采用的对象设计方法。我们将接受有用的功能特性,同时仍然保留适用的 “旧方法”。

这并不是说我们在此处提供的设计是解决问题最好的方法,只能说这是我们决定采用的设计方法;并且,因为我是本文的作者,所以我采用的是自己的方式。如果不喜欢,您可以编写自己的库和文章(并将 URL 发送给我,我会在未来的文章中向您发起挑战)。事实上,在未来的文章中,我会将所有这些封装在一个 Scala “sbaz” 包中,并上传到网上供大家下载。

现在,我们又要暂时说再见了。下个月,我将在 Scitter 库中添加更多有趣的特性,并开始考虑如何简化它的测试和使用。(责任编辑:A6)


时间:2009-08-20 16:49 来源:developerWorks 中国 作者:Ted Neward 原文链接

好文,顶一下
(1)
100%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量