顯示具有 Coding Skill 標籤的文章。 顯示所有文章
顯示具有 Coding Skill 標籤的文章。 顯示所有文章

2009年2月24日 星期二

Event driven與Exception架構很類似

Event driven其實跟我們熟悉的throw--catch很類似。

我們可以做個類比:


Event <--> Exception
它們都是這個架構的資訊攜帶者,自訂Event可以讓我們攜帶各種需要傳遞的物件,如同Exception可以攜帶各種錯誤訊息甚至錯誤物件往外傳播。
不同的地方在於,Exception的型態(type)決定了會被哪個catch抓到,而Event在initialization時必須給予一個事件代號,例如
new MouseEvent("click"),代表他是一個滑鼠的按鈕事件,當事件傳播出去後,只有指名要抓click代號的handler才能抓到此Event


dispatchEvent <--> throw
一樣都是將資訊向外傳播的方法,dispatchEvent是將Event傳播到UI元件的樹上,而throw是將Exception傳到call stack上。

例如 Window --> Form --> Button 的UI樹上,在Button上觸發的Event會一路傳到Form,再傳到Window上,若沒有UI有註冊聆聽此事件,Event會一路往外傳到最上層。Exception的傳遞非常類似,都是將Exception在function call stack上一路往外丟,沒有function實際catch時,就會丟給VM。


addEventListener <--> catch
攔截資訊的方法,catch指名要攔截哪個Exception,而addEventListener指名要攔截哪個Event,一樣的是攔截後Exception與Event原則上不會再往上(往外)傳播,除非使用者自己繼續throw Exception/dispatchEvent。


這兩種架構不一樣的地方在Exception有強制宣告的性質,若底層呼叫的method/function會throw某種VM無法處理的Exception,使用者必須自己寫處理方式,無論是繼續往上丟,或者自己寫try catch block處理,但是Event沒有此限制,因此若一個物件會dispatch某種自訂Event,上層元件不必強制處理此Event。

2007年2月22日 星期四

Family polymorphism in Java

Family polymorphism是一種特殊的繼承架構,舉例

Graphy {Node [] nodes ; Edge [] edges; ...}

則稱Graphy含有Node與Edge的物件。
若我們創造一個WeightGraphy,其Edge要用WeightEdge extends Edge,則其constructor為

WeightGraphy(){nodes = new WeightNode[];edges = new WeightEdge[]; ...}

換句話說,WeightGraphy所用的Node與Edge會與其他類型的Graphy,例如UndirectionGraphy所用的Edge,Node皆不一樣,但是在Graphy的定義中無法顯現出這種差別,所以這就是Family polymorphism造成的問題。

那如何在Java 1.5支援Generic Type時解決這問題呢?
Graphy <N extends Node, E extends Edge >{N [] nodes; E [] edges;...}

這樣的話,WeightGraphy的定義可以寫成

WeightGraphy
< WeightNode, WieghtEdge >{ ... }

對於family polymorphism的問題就可以迎刃而解了!

2007年2月18日 星期日

使用Big5編碼的Java properties

為了要實現可自由設定的字串訊息(也就是俗稱的i18n),我嘗試了一些方法。
最簡單的就是給每個locale一個index,而每個String都以String array的型態存在,利用locale的index找到相對應的字串。但是要這樣做,就必須定義一個存放此array的地方,我是用constant interface來儲存,但是這樣為免失之雜亂,因此我想到了利用properties來儲存各語系字串的方法。事實上這也是各大主流軟體的i18n設定技巧。

不過Java的properties輸入輸出介面並不是很友善,最原始的properties (entry = value)的型態只能允許ISO8859-1編碼,換句話說(abc = 中文)的形式輸入了java中只會得到一堆亂碼。因此JAVA製作了一個特別的XML格式,利用<entry="abc">中文</entry>來設定所有的property,且可利用xml宣告charset的特性保留文字編碼,使我們方便實現 利用不同encoding達到i18n的目的(而不需使用UTF-8等unicode系統),對於支援老舊編碼的protocol很有用。

2007年1月26日 星期五

Exception在網路程式的重要性

剛剛與一個網路程式奮戰一晚,終於將程式中的問題找出來了。

我以一個事先寫好的telnet server當作平台,測試自己的thread與telnet操作技巧,在完成了一些基礎的輸入輸出功能後,擺在自己的機器上測試。
ok,一切就緒,提槍上馬,果然馬上就出問題。當我在client端輸入任何字串都平安無事,但在不輸入任何字元的情況下按下enter,居然就當了!
後來搞清楚,因為我的程式會對輸入字串的第一個字元檢查,而不輸入任何字元得到的字串是"",因此根本沒有第一個字元這種東西,當然會當掉。

