栏目导航
热点推荐
- 三十条有用的 Java 编程规则
- Java制作水印图片源码
- Java常见异常及可能的导致原因
- Java中的修饰词使用方法总结
- J2EE系统异常的处理准则
- Java中的异常、断言、日志解析(
- Java面试技巧:Java面试题集锦(
- 面向Java开发人员的Scala指南:
- Java程序员:一刻钟精通正则表达
- 网友经验分享:学好java开发的关
- 专家解答:创建表格与数据库进行
- Java远程访问Domino数据库
阅览排行
面向Java开发人员的Scala指南: 增强Scitter库
www.jz123.cn 2009-09-03 来源: IT专家网 责任编辑(袁袁) 我要投递新闻
对于代码,第一件让我烦恼的事就是,我在 Scitter 对象和类的每个方法中都重复这样的操作序列:创建 HttpClient 实例,对它进行初始化,用必要的验证参数对它进行参数化,等等。当它们只有 3 个方法时,可以进行管理,但是显然不易于伸缩,而且以后还会增加很多方法。此外,以后重新在那些方法中引入模拟和/或本地/离线测试功能将十分困难。所以我们要解决这个问题。
实际上,我们这里介绍的并不是 Scala 本身,而是不要重复自己(Don't-Repeat-Yourself)的思想。因此,我将从基本的面向对象方法开始:创建一个 helper 方法,用于做实际工作:
清单 2. 对代码库执行 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() 方法返回一个元组,其中包含状态码和响应主体。这正是让元组成为语言中固有的一部分的一个强大之处,因为实际上很容易从一个方法调用返回多个返回值。当然,在 Java 代码中,也可以通过创建包含元组元素的顶级或嵌套类来实现这一点,但是这需要一整套专用于这一个方法的代码。此外,本来也可以返回一个包含 String 键和 Object 值的 Map,但是那样就在很大程度上丧失了类型安全性。元组并不是一个非常具有变革性的特性,它只不过是又一个使 Scala 成为强大语言的优秀特性。
由于使用元组,我需要使用 Scala 的另一个特色语法将两个结果都捕捉到本地变量中,就像下面这个重写后的 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 全部去掉,并用 _ 替代它,因为我没有用过第二个参数(test 没有返回 statusBody),但是对于其他调用将需要这个 statusBody,所以出于演示的目的,我保留了该参数。
注意,execute() 没有泄露任何与实际 HTTP 通信相关的细节 — 这是 Encapsulation 101.这样便于以后用其他实现替换 execute()(以后的确要这么做),或者便于通过重用 HttpClient 对象来优化代码,而不是每次重新实例化新的对象。
接下来,注意到 execute() 方法在 Scitter 对象上吗?这意味着我将可以从不同的 Scitter 实例中使用它(至少现在可以这样做,如果以后在 execute() 内部执行的操作不允许这样做,则另当别论)— 这就是我将 execute() 标记为 private[scitter] 的原因,这意味着 com.tedneward.scitter 包中的所有内容都可以看到它。
(顺便说一句,如果还没有运行测试的话,那么请运行测试,确保一切运行良好。我将假设我们在讨论代码时您会运行测试,所以如果我忘了提醒您,并不意味着您也忘记这么做。)
顺便说一句,对于经过验证的访问,为了支持 Scitter 类,需要一个用户名和密码,所以我将创建一个重载的 execute() 方法,该方法新增两个 String 参数:
清单 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 化处理,接下来我们转移到下一件事情:获得朋友的 tweet 的列表
上一篇:Java Math 类中的新功能(一): 实数 下一篇:通过Java或Jsp向数据库存取二进制图片