熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Javascript >> 正文

Java開發者的Scala指南: Scala+Twitter=Scitter

2022-06-13   來源: Javascript 

  抽象地討論 Scala 是一件有趣的事情但對於本專欄的大多數讀者而言需要通過實踐才能理解理論和應用之間的區別在本期文章中Ted Neward 將使用 Scala 為客戶構建基礎框架用於訪問流行的微型博客系統 Twitter

  Twitter 迅速占領了 Internet 市場您肯定知道這個出色的社交網絡工具允許訂閱者提供關於他們自身以及當前正在執行的任務的簡要狀態更新追隨者將接收到他們的 Twitter 提要 的更新這與博客將更新生成到博客閱讀者的提要中極為類似

  關於本系列

  Ted Neward 將深入探討 Scala 編程語言並帶領您一路隨行在本 系列 中您將學習最新的熱點以及 Scala 的語言功能Scala 代碼和 Java&#; 代碼將在必要時同時出現以方便進行比較但您會發現 Scala 中的許多內容都與 Java 沒有直接關系 — 這便是 Scala 的魄力所在!畢竟如果 Java 能夠做到為什麼還要大費周折來學習 Scala 呢?

  就其本身而言Twitter 是對社交網絡的有趣討論並且是用戶之間的新一代 高度互聯它具備您能想到的所有優點和缺點

  由於 Twitter 很早就發布了其 API因此大量 Twitter 客戶機應用程序湧入到 Internet 上由於該 API 主要建立在直觀和易於理解的基礎上因此許多開發人員都發現有必要構建一個自己的 Twitter 客戶機這與學習 Web 技術的開發人員構建自己的博客服務器極為類似

  考慮到 Scala 的功能性(這看上去能很好地協同 Twitter 的 REST 式特性)以及非常出眾的 XML 處理特性因此嘗試構建一個用於訪問 Twitter 的 Scala 客戶機庫應該是一個非常不錯的體驗

  何為 Twitter?

  在詳細討論之前我們先來看看 Twitter API

  簡單來說Twitter 是一個 微型博客 — 關於您自己的簡短個性化提要不超過 個字符任何 追隨者 都可以通過 Web 更新RSS文本消息等方式接收它們( 字符的限制完全來自文本消息它是 Twitter 的主要來源渠道並受到類似的限制)

  最具 REST 特征 是什麼意思?

  一些讀者會對我所使用的最具 REST 特征 短語感到好奇;這需要一些說明Twitter API 試圖符合 Representational State Transfer (REST) 的設計原則並且在很大程度上說它做到了該思想的創造者 Roy Fielding 可能不同意 Twitter 使用這個術語但實現來說Twitter 的方法將適合大多數人的 REST 定義我只希望避免關於 REST 定義的激烈爭論因此我使用了限定詞

  從實際的角度來說Twitter 是一個最具 REST 特征 的 API您可以使用一些種類的消息格式 — XMLATOMRSS 或 JSON — 來發送或從 Twitter 服務器接收消息不同的 URL與不同的消息和它們所需及可選的消息部分相結合可以發起不同的 API 調用例如如果您希望接收 Twitter 上所有人的所有 Tweets(Twitter 更新)的完整列表(也稱作 公共時間軸)您需要准備一個 XMLATOMRSS 或 JSON 消息將它發送給合適的 URL並采用與 Twitter 網站()上相同的格式來使用結果

  

  public_timeline

  返回設定了自定義用戶圖標的

  非保護用戶的 條最新狀態不需要身份驗證

  注意公共時間軸將緩存 秒鐘

  因此頻繁請求它不再浪費資源

  URL _timelineformat

  格式xmljsonrssatom

  方法GET

  API 限制不適用

  返回狀態元素列表

  

  從編程的角度來說這意味著我們給 Twitter 服務器發送一個簡單的 GET HTTP 請求並且我們將獲取一組封裝在 XMLRSSATOM 或 JSON 消息中的 狀態 消息Twitter 站點將 狀態 消息定義為類似清單 所示的內容

  清單 您好世界您在哪裡?

  
