新聞中心
現(xiàn)在對于Scala而言,Twitter是一個很好的學習對象。在之前一篇文章中,Ted已經(jīng)談到了 Twitter,這個微博客站點目前正引起社會性網(wǎng)絡(luò)的極大興趣,我們還談到它的基于 XML-/REST 的 API 如何使它成為開發(fā)人員進行研究和探索的一個有趣平臺。為此,我們首先充實了“Scitter” 的基本結(jié)構(gòu),Scitter 是用于訪問 Twitter 的一個 Scala 庫。更多來自IBMDW的Scala文章,請參考從Java走進Scala(Scala經(jīng)典讀物)。

濟南網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),濟南網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為濟南上千余家提供企業(yè)網(wǎng)站建設(shè)服務。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請找那個售后服務好的濟南做網(wǎng)站的公司定做!
我們對于 Scitter 有幾個目標:
- 簡化 Twitter 訪問,比過去打開 HTTP 連接然后 “手動” 執(zhí)行操作更容易。
- 可以從 Java 客戶機輕松訪問它。
- 輕松模擬以便進行測試。
在這一期,我們不必完成整個 Twitter API,但是我們將完成一些核心部分,目的是讓這個庫達到公共源代碼控制庫的程度,便于其他人來完成這項工作。
到目前為止:Scitter 0.1
首先我們簡單回顧一下到目前為止我們所處的階段:
清單 1. Scitter v0.1
package com.tedneward.scitter
{
import org.apache.commons.httpclient._, auth._, methods._, params._
import scala.xml._
/**
* Status message type. This will typically be the most common message type
* sent back from Twitter (usually in some kind of collection form). Note
* that all optional elements in the Status type are represented by the
* Scala Option[T] type, since that's what it's there for.
*/
abstract class Status
{
/**
* Nested User type. This could be combined with the top-level User type,
* if we decide later that it's OK for this to have a boatload of optional
* elements, including the most-recently-posted status update (which is a
* tad circular).
*/
abstract class User
{
val id : Long
val name : String
val screenName : String
val description : String
val location : String
val profileImageUrl : String
val url : String
val protectedUpdates : Boolean
val followersCount : Int
}
/**
* Object wrapper for transforming (format) into User instances.
*/
object User
{
/*
def fromAtom(node : Node) : Status =
{
}
*/
/*
def fromRss(node : Node) : Status =
{
}
*/
def fromXml(node : Node) : User =
{
new User {
val id = (node \ "id").text.toLong
val name = (node \ "name").text
val screenName = (node \ "screen_name").text
val description = (node \ "description").text
val location = (node \ "location").text
val profileImageUrl = (node \ "profile_image_url").text
val url = (node \ "url").text
val protectedUpdates = (node \ "protected").text.toBoolean
val followersCount = (node \ "followers_count").text.toInt
}
}
}
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
}
/**
* Object wrapper for transforming (format) into Status instances.
*/
object Status
{
/*
def fromAtom(node : Node) : Status =
{
}
*/
/*
def fromRss(node : Node) : Status =
{
}
*/
def fromXml(node : Node) : Status =
{
new Status {
val createdAt = (node \ "created_at").text
val id = (node \ "id").text.toLong
val text = (node \ "text").text
val source = (node \ "source").text
val truncated = (node \ "truncated").text.toBoolean
val inReplyToStatusId =
if ((node \ "in_reply_to_status_id").text != "")
Some((node \"in_reply_to_status_id").text.toLong)
else
None
val inReplyToUserId =
if ((node \ "in_reply_to_user_id").text != "")
Some((node \"in_reply_to_user_id").text.toLong)
else
None
val favorited = (node \ "favorited").text.toBoolean
val user = User.fromXml((node \ "user")(0))
}
}
}
/**
* Object for consuming "non-specific" Twitter feeds, such as the public timeline.
* Use this to do non-authenticated requests of Twitter feeds.
*/
object Scitter
{
/**
* Ping the server to see if it's up and running.
*
* Twitter docs say:
* test
* Returns the string "ok" in the requested format with a 200 OK HTTP status code.
* URL: http://twitter.com/help/test.format
* Formats: xml, json
* Method(s): GET
*/
def test : Boolean =
{
val client = new HttpClient()
val method = new GetMethod("http://twitter.com/help/test.xml")
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false))
client.executeMethod(method)
val statusLine = method.getStatusLine()
statusLine.getStatusCode() == 200
}
/**
* 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
}
}
}
/**
* 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 200 OK response code and a representation of the
* requesting user if authentication was successful; returns a 401 status
* code and an error message if not. Use this method to test if supplied
* user credentials are valid.
* URL: http://twitter.com/account/verify_credentials.format
* Formats: xml, json
* Method(s): GET
*/
def verifyCredentials : Boolean =
{
val client = new HttpClient()
val method = new GetMethod("http://twitter.com/help/test.xml")
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false))
client.getParams().setAuthenticationPreemptive(true)
val creds = new UsernamePasswordCredentials(username, password)
client.getState().setCredentials(
new AuthScope("twitter.com", 80, AuthScope.ANY_REALM), creds)
client.executeMethod(method)
val statusLine = method.getStatusLine()
statusLine.getStatusCode() == 200
}
}
}
|
代碼有點長,但是很容易分為幾個基本部分:
- case 類
User和Status,表示 Twitter 在對 API 調(diào)用的響應中發(fā)回給客戶機的基本類型,包括用于構(gòu)造或提取 XML 的一些方法。 - 一個 Scitter 獨立對象,處理那些不需要對用戶進行驗證的操作。
- 一個 Scitter 實例(用 username 和 password 參數(shù)化),用于那些需要對用戶執(zhí)行驗證的操作。
到目前為止,對于這兩種 Scitter 類型,我們只談到了測試、verifyCredentials 和 public_timeline API。雖然這些有助于確定 HTTP 訪問的基礎(chǔ)(使用 Apache HttpClient 庫)可以工作,并且我們將 XML 響應轉(zhuǎn)換成 Status 對象的基本方式也是可行的,但是現(xiàn)在我們甚至不能進行基本的 “我的朋友在說什么” 的公共時間線查詢,也沒有采取過基本的措施來防止代碼庫中出現(xiàn) “重復” 問題,更不用說尋找一些方法來模擬用于測試的網(wǎng)絡(luò)訪問代碼。
顯然,在這一期我們有很多事情要做。
#p#
連接
對于代碼,***件讓我煩惱的事就是,我在 Scitter 對象和類的每個方法中都重復這樣的操作序列:創(chuàng)建 HttpClient 實例,對它進行初始化,用必要的驗證參數(shù)對它進行參數(shù)化,等等。當它們只有 3 個方法時,可以進行管理,但是顯然不易于伸縮,而且以后還會增加很多方法。此外,以后重新在那些方法中引入模擬和/或本地/離線測試功能將十分困難。所以我們要解決這個問題。
實際上,我們這里介紹的并不是 Scala 本身,而是不要重復自己(Don't-Repeat-Yourself)的思想。因此,我將從基本的面向?qū)ο蠓椒ㄩ_始:創(chuàng)建一個 helper 方法,用于做實際工作:
清單 2. 對代碼庫執(zhí)行 DRY 原則
package com.tedneward.scitter
{
// ...
object Scitter
{
// ...
private[scitter] def exec ute(url : String) =
{
val client = new HttpClient()
val method = new GetMethod(url)
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false))
client.executeMethod(method)
(method.getStatusLine().getStatusCode(), method.getResponseBodyAsString())
}
}
}
|
注意兩點:首先,我從 execute() 方法返回一個元組,其中包含狀態(tài)碼和響應主體。這正是讓元組成為語言中固有的一部分的一個強大之處,因為實際上很容易從一個方法調(diào)用返回多個返回值。當然,在 Java 代碼中,也可以通過創(chuàng)建包含元組元素的***或嵌套類來實現(xiàn)這一點,但是這需要一整套專用于這一個方法的代碼。此外,本來也可以返回一個包含 String 鍵和 Object 值的 Map,但是那樣就在很大程度上喪失了類型安全性。元組并不是一個非常具有變革性的特性,它只不過是又一個使 Scala 成為強大語言的優(yōu)秀特性。
由于使用元組,我需要使用 Scala 的另一個特色語法將兩個結(jié)果都捕捉到本地變量中,就像下面這個重寫后的 Scitter.test 那樣:
清單 3. 這符合 DRY 原則嗎?
package com.tedneward.scitter
{
// ...
object Scitter
{
/**
* Ping the server to see if it's up and running.
*
* Twitter docs say:
* test
* Returns the string "ok" in the requested format with a 200 OK HTTP status code.
* URL: http://twitter.com/help/test.format
* Formats: xml, json
* Method(s): GET
*/
def test : Boolean =
{
val (statusCode, statusBody) =
execute("http://twitter.com/statuses/public_timeline.xml")
statusCode == 200
}
}
}
|
實際上,我可以輕松地將 statusBody 全部去掉,并用 _ 替代它,因為我沒有用過第二個參數(shù)(test 沒有返回 statusBody),但是對于其他調(diào)用將需要這個 statusBody,所以出于演示的目的,我保留了該參數(shù)。
注意,execute() 沒有泄露任何與實際 HTTP 通信相關(guān)的細節(jié) — 這是 Encapsulation 101。這樣便于以后用其他實現(xiàn)替換 execute()(以后的確要這么做),或者便于通過重用 HttpClient 對象來優(yōu)化代碼,而不是每次重新實例化新的對象。
接下來,注意到 execute() 方法在 Scitter 對象上嗎?這意味著我將可以從不同的 Scitter 實例中使用它(至少現(xiàn)在可以這樣做,如果以后在 execute() 內(nèi)部執(zhí)行的操作不允許這樣做,則另當別論)— 這就是我將 execute() 標記為 private[scitter] 的原因,這意味著 com.tedneward.scitter 包中的所有內(nèi)容都可以看到它。
(順便說一句,如果還沒有運行測試的話,那么請運行測試,確保一切運行良好。我將假設(shè)我們在討論代碼時您會運行測試,所以如果我忘了提醒您,并不意味著您也忘記這么做。)
順便說一句,對于經(jīng)過驗證的訪問,為了支持 Scitter 類,需要一個用戶名和密碼,所以我將創(chuàng)建一個重載的 execute() 方法,該方法新增兩個 String 參數(shù):
清單 4. 更加 DRY 化的版本
package com.tedneward.scitter
{
// ...
object Scitter
{
// ...
private[scitter] def execute(url : String, username : String, password : String) =
{
val client = new HttpClient()
val method = new GetMethod(url)
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false))
client.getParams().setAuthenticationPreemptive(true)
client.getState().setCredentials(
new AuthScope("twitter.com", 80, AuthScope.ANY_REALM),
new UsernamePasswordCredentials(username, password))
client.executeMethod(method)
(method.getStatusLine().getStatusCode(), method.getResponseBodyAsString())
}
}
}
|
實際上,除了驗證部分,這兩個 execute() 基本上是做相同的事情,我們可以按照第二個版本完全重寫***個 execute(),但是要注意,Scala 要求顯式表明重載的 execute() 的返回類型:
清單 5. 放棄 DRY
package com.tedneward.scitter
{
// ...
object Scitter
{
// ...
private[scitter] def execute(url : String) : (Int, String) =
execute(url, "", "")
private[scitter] def execute(url : String, username : String, password : String) =
{
val client = new HttpClient()
val method = new GetMethod(url)
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false))
if ((username != "") && (password != ""))
{
client.getParams().setAuthenticationPreemptive(true)
client.getState().setCredentials(
new AuthScope("twitter.com", 80, AuthScope.ANY_REALM),
new UsernamePasswordCredentials(username, password))
}
client.executeMethod(method)
(method.getStatusLine().getStatusCode(), method.getResponseBodyAsString())
}
}
}
|
到目前為止,一切良好。我們對 Scitter 的通信部分進行了 DRY 化處理,接下來我們轉(zhuǎn)移到下一件事情:獲得朋友的 tweet 的列表。
#p#
連接(到朋友)
Twitter API 表明,friends_timeline API 調(diào)用 “返回認證用戶和該用戶的朋友發(fā)表的最近 20 條狀態(tài)”。(它還指出,對于直接從 Twitter Web 站點使用 Twitter 的用戶,“這相當于 Web 上的 ‘/home’”。)對于任何 Twitter API 來說,這是非?;镜囊?,所以讓我們將它添加到 Scitter 類中。之所以將它添加到類而不是對象中,是因為正如文檔中指出的那樣,這是以驗證用戶的身份做的事情,而我已經(jīng)決定歸入 Scitter 類,而不是 Scitter 對象。
但是,這里我們碰到一塊絆腳石:friends_timeline 調(diào)用接受一組 “可選參數(shù)”,包括 since_id、max_id、count 和 page,以控制返回的結(jié)果。這是一項比較復雜的操作,因為 Scala 不像其他語言(例如 Groovy、JRuby 或 JavaScript)那樣原生地支持 “可選參數(shù)” 的思想,但是我們首先來處理簡單的東西 — 我們來創(chuàng)建一個 friendsTimeline 方法,該方法只執(zhí)行一般的、非參數(shù)化的調(diào)用:
清單 6.“告訴我你身邊的朋友是怎樣的...”
package com.tedneward.scitter
{
class Scitter
{
def friendsTimeline : List[Status] =
{
val (statusCode, statusBody) =
Scitter.execute("http://twitter.com/statuses/friends_timeline.xml",
username, password)
if (statusCode == 200)
{
val responseXML = XML.loadString(statusBody)
val statusListBuffer = new ListBuffer[Status]
for (n <- (responseXML \\ "status").elements)
statusListBuffer += (Status.fromXml(n))
statusListBuffer.toList
}
else
{
Nil
}
}
}
}
|
到目前為止,一切良好。用于測試的相應方法看上去如下所示:
清單 7. “我能判斷您是怎樣的人 ”(Miguel de Cervantes)
package com.tedneward.scitter.test
{
class ScitterTests
{
// ...
@Test def scitterFriendsTimeline =
{
val scitter = new Scitter(testUser, testPassword)
val result = scitter.friendsTimeline
assertTrue(result.length > 0)
}
}
}
|
好極了??瓷先ゾ拖?Scitter 對象中的 publicTimeline() 方法,并且行為也幾乎完全相同。
對于我們來說,那些可選參數(shù)仍然有問題。因為 Scala 并沒有可選參數(shù)這樣的語言特性,乍一看來,惟一的選擇就是完整地創(chuàng)建重載的 friendsTimeline() 方法,讓該方法帶有一定數(shù)量的參數(shù)。
幸運的是,還有一種更好的方式,即通過一種有趣的方式將 Scala 的兩個語言特性(有一個特性我還沒有提到過) — case 類和 “重復參數(shù)” 結(jié)合起來(見清單 8):
清單 8. “我有多愛你?……”
package com.tedneward.scitter
{
// ...
abstract class OptionalParam
case class Id(id : String) extends OptionalParam
case class UserId(id : Long) extends OptionalParam
case class Since(since_id : Long) extends OptionalParam
case class Max(max_id : Long) extends OptionalParam
case class Count(count : Int) extends OptionalParam
case class Page(page : Int) extends OptionalParam
class Scitter(username : String, password : String)
{
// ...
def friendsTimeline(options : OptionalParam*) : List[Status] =
{
val optionsStr =
new StringBuffer("http://twitter.com/statuses/friends_timeline.xml?")
for (option <- options)
{
option match
{
case Since(since_id) =>
optionsStr.append("since_id=" + since_id.toString() + "&")
case Max(max_id) =>
optionsStr.append("max_id=" + max_id.toString() + "&")
case Count(count) =>
optionsStr.append("count=" + count.toString() + "&")
case Page(page) =>
optionsStr.append("page=" + page.toString() + "&")
}
}
val (statusCode, statusBody) =
Scitter.execute(optionsStr.toString(), username, password)
if (statusCode == 200)
{
val responseXML = XML.loadString(statusBody)
val statusListBuffer = new ListBuffer[Status]
for (n <- (responseXML \\ "status").elements)
statusListBuffer += (Status.fromXml(n))
statusListBuffer.toList
}
else
{
Nil
}
}
}
}
|
看到標在選項參數(shù)后面的 * 嗎?這表明該參數(shù)實際上是一個參數(shù)序列,這類似于 Java 5 中的 varargs 結(jié)構(gòu)。和 varargs 一樣,傳遞的參數(shù)數(shù)量可以像前面那樣為 0(不過,我們將需要回到測試代碼,向 friendsTimeline 調(diào)用增加一對括號,否則編譯器無法作出判斷:是調(diào)用不帶參數(shù)的方法,還是出于部分應用之類的目的而調(diào)用該方法);我們還可以開始傳遞那些 case 類型,如下面的清單所示:
清單 9. “……聽我細細說”(William Shakespeare)
package com.tedneward.scitter.test
{
class ScitterTests
{
// ...
@Test def scitterFriendsTimelineWithCount =
{
val scitter = new Scitter(testUser, testPassword)
val result = scitter.friendsTimeline(Count(5))
assertTrue(result.length == 5)
}
}
}
|
當然,總是存在這樣的可能性:客戶機傳入古怪的參數(shù)序列,例如 friendsTimeline(Count(5), Count(6), Count(7)),但是在這里,我們只是將列表傳遞給 Twitter(希望它們的錯誤處理足夠強大,能夠只采用指定的***那個參數(shù))。當然,如果真的擔心這一點,也很容易在構(gòu)造發(fā)送到 Twitter 的 URL 之前,從頭至尾檢查重復參數(shù)列表,并采用指定的每種參數(shù)的***一個參數(shù)。不過,后果自負。
#p#
兼容性
但是,這又產(chǎn)生一個有趣的問題:從 Java 代碼調(diào)用這個方法有多容易?畢竟,如果這個庫的主要目標之一是維護與 Java 代碼的兼容性,那么我們需要確保 Java 代碼在使用它時不至于太麻煩。
我們首先通過我們的好朋友 javap 檢驗一下 Scitter 類:
清單 10. 哦,沒錯,Java 代碼……我現(xiàn)在想起來了……
C:\>javap -classpath classes com.tedneward.scitter.Scitter
Compiled from "scitter.scala"
public class com.tedneward.scitter.Scitter extends java.lang.Object implements s
cala.ScalaObject{
public com.tedneward.scitter.Scitter(java.lang.String, java.lang.String);
public scala.List friendsTimeline(scala.Seq);
public boolean verifyCredentials();
public int $tag() throws java.rmi.RemoteException;
}
|
這時我心中有兩點擔心。首先,friendsTimeline() 帶有一個 scala.Seq 參數(shù)(這是我們剛才用過的重復參數(shù)特性)。其次,friendsTimeline() 方法和 Scitter 對象中的 publicTimeline() 方法一樣(如果不信,可以運行 javap 查證),返回一個元素列表 scala.List。這兩種類型在 Java 代碼中有多好用?
最簡單的方法是用 Java 代碼而不是 Scala 編寫一組小型的 JUnit 測試,所以接下來我們就這樣做。雖然可以測試 Scitter 實例的構(gòu)造,并調(diào)用它的 verifyCredentials() 方法,但這些并不是特別有用 — 記住,我們不是要驗證 Scitter 類的正確性,而是要看看從 Java 代碼中使用它有多容易。為此,我們直接編寫一個測試,該測試將獲取 “friends timeline” — 換句話說,我們要實例化一個 Scitter 實例,并且不使用任何參數(shù)來調(diào)用它的 friendsTimeline() 方法。
這有點復雜,因為需要傳入 scala.Seq 參數(shù) — scala.Seq 是一個 Scala 特性,它將映射到底層 JVM 中的一個接口,所以不能直接實例化。我們可以嘗試典型的 Java null 參數(shù),但是這樣做會在運行時拋出異常。我們需要的是一個 scala.Seq 類,以便從 Java 代碼中輕松地實例化這個類。
最終,我們還是在 mutable.ListBuffer 類型中找到一個這樣的類,這正是在 Scitter 實現(xiàn)本身中使用的類型:
清單 11. 現(xiàn)在我明白了自己為什么喜歡 Scala……
package com.tedneward.scitter.test;
import org.junit.*;
import com.tedneward.scitter.*;
public class JavaScitterTests
{
public static final String testUser = "TESTUSER";
public static final String testPassword = "TESTPASSWORD";
@Test public void getFriendsStatuses()
{
Scitter scitter = new Scitter(testUser, testPassword);
if (scitter.verifyCredentials())
{
scala.List statuses =
scitter.friendsTimeline(new scala.collection.mutable.ListBuffer());
Assert.assertTrue(statuses.length() > 0);
}
else
Assert.assertTrue(false);
}
}
|
使用返回的 scala.List 不是問題,因為我們可以像對待其他 Collection 類一樣對待它(不過我們的確懷念 Collection 的一些優(yōu)點,因為 List 上基于 Scala 的方法都假設(shè)您將從 Scala 中與它們交互),所以,遍歷結(jié)果并不難,只要用上一點 “舊式” Java 代碼(大約 1995 年時候的風格):
清單 12. 重回 1995,又見 Vector……
package com.tedneward.scitter.test;
import org.junit.*;
import com.tedneward.scitter.*;
public class JavaScitterTests
{
public static final String testUser = "TESTUSER";
public static final String testPassword = "TESTPASSWORD";
@Test public void getFriendsStatuses()
{
Scitter scitter = new Scitter(testUser, testPassword);
if (scitter.verifyCredentials())
{
scala.List statuses =
scitter.friendsTimeline(new scala.collection.mutable.ListBuffer());
Assert.assertTrue(statuses.length() > 0);
for (int i=0; i
|
這將我們引向另一個部分,即將參數(shù)傳遞到 friendsTimeline() 方法。不幸的是,ListBuffer 類型不是將一個集合作為構(gòu)造函數(shù)參數(shù),所以我們必須構(gòu)造參數(shù)列表,然后將集合傳遞到方法調(diào)用。這樣有些單調(diào)乏味,但還可以承受:
清單 13. 現(xiàn)在可以回到 Scala 嗎?
package com.tedneward.scitter.test;
import org.junit.*;
import com.tedneward.scitter.*;
public class JavaScitterTests
{
public static final String testUser = "TESTUSER";
public static final String testPassword = "TESTPASSWORD";
// ...
@Test public void getFriendsStatusesWithCount()
{
Scitter scitter = new Scitter(testUser, testPassword);
if (scitter.verifyCredentials())
{
scala.collection.mutable.ListBuffer params =
new scala.collection.mutable.ListBuffer();
params.$plus$eq(new Count(5));
scala.List statuses = scitter.friendsTimeline(params);
Assert.assertTrue(statuses.length() > 0);
Assert.assertTrue(statuses.length() == 5);
for (int i=0; i
|
所以,雖然 Java 版本比對應的 Scala 版本要冗長一點,但是到目前為止,從任何要使用 Scitter 庫的 Java 客戶機中調(diào)用該庫仍然非常簡單。好極了。
結(jié)束語
顯然,對于 Scitter 還有很多事情要做,但是它已經(jīng)逐漸豐滿起來,感覺不錯。我們設(shè)法對 Scitter 庫的通信部分進行了 DRY 化處理,并且為 Twitter 提供的很多不同的 API 調(diào)用合并了所需的可選參數(shù) — 到目前為止,Java 客戶機基本上沒有受到我們公布的 API 的拖累。即使 API 沒有 Scala 所使用的那些 API 那樣干凈,但是如果 Java 開發(fā)人員要使用 Scitter 庫,也不需要費太多力氣。
Scitter 庫仍然帶有對象的意味,不過我們也開始看到,一些有實用意義的 Scala 特性正在出現(xiàn)。隨著我們繼續(xù)構(gòu)建這個庫,只要有助于使代碼更簡潔、更清晰,越來越多這樣的特性將添加進來。本應如此。
是時候說再見了,我要短暫離開一下。等我回來時,我將為這個庫增加對離線測試的支持,并增加更新用戶狀態(tài)的功能。到那時,Scala 迷們,請記?。汗δ苷?偙裙δ苁С:?。(對不起,我只是太喜歡開玩笑了。)
【相關(guān)閱讀】
- Scala編程語言專題
- 從Java走進Scala(Scala經(jīng)典讀物)
- Scala代碼實例之Kestrel:文章匯總
- Java開發(fā)人員指南:Scala + Twitter = Scitter
- 從Java走進Scala:一步步教你使用Scala Actor
文章名稱:從Java走進Scala:Scitter庫的增強
鏈接地址:http://www.dlmjj.cn/article/dhccjeo.html


咨詢
建站咨詢
