MVP For GWT 系列资料转载十二:彻底了解 GWT Part 2:JavaScript 的 overlay type

源文出自:https://txt.appspot.com/pt2club.blogspot.com/2010/02/gwt-part-2javascript-overlay-type.html

彻底了解 GWT Part 2:JavaScript 的 overlay type

原文:http://googlewebtoolkit.blogspot.com/2008/08/getting-to-really-know-gwt-part-2.html

技术校正、审阅:tkcn

假设你已经在 GWT module 当中,愉快地使用 JSNI 来呼叫某些手写的 JavaScript。一切运作正常,但是 JSNI 只能在独立的 method 下运作。某些整合性状况需要你彻底地把 JavaScript 跟 Java 的 object 绑在一起——写 DOM 跟 JSON 就是两个好例子——所以我们十分需要可以从 Java 程式码直接与 JavaScript object 互动的方法。换句话说,我们想要 JavaScript 的 object 看起来就像我们写的 Java object。

GWT 1.5 引入了 JavaScript overlay type,这让 GWT 程式整合各种 JavaScript object 变得容易许多。这个技术有很多好处,像是让你能用 Java IDE 的 code completion 跟 refactoring 功能,即使你写的是 untype 的 JavaScript object。

范例:简单、有效率的 JSON
用一个范例来了解 overlay type 是最简单的方法。假设我们要存取一组「customer」数据,底层是用 JSON object。在 JavaScript 中的资料结构可能像这样:

void jsonData = [

  { "FirstName" : "Ps", "LastName" : "Monkey" },

  { "FirstName" : "痞子", "LastName" : "" },

  { "FirstName" : "Pt2", "LastName" : "Club" },

  { "FirstName" : "STO", "LastName" : "Orz" },

];

 

要把一个 Java type 加到上述的资料结构,要从建立一个 JavaScriptObject 的 subclass 开始,这在 GWT 表示是一个 JavaScript 的 object。接著增加一些 getter。

// An overlay type

class Customer extends JavaScriptObject {

  // Overlay types always have protected, zero-arg ctors

  protected Customer() { }    

 

  // Typically, methods on overlay types are JSNI

  public final native String getFirstName() /*-{ return this.FirstName; }-*/

  public final native String getLastName()  /*-{ return this.LastName;  }-*/

 

  // Note, though, that methods aren't required to be JSNI

  public final String getFullName() {

    return getFirstName() + " " + getLastName(); 

  }

}

 

如此一来,GWT 就会了解所有 Customer 的 instance 实际上是来自 GWT module 以外的 JavaScript object。这包含了很多意义。举例来说,看到 getFirstName() 跟 getLastName() 里头的 this reference。它实质上是代表一个 JavaScript object,所以你操作这个 this 就像在 JavaScript 里头一样。在这个例子中,我们可以直接存取 JSON 中那些我们已知的 field:this.FirstName 跟 this.LastName。

 

那么,你要如何才能真正得到一个被包装成 Java type 的 JavaScript object 呢?你不能用 new Customer() 来建构它,因为重点是把一个既有的 JavaScript object 包装成 Java type。因此,我们必须使用 JSNI 来得到这样一个 object:

class MyModuleEntryPoint implements EntryPoint {

  public void onModuleLoad() {

    Customer c = getFirstCustomer();

    // Yay! Now I have a JS object that appears to be a Customer

    Window.alert("Hello, " + c.getFirstName());

  }

 

  // Use JSNI to grab the JSON object we care about

  // The JSON object gets its Java type implicitly 

  // based on the method's return type

  private native Customer getFirstCustomer() /*-{

    // Get a reference to the first customer in the JSON array from earlier

    return $wnd.jsonData[0];

  }-*/;

}

 

现在来搞清楚我们做了啥。我们拿到了一个 plain old JSON object(译注:源自于 POJO)并且建立一个看起来很正常的 Java type,让 GWT 程式码中能够使用它。于是你就有了 code completion、refactoring、compile 阶段的检查——这些写 Java 时所拥有的好处。然而,你还是可以灵活地操作任何 JavaScript object,这使得存取 JSON service(使用 RequestBuilder)变得很轻而易举。

 

为一些 compiler 强者岔题一下。overlay type 另一个美妙的事情是你可以增加 Java 的 type,但是却不用影响底层的 JavaScript object。注意到上面例子中,我们加入的 getFullName() 这个 method。它是纯粹的 Java 程式码(并不存在于底层的 JavaScript object),但却是依照底层 JavaScript object 所写的。也就是说,处理同一个 JavaScript object,以 Java 的角度会比用 JavaScript 功能丰富得多;而且不用动到底层的 JavaScript object——无论是 instance 或是 prototype。

 