<feed xml:lang=enUS xmlns=>
  <title>Twitter / tedneward</title>
  <id>tag::Status</id>
  <link type=text/html rel=alternate />
  <updated>T::+:</updated>
  <subtitle>Twitter updates from Ted Neward / tedneward</subtitle>
  <entry>
<title>tedneward: @kdellison Happens to the best of us</title>
<content type=html>tedneward: @kdellison Happens to the best of us</content>
<id>tag::;/id>
<published>T::+:</published>
<updated>T::+:</updated>
<link type=text/html rel=alternate
        />
<link type=image/png rel=image
        _production/profile_images/
         /javapolis_normalpng/>
<author>
  <name>Ted Neward</name>
  <uri>;/uri>
</author>
  </entry>
</feed>

  部的話)都很直觀因此不再贅述

  由於我們可以采用三種基於 XML 的格式使用 Twitter 消息以及 Scala 具備一些非常強大的 XML 特性包括 XML 字面值和類似 XPath 的查詢語法 API因此編寫可以發送和接收 Twitter 消息的 Scala 庫只需要一些基礎的 Scala 編碼工作舉例來說通過 Scala 使用清單 消息來提取狀態更新的標題或內容可以利用 Scala 的 XML 類型和 \ 及 \\ 方法如清單 所示

  清單 您好 Ted您在哪裡?

  
<![CDATA[
package comtednewardscittertest
{
  class ScitterTest
  {
    import orgjunit_ Assert_
    
    @Test def simpleAtomParse =
    {
      val atom =
        <feed xml:lang=enUS xmlns=>
          <title>Twitter / tedneward</title>
          <id>tag::Status</id>
          <link type=text/html rel=alternate />
          <updated>T::+:</updated>
          <subtitle>Twitter updates from Ted Neward / tedneward</subtitle>
          <entry>
            <title>tedneward: @kdellison Happens to the best of us</title>
            <content type=html>tedneward: @kdellison
                                 Happens to the best of us</content>
            <id>tag::
                ;/id>
            <published>T::+:</published>
            <updated>T::+:</updated>
            <link type=text/html rel=alternate
                  />
            <link type=image/png rel=image
                  _production/profile_images/
                        /javapolis_normalpng/>
            <author>
              <name>Ted Neward</name>
              <uri>;/uri>
            </author>
          </entry>
        </feed>
    
      assertEquals(atom \\ entry \ title
tedneward: @kdellison Happens to the best of us)
    }
  }
}
]]>

  有關 Scala 的 XML 支持的更多詳細信息請參閱 Scala 和 XML(參見 參考資料)

  實際上使用原始 XML 本身並不是一個有趣的練習如果 Scala 的宗旨是讓我們的生活更加輕松那麼可以創建一個或一組專用於簡化 Scala 消息發送和接收任務的類作為其中一個目標應該能夠在 普通 Java 程序中方便地使用庫(這意味著可以方便地從任何可理解普通 Java 語義的環境中來訪問它比如說 Groovy 或 Clojure)

  API 設計

  在深入了解 Scala/Twitter 庫的 API 設計之前(根據同事 ThoughtWorker Neal Ford 的建議我將它稱作 Scitter)需要明確一些需求

  首先Scitter 顯然會對網絡訪問有一些依賴 — 並且可擴展到 Twitter 服務器 — 這會使測試變得非常困難

  其次我們需要解析(和測試)Twitter 發回的各種格式

  第三我們希望隱藏 API 內部各種格式之間的差異以便客戶機不需要擔心已記錄的 Twitter 消息格式但是可以僅使用標准類

  最後由於 Twitter 依賴 通過身份驗證的用戶 才能使用大量 API因此 Scitter 庫需要適應 驗證未驗證 API 之間的差異而不會讓事情變得過於復雜

  網絡訪問需要一些形式的 HTTP 通信以便聯系 Twitter 服務器雖然我們可以使用 Java 庫本身(特別是 URL 類及其同胞)但由於 Twitter API 需要大量請求和響應主體連接因此可以更加輕松地使用不同的 HTTP API特別是 Apache Commons HttpClient 庫為了更便於測試客戶機 API實際通信將隱藏在一些 API 內部的 Scitter 庫中以便能夠更加輕松地切換到另一個 HTTP 庫(其必要性不太容易想到)並能模擬實際網絡通信以簡化測試(其作用很容易想到)

  結果第一個測試是 Scala 化 HttpClient 調用以確保基本通信模式就位;注意由於 HttpClient 依賴另外兩個 Apache 庫(Commons Logging 和 Commons Codec)因此還需要在運行時提供這些庫;對於那些希望開發相似種類代碼的讀者確保類路徑中包括所有三個庫

  由於最易於使用的 Twitter API 是測試 API

  因此在請求格式中返回 ok並附帶 OK HTTP 狀態碼

  我們將使用它作為 Scitter 測試中的保留條款它位於 URL (其中formatxmljson;至於目前我們將選擇使用 xml)並且僅有的支持 HTTP 方法是 GETHttpClient 代碼簡明易懂如清單 所示

  清單 Twitter PING!

  