第二個問題是,當我利用不正常方法斷線(直接關掉視窗),之後再連線,輸入完帳號密碼後馬上會被斷線。之後知道是因為我的不正常斷線會讓thread卡在無窮迴圈while(!exit)readLine()之中,因此任何對這個斷線thread輸出文字的舉動當然會造成IOException,但之前我將IOException的處理程式寫成啥都不做,自然就沒有任何除錯訊息可以讓我看,害我白白浪費依堆時間找錯。

這次的經驗告訴我們:一旦一段程式會丟出Exception,最好還是將他用try/catch包住並列印錯誤訊息,否則就乖乖往外throw吧,別把try/catch寫出來了卻不做任何處理!之後的debug時一定會後悔的!

2007年1月25日 星期四

F-bounded polymorphism in Java 1.5

剛剛在寫code時遇到一個需求
Class A extends Car{}
Class B extends Car{}
Class C extends Car{}
Class ListA extends List<A>{}
Class ListB extends List<B>{}
Class ListC extends List<C>{}

各位應該可以看到,ListA是專門容納class A的容器,以下類推。

若是某些method的內容會操作到特定List與其member,且內容一模一樣:
public void mA(ListA la, A a){
...
la.add(a);
}

public void mB(ListB lb, B b){
...
lb.add(b);
}

public void mC(ListC lc, C c){
...
lc.add(c);
}

在JAVA 1.5之前,因為沒有Generic Type,因此想幫上列三種method整合為一是不可能的。這個問題我們可以用F-bounded polymorphism來解釋它。

在Java 1.5,我們可以用以下的method合併mA mB mC:

public void <T extends Car> mX(List<T> lt, T t){
...
lt.add(t)
}

Java真是厲害呀!

Java synchronized概念

假設有一排餐廳,他們的編號從1...到無限大 ,每間餐廳都通過Restaurant標準,因此都能提供某些固定的服務。
每間餐廳都擁有一些一般區域與一個預約包廂,一般區域可以讓任意數量的人通行;而包廂只能同時讓一個人使用,若是有多於一個人要用,第一個進入包廂的人會將門鎖上,因此其他要進去的人就無法進入了,另外。
這些餐廳共用一間豪華廁所,也是一次只能容納一個顧客,若廁所中已經有人,則後到者要等待。

在此例子,每個物件與 Java術語的對應關係是:

  • 餐廳標準Restaurant:Class Restaurant
  • 餐廳1號:Instance 1 of Restaurant
  • 每間餐廳的一般區域:Normal Methods of Instance Restaurant
  • 每間餐廳的包廂:Synchronized Methods of Instance Restaurant
  • 顧客:Thread
  • 所有餐廳共用的豪華廁所:Synchronized Static Methods of Class Restaurant
另外,所謂的包廂,指的是一組房間,因此所有被設定為synchronized的method都算在同一組包廂內。

1.現在若有兩人想要使用同一個餐廳的包廂,後到者B會被檔在門外,而先到者A可以進入包廂並任意使用其中的任何房間,B必須呆呆等到A離開這個包廂,或者A自願進入一間等待室,讓B進入。

在此例中,Thread A呼叫了餐廳instance r1的synchronized method m1();且Thread B隨後呼叫r1的另一個synchronized method m2();則B被阻擋,直到A離開m1()或者A執行了r1的wait()為止。

2.另一例子,若A進入包廂,但B在一般區域活動是沒有問題的,B並不會受到阻擋

如何建立排序列(Sorted list)

我在寫JavaMudX時,需要用到排序的List,因為這樣可以讓list中的物件在顯示時有某種序列。但是我發現Java標準的API中並沒有自動排序的List,只有用Collections.sort(List)或Collections.sort(List, Comparator)來執行排序!
眾所皆知,任何排序動作都需要O(n ln n)的時間,因此使用Collections.sort(...)來排序是不切實際的,若是在insert method of List執行sort,那每次insert至少都要花O(n ln n)的時間,這真是浪費!因此我便在網路上尋找可排序的其他種方法。

後來我在某家網路文件上看到合理又簡單的建立sorted list方法:


//假設這是個已經排序的List,因此對此List進行BinarySearch是很合理的
//此index回傳的是此物件在此List上的序數,若是此物件不在List上,

//則會回傳一個負數
int index = Collections.binarySearch(itemList, item);
//若index為負數,將它插入-index-1的位置
if(index < style="color: rgb(153, 153, 153);">//否則,插入他尋找到的index
else itemList.add(item);


因此,在一個內容為 ["aaa", "bbb", "ccc", "ddd"]的List上,若您要add的是"ccc",則它會被插入 index= 3的地方;若要add("czz")的話,index會很神奇地回傳-5,則插入動作把"czz"放在-(-5)-1 = 4的地方!
如此我們就完成了可排序的add行為。若能保證此List的開始狀態就是已經sorted(例如empty List) 的話,則之後的add/remove都不會破壞已排序的特性。

且此add()耗費的時間只是從O(1)提升到O(e * lnn)+O(c)而已!
(e:equals花費的時間 n:List的大小 e * ln n:Binary Search的時間 c:add(item, index) 的時間)
非常划算!