(接续上一段的题外话)在 overlay type 增加 method 这个很酷的怪招是可行的,因为 overlay type 的设计规则是不允许 polymorphic 呼叫,所有的 method 必须是 final 且/或 private。因此,compiler 是静态地解读每一个 overlay type 的 method,所以不需要在 runtime 的时候动态 dispatch。这是为什么我们不用拘泥在 object 的 function pointer;compiler 可以直接对 method 呼叫,就好像是 global function、独立于 object 之外。很明显的,直接呼叫 function 会比间接快得多。更棒的是,因为呼叫 overlay type 的 method 是静态解读担庑┒。

 

范例:lightweight collection

 

我们在上面的例子当中掩盖了某些事情。getFirstCustomer() 这个 method 是非常不切实际的。你一定会希望存取全部的 customer 阵列。所以,我们需要一个 overlay type 来表示这个 JavaScript 阵列。幸运的是,这很简单:

//泛型在 overlay type 裡頭也運作正常!

class JsArray<E extends JavaScriptObject> extends JavaScriptObject {

  protected JsArray() { }

  public final native int length() /*-{ return this.length; }-*/;

  public final native E get(int i) /*-{ return this[i];     }-*/;

}

 

现在我们可以写出更有趣的程式了:

class MyModuleEntryPoint implements EntryPoint {

  public void onModuleLoad() {

    JsArray<Customer> cs = getCustomers();

    for (int i = 0, n = cs.length(); i < n; ++i) {

      Window.alert("Hello, " + cs.get(i).getFullName());

    }

  }

 

  // Return the whole JSON array, as is 

private final native JsArray<Customer> getCustomers() /*-{

    return $wnd.jsonData;

  }-*/;

}

 

这是一个很干净的程式码,尤其是以建立灵活配置的角度来看。正如上头提到的,compiler 可以作一些十分 fancy 的事情,让它相当有效率。看一下 onModuleLoad() 这个 method 在没有 obfuscate 的 compile 结果:

function $onModuleLoad(){

  var cs, i, n;

  cs = $wnd.jsonData;

  for (i = 0, n = cs.length; i < n; ++i) {

    $wnd.alert('Hello, ' + (cs[i].FirstName + ' ' + cs[i].LastName));

  }

}

 

这个最佳化真的是 xx 的好。即使是 getFullName() 这个 method 的 overhead 也没了。事实上, 所有 Java method 的呼叫动作都不见了。当我们说:「GWT 给你可负担的 abstraction」,这就是其中之一。不仅 inline 的程式码执行速度明显变快、我们不再需要定义 function 的内容、也因而得以将 script 简短化(虽然持平而论,inline 的方式也很容易让 script 量变多,所以我们小心地在速度与程式码大小之间取得平衡)。现在回顾上头原始的 Java 程式码是十分有趣的,而试著推导 compiler 最佳化的步骤就展示到这边。 不过,我们还是忍不住要 show 一下对应、obfuscate 过的程式码: 

function B(){var a,b,c;a=$wnd.jsonData;for(b=0,c=a.length;b<c;++b){  $wnd.alert(l+(a[b].FirstName+m+a[b].LastName))}}

 

注意在这个版本当中,唯一没有 obfuscate 的是 JavaScript 当中的识别字,例如 FirstName、 LastName、jsonData 等。这是为什么即使 GWT 努力让大量 JavaScript 交互沟通的操作变得容易,但我们还是努力说服别人尽量用纯 Java 来写程式、而不是混著 JavaScript 写。希望你听到我们讲这些之后,你会明白我们不是要打击 JavaScript——只是我们不能对它们最佳化,这会让我们很沮丧。

 

摻在一起作撒尿牛丸 

overlay type 是 GWT 1.5 的重要特性。这个技术让直接与 JavaScript library 互相沟通变得相当容易。希望在读完这篇文章之后,你可以想像如何以一组 Java type 直接导入任何 JavaScript library 到 GWT 里头,进而使用 Java IDE 来进行高效率的开发跟 debug,却不会因为任何类型的 GWT overhead 而影响程式码大小或执行速度。同时,overlay type 作为一个强大的 abstraction 工具,提供更优雅的低阶 API,例如新的DOM package

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值