package comtednewardscittertest
  {
  class ExplorationTests
  {
  // 
  import mons_ methods_ params_ cookie_
  @Test def callTwitterTest =
  {
  val testURL = 
  // HttpClient API 
  val client = new HttpClient()
  val method = new GetMethod(testURL)
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  clientexecuteMethod(method)
  val statusLine = methodgetStatusLine()
  assertEquals(statusLinegetStatusCode() )
  assertEquals(statusLinegetReasonPhrase() OK)
  }
  }
  }

  此代碼中最重要的一部分是 HttpClient 樣板 — 感興趣的讀者應該查閱 HttpClient API 文檔了解詳細信息假設連接到公共 Internet 的網絡可用(並且 Twitter 並未修改其公共 API)那麼該測試應該能順利通過

  鑒於此我們詳細分析 Scitter 客戶機的第一部分這意味著我們需要解決一個設計問題如何構建 Scitter 客戶機來處理經過驗證和未經過驗證的調用目前我將采用典型的 Scala 方式假定驗證是 按對象 執行的因此將需要驗證的調用放在類定義中並在未驗證的調用放在對象定義中

  清單 Scittertest

  
 package comtednewardscitter
  {
  /**
  * Object for consuming nonspecific Twitter feeds such as the public timeline
  * Use this to do nonauthenticated requests of Twitter feeds
  */
  object Scitter
  {
  import mons_ methods_ params_ cookie_
  /**
  * Ping the server to see if its up and running
  *
  * Twitter docs say:
  * test
  * Returns the string ok in the requested format with a  OK HTTP status code
  * URL: 
  * Formats: xml json
  * Method(s): GET
  */
  def test : Boolean =
  {
  val client = new HttpClient()
  val method = new GetMethod()
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  clientexecuteMethod(method)
  val statusLine = methodgetStatusLine()
  statusLinegetStatusCode() == 
  }
  }
  /**
  * Class for consuming authenticated user Twitter APIs Each instance is
  * thus tied to a particular authenticated user on Twitter and will
  * behave accordingly (according to the Twitter API documentation)
  */
  class Scitter(username : String password : String)
  {
  }
  }

  目前我們將網絡抽象放在一邊 — 稍後當離線測試變得更加重要時再添加它當我們更好地理解如何使用 HttpClient 類時這還將幫助避免 過度抽象 網絡通信

  由於已經明確區分了驗證和未驗證 Twitter 客戶機因此我們將快速創建一個經過驗證的方法看上去 Twitter 提供了一個可驗證用戶登錄憑證的 API再次HttpClient 代碼將類似於之前的代碼除了將用戶名和密碼傳遞到 Twitter API 中之外

  這引出了 Twitter 如何驗證用戶的概念快速查看 Twitter API 頁面後可以發現 Twitter 使用的是一種 Stock HTTP 驗證方法這與任何經過驗證的資源在 HTTP 中的方法相同這意味著 HttpClient 代碼必須提供用戶名和密碼作為 HTTP 請求的一部分而不是作為 POST 的主體如清單 所示

  清單 您好 Twitter是我!

  
package comtednewardscittertest
  {
  class ExplorationTests
  {
  def testUser = TwitterUser
  def testPassword = TwitterPassword
  @Test def verifyCreds =
  {
  val client = new HttpClient()
  val verifyCredsURL = _credentialsxml
  val method = new GetMethod(verifyCredsURL)
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  clientgetParams()setAuthenticationPreemptive(true)
  val defaultcreds = new UsernamePasswordCredentials(testUser testPassword)
  clientgetState()setCredentials(new AuthScope( 
  AuthScopeANY_REALM) defaultcreds)
  clientexecuteMethod(method)
  val statusLine = methodgetStatusLine()
  assertEquals( statusLinegetStatusCode())
  assertEquals(OK statusLinegetReasonPhrase())
  }
  }
  }

  注意要讓此測試順利通信用戶名和密碼字段將需要輸入 Twitter 能接收的內容 — 我在開發時使用了自己的 Twitter 用戶名和密碼但顯然您需要使用自己設定的用戶名和密碼注冊新的 Twitter 帳戶相當簡單因此我假定您已經擁有一個帳戶或者知道如何注冊(很好我會等待完成此任務)

  完成後使用用戶名和密碼構造函數參數將它映射到 Scitter 類非常簡單如清單 所示

  清單 ScitterverifyCredentials

  
package comtednewardscitter
  {
  import mons_ auth_ methods_ params_
  // 
  /**
  * Class for consuming authenticated user Twitter APIs Each instance is
  * thus tied to a particular authenticated user on Twitter and will
  * behave accordingly (according to the Twitter API documentation)
  */
  class Scitter(username : String password : String)
  {
  /**
  * Verify the user credentials against Twitter
  *
  * Twitter docs say:
  * verify_credentials
  * Returns an HTTP  OK response code and a representation of the
  * requesting user if authentication was successful; returns a  status
  * code and an error message if not Use this method to test if supplied
  * user credentials are valid
  * URL: _credentialsformat
  * Formats: xml json
  * Method(s): GET
  */
  def verifyCredentials : Boolean =
  {
  val client = new HttpClient()
  val method = new GetMethod()
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  clientgetParams()setAuthenticationPreemptive(true)
  val creds = new UsernamePasswordCredentials(username password)
  clientgetState()setCredentials(
  new AuthScope(  AuthScopeANY_REALM) creds)
  clientexecuteMethod(method)
  val statusLine = methodgetStatusLine()
  statusLinegetStatusCode() == 
  }
  }
  }

  清單 中相應的 Scitter 類測試也相當簡單

  清單 測試 ScitterverifyCredentials

  
 package comtednewardscittertest
  {
  class ScitterTests
  {
  import orgjunit_ Assert_
  import comtednewardscitter_
  def testUser = TwitterUsername
  def testPassword = TwitterPassword
  // 
  @Test def verifyCreds =
  {
  val scitter = new Scitter(testUser testPassword)
  val result = scitterverifyCredentials
  assertTrue(result)
  }
  }
  }

  不算太糟庫的基本結構已經成形但顯然還有很長的路要走特別是因為目前實際上未執行任何特定於 Scala 的任務 — 在面向對象設計中庫的構建並不像練習那樣簡單因此我們開始使用一些 XML並通過更加合理的格式將它返回

  從 XML 到對象

  現在可以添加的最簡單的 API 是 public_timeline它收集 Twitter 從所有用戶處接收到的最新的 n 更新並返回它們以便於進行使用與之前討論的另外兩個 API 不同public_timeline API 返回一個響應主體(而不是僅依賴於狀態碼)因此我們需要分解生成的 XML/RSS/ATOM/然後將它們返回給 Scitter 客戶機

  現在我們編寫一個探索測試它將訪問公共提要並將結果轉儲到 stdout 以便進行分析如清單 所示

  清單 大家都在忙什麼?

  
package comtednewardscittertest
  {
  class ExplorationTests
  {
  // 
  @Test def callTwitterPublicTimeline =
  {
  val publicFeedURL = _timelinexml
  // HttpClient API 
  val client = new HttpClient()
  val method = new GetMethod(publicFeedURL)
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  clientexecuteMethod(method)
  val statusLine = methodgetStatusLine()
  assertEquals(statusLinegetStatusCode() )
  assertEquals(statusLinegetReasonPhrase() OK)
  val responseBody = methodgetResponseBodyAsString()
  Systemoutprintln(callTwitterPublicTimeline got )
  Systemoutprintln(responseBody)
  }
  }
  }

  運行後結果每次都會有所不同因為公共 Twitter 服務器上有許多用戶但通常應與清單 的 JUnit 文本文件轉儲類似

  清單 我們的 Tweets 結果

  
 <statuses type=array>
    <status>
      <created_at>Tue Mar  :: + </created_at>
      <id></id>
      <text>She really is ;/text>
      <source><a >twitterrific</a>
        </source>
      <truncated>false</truncated>
      <in_reply_to_status_id></in_reply_to_status_id>
      <in_reply_to_user_id></in_reply_to_user_id>
      <favorited>false</favorited>
      <user>
          <id></id>
          <name>Brittanie</name>
          <screen_name>brittaniemarie</screen_name>
          <description>Im a bright character I suppose</description>
          <location>Atlanta or Philly</location>
          <profile_image_url>_production/profile_images/
                             /goodish_normaljpg</profile_image_url>
          <url>;/url>
          <protected>false</protected>
          <followers_count></followers_count>
      </user>
    </status>
    <status>
      <created_at>Tue Mar  :: + </created_at>
      <id></id>
      <text>Number  of my four life principles  Life is fun and rewarding</text>
      <source>web</source>
      <truncated>false</truncated>
      <in_reply_to_status_id></in_reply_to_status_id>
      <in_reply_to_user_id></in_reply_to_user_id>
      <favorited>false</favorited>
      <user>
          <id></id>
          <name>Dale Greenwood</name>
          <screen_name>Greeendale</screen_name>
          <description>Vegetarian Eat and use only organics 
                       Love helping people become prosperous</description>
          <location>Melbourne Australia</location>
          <profile_image_url>_production/profile_images/
                             /Dock_normaljpg</profile_image_url>
          <url>;/url>
          <protected>false</protected>
          <followers_count></followers_count>
       </user>
     </status>
       (A lot more have been snipped)
</statuses>

  通過查看結果和 Twitter 文檔可以看出調用的結果是一組具備一致消息結構的簡單 狀態 消息使用 Scala 的 XML 支持分離結果相當簡單但我們會在基本測試通過後立即簡化它們如清單 所示

  清單 大家都在忙什麼?

  
 package comtednewardscittertest
  {
  class ExplorationTests
  {
  // 
  @Test def simplePublicFeedPullAndParse =
  {
  val publicFeedURL = _timelinexml
  // HttpClient API 
  val client = new HttpClient()
  val method = new GetMethod(publicFeedURL)
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  val statusCode = clientexecuteMethod(method)
  val responseBody = new String(methodgetResponseBody())
  val responseXML = scalaxmlXMLloadString(responseBody)
  val statuses = responseXML \\ status
  for (n < statuseselements)
  {
  n match
  {
  case { contents @ _*} =>
  {
  Systemoutprintln(Status: )
  contentsforeach((c) =>
  c match
  {
  case { t @ _*} =>
  Systemoutprintln(\tText:  + ttexttrim)
  case { contents @ _* } =>
  {
  contentsforeach((c) =>
  c match
  {
  case { u } =>
  Systemoutprintln(\tUser:  + utexttrim)
  case _ =>
  ()
  }
  )
  }
  case _ =>
  ()
  }
  )
  }
  case _ =>
  () // or if you prefer Systemoutprintln(Unrecognized element!)
  }
  }
  }
  }
  }

  隨著示例代碼模式的變化這並不值得推薦 — 這有點類似於 DOM依次導航到各個子元素提取文本然後導航到另一個節點我可以僅執行兩個 XPath 樣式的查詢如清單 所示

  清單 替代解析方法

  
 for (n < statuseselements)
  {
  val text = (n \\ text)text
  val screenName = (n \\ user \ screen_name)text
  }

  這顯然更加簡短但它帶來了兩個基本問題

  我們可以強制 Scala 的 XML 庫針對每個元素或子元素遍歷一次圖其速度會隨時間減慢

  我們仍然需要直接處理 XML 消息的結構這是兩個問題中最為重要的

  也就是說這種方式不具備可伸縮性 — 假設我們最終對 Twitter 狀態消息中的每個元素都感興趣我們將需要分別從各狀態中提取各個元素

  這又造成了另一個與各格式本身相關的問題記住Twitter 可以使用四種不同的格式並且我們不希望 Scitter 客戶機需要了解它們之間的任何差異因此 Scitter 需要一個能返回給客戶機的中間結構以便未來使用如清單 所示

  清單 Breaker您的狀態是什麼?

  
abstract class Status
  {
  val createdAt : String
  val id : Long
  val text : String
  val source : String
  val truncated : Boolean
  val inReplyToStatusId : Option[Long]
  val inReplyToUserId : Option[Long]
  val favorited : Boolean
  val user : User
  }

  這與 User 方式相類似考慮到簡潔性我就不再重復了注意User 子元素有一個有趣的問題 — 雖然存在 Twitter 用戶類型但其中內嵌了一個可選的 最新狀態狀態消息還內嵌了一個用戶對於這種情況為了幫助避免一些潛在的遞歸問題我選擇創建一個嵌入在 Status 內部的 User 類型以反映所出現的 User 數據;反之亦然Status 也可以嵌入在 User 中這樣可以明確避免該問題(至少在沒發現問題之前這種方法是有效的)

  現在創建了表示 Twitter 消息的對象類型之後我們可以遵循 XML 反序列化的公共 Scala 模式創建相應的對象定義其中包含一個 fromXml 方法用於將 XML 節點分離到對象實例中如清單 所示

  清單 分解 XML

  
 /**
  * Object wrapper for transforming (format) into Status instances
  */
  object Status
  {
  def fromXml(node : scalaxmlNode) : Status =
  {
  new Status {
  val createdAt = (node \ created_at)text
  val id = (node \ id)texttoLong
  val text = (node \ text)text
  val source = (node \ source)text
  val truncated = (node \ truncated)texttoBoolean
  val inReplyToStatusId =
  if ((node \ in_reply_to_status_id)text != )
  Some((node \in_reply_to_status_id)texttoLong)
  else
  None
  val inReplyToUserId =
  if ((node \ in_reply_to_user_id)text != )
  Some((node \in_reply_to_user_id)texttoLong)
  else
  None
  val favorited = (node \ favorited)texttoBoolean
  val user = UserfromXml((node \ user)())
  }
  }
  }

  其中最強大的一處是它可以針對 Twitter 支持的其他任何格式進行擴展 — fromXml 方法可以在分解節點之前檢查它是否保存了 XMLRSS 或 Atom 類型的內容或者 Status 可以包含 fromXmlfromRssfromAtom 和 fromJson 方法實際上後一種方法是我的優先選擇因為它會平等對待基於 XML 的格式和 JSON(基於文本)格式

  好奇和細心的讀者會注意到在 Status 及其內嵌 User 的 fromXml 方法中我使用的是 XPath 樣式的分解方法而不是之前建議的遍歷內嵌元素的方法現在XPath 樣式的方法看上去更易於閱讀但幸運的是我後來改變了注意良好的封裝仍然是我的朋友 — 我可以在隨後修改它Scitter 外部的任何人都不會知道

  注意 Status 內部的兩個成員如何使用 Option[T] 類型;這是因為這些元素通常排除在 Status 消息外部並且雖然元素本身會出現但它們顯示為空(類似於 )這正是 Option[T] 的作用所在當元素為空時它們將使用 None(這表示考慮到基於 Java 的兼容性訪問它們會更加困難但惟一可行方法是對最終生成的 Option 實例調用 get()這不太復雜並且能很好地解決 非 null 即 問題)

  現在已經可以輕而易舉地使用公共時間軸

  清單 分解公共時間軸

  
@Test def simplePublicFeedPullAndDeserialize =
  {
  val publicFeedURL = _timelinexml
  // HttpClient API 
  val client = new HttpClient()
  val method = new GetMethod(publicFeedURL)
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  val statusCode = clientexecuteMethod(method)
  val responseBody = new String(methodgetResponseBody())
  val responseXML = scalaxmlXMLloadString(responseBody)
  val statuses = responseXML \\ status
  for (n < statuseselements)
  {
  val s = StatusfromXml(n)
  Systemoutprintln(\t@ + suserscreenName +  wrote  + stext)
  }
  }

  顯然這看上去更加簡潔並且易於使用

  將所有這些結合到 Scitter 單一實例中相當簡單僅涉及執行查詢解析各個 Status 元素以及將它們添加到 List[Status] 實例中如清單 所示

  清單 ScitterpublicTimeline

  
package comtednewardscitter
  {
  import mons_ auth_ methods_ params_
  import scalaxml_
  object Scitter
  {
  // 
  /**
  * Query the public timeline for the most recent statuses
  *
  * Twitter docs say:
  * public_timeline
  * Returns the  most recent statuses from nonprotected users who have set
  * a custom user icon Does not require authentication Note that the
  * public timeline is cached for  seconds so requesting it more often than
  * that is a waste of resources
  * URL: _timelineformat
  * Formats: xml json rss atom
  * Method(s): GET
  * API limit: Not applicable
  * Returns: list of status elements
  */
  def publicTimeline : List[Status] =
  {
  import llectionmutableListBuffer
  val client = new HttpClient()
  val method =
  new GetMethod(_timelinexml)
  methodgetParams()setParameter(HttpMethodParamsRETRY_HANDLER
  new DefaultHttpMethodRetryHandler( false))
  clientexecuteMethod(method)
  val statusLine = methodgetStatusLine()
  if (statusLinegetStatusCode() == )
  {
  val responseXML =
  XMLloadString(methodgetResponseBodyAsString())
  val statusListBuffer = new ListBuffer[Status]
  for (n < (responseXML \\ status)elements)
  statusListBuffer += (StatusfromXml(n))
  statusListBuffertoList
  }
  else
  {
  Nil
  }
  }
  }
  }

  在實現功能全面的 Twiter 客戶機之前我們顯然還有很長的路要走但到目前為止我們已經實現基本的行為

  結束語

  構建 Scitter 庫的工作進展順利;目前Scitter 測試實現相對比較簡單與產生 Scitter API 的探索測試相比時尤為如此外部用戶不需要擔心 Twitter API 或者它的各種格式的復雜性雖然目前測試 Scitter 庫有點困難(對單元測試而言依賴網絡並不是個好方法)但我們會及時解決此問題

  注意我故意在 Twitter API 中維持了面向對象的感覺秉承了 Scala 的精神 — 因為 Scala 支持大量功能特性並不表示我們要放棄 Java 結構采用的對象設計方法我們將接受有用的功能特性同時仍然保留適用的 舊方法

  這並不是說我們在此處提供的設計是解決問題最好的方法只能說這是我們決定采用的設計方法;並且因為我是本文的作者所以我采用的是自己的方式如果不喜歡您可以編寫自己的庫和文章(並將 URL 發送給我我會在未來的文章中向您發起挑戰)事實上在未來的文章中我會將所有這些封裝在一個 Scala sbaz 包中並上傳到網上供大家下載

  現在我們又要暫時說再見了下個月我將在 Scitter 庫中添加更多有趣的特性並開始考慮如何簡化它的測試和使用


From:http://tw.wingwit.com/Article/program/Java/Javascript/201311/25296.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.