<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    2006年3月2日

    http://www.firstui.com/read.php/250.htm

    posted @ 2006-11-23 23:47 killvin| 編輯 收藏

    很久都已經下定了決心不再維護那里的空間了,即使是成為了特約的作者也難以挽回我對他們Blog的信心,雖然blogjava的系統與csdn的是同樣出自一份代碼,但顯然無論從服務的內容還是代碼的理解上,csdn與blogjava相差的太遠,當然我并不是完全的贊同目前blogjava的風格,尤其是身為介紹java的博客系統,竟然采用asp的技術來實現,實在是太煞風景了,甚至有些讓你匪夷所思,不過瑕不掩瑜,畢竟無論從速, 專業程度還是目前提供的服務上來看,blogjava確實比同類型的網站要優秀一些.

    posted @ 2006-10-29 15:54 killvin| 編輯 收藏

    Version 1.01 on Bindows enhanced its Mozilla support considerably. At this point, the level of support is such that many complicated applications are available to Mozilla users. For example: now these can use our forum and the registration application.
    We are dedicated to make Bindows work at least as well with Mozilla 1.4+ as it does with Internet Explorer 6.0 SP1. The Mozilla support is now integral part of Bindows and we will continue to develop, maintain and support it.
    However, some issues are not solved yet and this document details them.


    Known Issues
    Web Services

    We do not yet have a working SOAP web service client working with Mozilla. Mozilla has a built in web service client but it does not yet work with all the major web services platforms. We anticipate this to be resolve in a near future version of Bindows.


    Graphs, Charts and Gauges
    Graphs, charts and gauges are not supported. For the Bindows charts and gauges we rely on VML to do the drawing. Mozilla does not yet have a way to draw vector graphics at the client side without using a plugin. We consider several alternatives here but there is no time frame for a solution yet.


    Miscellaneous
    The caret is sometimes not shown in a focused textfield. This is a Mozilla bug and unless this is fixed in Mozilla this issue will remain.
    Preferred size is not working consistently. Please report the cases where you are experiencing issues with the preferred size and we will see what can be done in those specific cases.
    Icons in menu items works inconsistently between Mozilla versions. This is due to differences in the Mozilla toolkits used by different browsers.
    Menu labels do not accept HTML. If you use HTML, the markup will be stripped.
    The rendering of the group box is incorrect.
    Labels with text align center and icon position set to top or bottom are a bit jumpy, ie. the icon move around depending on the state.
    Dragging elements sometimes inconsistent. If an image is dragged an OS level drag and drop is initiated. Also, for some components a dragging will not work when the mouse leaves the browser window or enters a frame.
    The upper border for the tab pages are not correctly drawn in Mozilla Firefox using Windows Classic. This is a Mozilla bug.
    BiKeyboardEvent keyCode is sometimes incorrect in a keypress event (for special keys). The reason for this bug is that Internet Explorer and Mozilla are handling keyboard events differently.
    Resize events are not fired on all BiComponents.
    Focus and blur events are inconsistent when using BiRichEdit.

    posted @ 2006-10-14 16:21 killvin| 編輯 收藏

    Jsparse is a parse?to parse the schema file?with javascript.
    If you are interested in it, you can vist the url
    http://code.google.com/p/jsparse/ ?

    get source with svn
    svn checkout http://jsparse.googlecode.com/svn/trunk/ jsparse

    posted @ 2006-09-13 23:23 killvin| 編輯 收藏

    ACE中的Double Checked Locking 模式


    (作者:Douglas C. Schmidt ,by huihoo.org CORBA課題 Thzhang 譯 , Allen整理,制作)

    意圖

    無論什么時候當臨界區中的代碼僅僅需要加鎖一次,同時當其獲取鎖的時候必須是線程安全的,可以用Double Checked Locking 模式來減少競爭和加鎖載荷。

    動機

    1、標準的單例。開發正確的有效的并發應用是困難的。程序員必須學習新的技術(并發控制和防止死鎖的算法)和機制(如多線程和同步API)。此外,許多熟悉的設計模式(如單例和迭代子)在包含不使用任何并發上下文假設的順序程序中可以工作的很好。為了說明這點,考慮一個標準的單例模式在多線程環境下的實現。單例模式保證一個類僅有一個實例同時提供了全局唯一的訪問這個實例的入口點。在c++程序中動態分配單例對象是通用的方式,這是因為c++程序沒有很好的定義靜態全局對象的初始化次序,因此是不可移植的。而且,動態分配避免了單例對象在永遠沒有被使用情況下的初始化開銷。

    
    class Singleton
    {
    public:
    static Singleton *instance (void)
    {
    if (instance_ == 0)
    // Critical section.
    instance_ = new Singleton;
    return instance_;
    }
    void method (void);
    // Other methods and members omitted.
    private:
    static Singleton *instance_;
    };
    
    
    

    應用代碼在使用單例對象提供的操作前,通過調用靜態的instance方法來獲取單例對象的引用,如下所示:
    Singleton::instance ()->method ();
    2、問題:競爭條件。不幸的是,上面展示的標準單例模式的實現在搶先多任務和真正并行環境下無法正常工作。例如,如果在并行主機上運行的多個線程在單例對象初始化之前同時調用Singleton::instance方法,Singleton的構造函數將被調用多次,這是因為多個線程將在上面展示的臨界區中執行new singleton操作。臨界區是一個必須遵守下列定式的指令序列:當一個線程/進程在臨界區中運行時,沒有其他任何線程/進程會同時在臨界區中運行。在這個例子中,單例的初始化過程是一個臨界區,違反臨界區的原則,在最好的情況下將導致內存泄漏,最壞的情況下,如果初始化過程不是冪等的(idempotent.),將導致嚴重的后果。

    3、 通常的陷阱和弊端。實現臨界區的通常方法是在類中增加一個靜態的Mutex對象。這個Mutex保證單例的分配和初始化是原子操作,如下:

    
    class Singleton
    {
    public:
    static Singleton *instance (void)
    {
    // Constructor of guard acquires lock_ automatically.
    Guard guard (lock_);
    // Only one thread in the critical section at a time.
    if (instance_ == 0)
    instance_ = new Singleton;
    return instance_;
    // Destructor of guard releases lock_ automatically.
    }
    private:
    static Mutex lock_;
    static Singleton *instance_;
    };
    
    

    guard類使用了一個c++的習慣用法,當這個類的對象實例被創建時,它使用構造函數來自動獲取一個資源,當類對象離開一個區域時,使用析構器來自動釋放這個資源。通過使用guard,每一個對Singleton::instance方法的訪問將自動的獲取和釋放lock_。
    即使這個臨界區只是被使用了一次,但是每個對instance方法的調用都必須獲取和釋放lock_。雖然現在這個實現是線程安全的,但過多的加鎖負載是不能被接受的。一個明顯(雖然不正確)的優化方法是將guard放在針對instance進行條件檢測的內部:

    
    static Singleton *instance (void)
    {
    if (instance_ == 0) {
    Guard guard (lock_);
    // Only come here if instance_ hasn't been initialized yet.
    instance_ = new Singleton;
    }
    return instance_;
    }
    
    
    這將減少加鎖負載,但是不能提供線程安全的初始化。在多線程的應用中,仍然存在競爭條件,將導致多次初始化instance_。例如,考慮兩個線程同時檢測 instance_ == 0,都將會成功,一個將通過guard獲取lock_另一個將被阻塞。當第一線程初始化Singleton后釋放lock_,被阻塞的線程將獲取lock_,錯誤的再次初始化Singleton。
    4、解決之道,Double Checked Locking優化。解決這個問題更好的方法是使用Double Checked Locking。它是一種用于清除不必要加鎖過程的優化模式。具有諷刺意味的是,它的實現幾乎和前面的方法一樣。通過在另一個條件檢測中包裝對new的調用來避免不必要的加鎖:

    
    class Singleton
    {
    public:
    static Singleton *instance (void)
    {
    // First check
    if (instance_ == 0)
    {
    // Ensure serialization (guard constructor acquires lock_).
    Guard guard (lock_);
    // Double check.
    if (instance_ == 0)
    instance_ = new Singleton;
    }
    return instance_;
    // guard destructor releases lock_.
    }
    private:
    static Mutex lock_;
    static Singleton *instance_;
    };
    
    

    第一個獲取lock_的線程將構建Singleton,并將指針分配給instance_,后續調用instance方法的線程將發現instance_ != 0,于是將跳過初始化過程。如果多個線程試圖并發初始化Singleton,第二個檢測件阻止競爭條件的發生。在上面的代碼中,這些線程將在lock_上排隊,當排隊的線程最終獲取lock_時,他們將發現instance_ != 0于是將跳過初始化過程。

    上面Singleton::instance的實現僅僅在Singleton首次被初始化時,如果有多個線程同時進入instance方法將導致加鎖負載。在后續對Singleton::instance的調用因為instance_ != 0而不會有加鎖和解鎖的負載。 通過增加一個mutex和一個二次條件檢測,標準的單例實現可以是線程安全的,同時不會產生過多的初始化加鎖負載。

    適應性

    > 當一個應用具有下列特征時,可以使用Double Checked Locking優化模式:
    1、應用包含一個或多個需要順序執行的臨界區代碼。
    2、多個線程可能潛在的試圖并發執行臨界區。
    3、臨界區僅僅需要被執行一次。
    4、在每一個對臨界區的訪問進行加鎖操作將導致過多加鎖負載。
    5、在一個鎖的范圍內增加一個輕量的,可靠的條件檢測是可行的。

    結構和參與者

    通過使用偽代碼能夠最好地展示Double Checked Locking模式的結構和參與者,圖1展示了在Double Checked Locking模式有下列參與者:



    1、僅有一次臨界區(Just Once Critical Section,)。臨界區所包含的代碼僅僅被執行一次。例如,單例對象僅僅被初始化一次。這樣,執行對new Singleton的調用(只有一次)相對于Singleton::instance方法的訪問將非常稀少。
    2、mutex。鎖被用來序列化對臨界區中代碼的訪問。
    3、標記。標記被用來指示臨界區的代碼是否已經被執行過。在上面的例子中單例指針instance_被用來作為標記。
    4、 應用線程。試圖執行臨界區代碼的線程。

    協作

    圖2展示了Double Checked Locking模式的參與者之間的互動。作為一種普通的優化用例,應用線程首先檢測flag是否已經被設置。如果沒有被設置,mutex將被獲取。在持有這個鎖之后,應用線程將再次檢測flag是否被設置,實現Just Once Critical Section,設定flag為真。最后應用線程釋放鎖。



    結論

    使用Double Checked Locking模式帶來的幾點好處:
    1、最小化加鎖。通過實現兩個flag檢測,Double Checked Locking模式實現通常用例的優化。一旦flag被設置,第一個檢測將保證后續的訪問不要加鎖操作。
    2、防止競爭條件。對flag的第二個檢測將保證臨界區中的事件僅實現一次。
    使用Double Checked Locking模式也將帶來一個缺點:產生微妙的移植bug的潛能。這個微妙的移植問題能夠導致致命的bug,如果使用Double Checked Locking模式的軟件被移植到沒有原子性的指針和正數賦值語義的硬件平臺上。例如,如果一個instance_指針被用來作為Singleton實現的flag,instance_指針中的所有位(bit)必須在一次操作中完成讀和寫。如果將new的結果寫入內存不是一個原子操作,其他的線程可能會試圖讀取一個不健全的指針,這將導致非法的內存訪問。
    在一些允許內存地址跨越對齊邊界的系統上這種現象是可能的,因此每次訪問需要從內存中取兩次。在這種情況下,系統可能使用分離的字對齊合成flag,來表示instance_指針。
    如果一個過于激進(aggressive)編譯器通過某種緩沖手段來優化flag,或是移除了第二個flag==0檢測,將帶來另外的相關問題。后面會介紹如何使用volatile關鍵字來解決這個問題。

    實現和例子代碼

    ACE在多個庫組件中使用Double Checked Locking模式。例如,為了減少代碼的重復,ACE使用了一個可重用的適配器ACE Singleton來將普通的類轉換成具有單例行為的類。下面的代碼展示了如何用Double Checked Locking模式來實現ACE Singleton。

    
    // A Singleton Adapter: uses the Adapter
    // pattern to turn ordinary classes into
    // Singletons optimized with the
    // Double-Checked Locking pattern.
    template 
    class ACE_Singleton
    {
    public:
    static TYPE *instance (void);
    protected:
    static TYPE *instance_;
    static LOCK lock_;
    };
    template  TYPE *
    ACE_Singleton::instance ()
    {
    // Perform the Double-Checked Locking to
    // ensure proper initialization.
    if (instance_ == 0) {
    ACE_Guard lock (lock_);
    if (instance_ == 0)
    instance_ = new TYPE;
    }
    return instance_;
    }
    


    ACE Singleton類被TYPE和LOCK來參數化。因此一個給定TYEP的類將被轉換成使用LOCK類型的互斥量的具有單例行為的類。
    ACE中的Token Manager.是使用ACE Singleton的一個例子。Token Manager實現在多線程應用中對本地和遠端的token(一種遞歸鎖)死鎖檢測。為了減少資源的使用,Token Manager被按需創建。為了創建一個單例的Token Manager對象,只是需要實現下面的typedef:

    typedef ACE_Singleton Token_Mgr;
    Token Manager單例將被用于本地和遠端的token死鎖檢測。在一個線程阻塞等待互斥量之前,它首先查詢Token Manager單例,來測試阻塞是否會導致死鎖狀態。對于系統中的每一個token,Token Manager單例維護一個持有token線程和所有阻塞等待在該token的線程記錄鏈表。這些數據將提供充足的檢測死鎖狀態的依據。使用Token Manager單例的過程如下:

    
    // Acquire the mutex.
    int Mutex_Token::acquire (void)
    {
    // If the token is already held, we must block.
    if (mutex_in_use ()) {
    // Use the Token_Mgr Singleton to check
    // for a deadlock situation *before* blocking.
    if (Token_Mgr::instance ()->testdeadlock ()) {
    errno = EDEADLK;
    return -1;
    				}
    else
    // Sleep waiting for the lock...
    		// Acquire lock...
    		}
    

    變化

    一種變化的Double Checked Locking模式實現可能是需要的,如果一個編譯器通過某種緩沖方式優化了flag。在這種情況下,緩沖的粘著性(coherency)將變成問題,如果拷貝flag到多個線程的寄存器中,會產生不一致現象。如果一個線程更改flag的值將不能反映在其他線程的對應拷貝中。

    另一個相關的問題是編譯器移除了第二個flag==0檢測,因為它對于持有高度優化特性的編譯器來說是多余的。例如,下面的代碼在激進的編譯器下將被跳過對flag的讀取,而是假定instance_還是為0,因為它沒有被聲明為volatile的。
    
    Singleton *Singleton::instance (void)
    {
    if (Singleton::instance_ == 0)
    {
    // Only lock if instance_ isn't 0.
    Guard guard (lock_);
    // Dead code elimination may remove the next line.
    // Perform the Double-Check.
    if (Singleton::instance_ == 0)
    // ...
    
    


    解決這兩個問題的一個方法是生命flag為Singleton的volatile成員變量,如下:
    private:
    static volatile long Flag_; // Flag is volatile.
    使用volatile將保證編譯器不會將flag緩沖到編譯器,同時也不會優化掉第二次讀操作。使用volatile關鍵字的言下之意是所有對flag的訪問是通過內存,而不是通過寄存器。

    相關模式

    Double Checked Locking模式是First-Time-In習慣用法的一個變化。First-Time-In習慣用法經常使用在類似c這種缺少構造器的程序語言中,下面的代碼展示了這個模式:

    
    	static const int STACK_SIZE = 1000;
    static T *stack_;
    static int top_;
    void push (T *item)
    {
    // First-time-in flag
    if (stack_ == 0) {
    stack_ = malloc (STACK_SIZE * sizeof *stack);
    assert (stack_ != 0);
    top_ = 0;
    }
    stack_[top_++] = item;
    // ...
    }
    
    

    第一次push被調用時,stack_是0,這將導致觸發malloc來初始化它自己。

    posted @ 2006-09-13 23:16 killvin| 編輯 收藏

    在上一篇的文章中,比較仔細的分析了IE對于Cookie處理過程中出現的問題,既然存在問題就要想辦法解決,而問題的關鍵在于IE沒有能夠"老實"的報告用戶對于Cookie的設置問題

    IE
    GET /mail HTTP/1.1
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
    Accept-Language: zh-cn
    Accept-Encoding: gzip, deflate
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)
    Host: mail.google.com
    Connection: Keep-Alive
    Cookie:
    gmailchat=killvin.liu@gmail.com/519974 ; PREF=ID=d68c481e542af276:NW=1:TM=1151742225:LM=1151742225:S=2qbdhg0_z3i-OAbW; SID=DQAAAG8AAACEdcjD2IZMNqZVatDbD62X8_U18oJuTVQc9XZUJi7MgCkM8sggJ8M5npZ35GXjdalT2o8QWPUve04tepy61MPv4v_EpILafg3JdIf8AFjD91aMT0tI5gb763FouV3e_2-C364HDO5Qzb4P4gjjgpHC

    Firefox
    GET /mail HTTP/1.1
    Host: mail.google.com
    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
    Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 300
    Connection: keep-alive

    我們可以注意到:如果用戶關閉了瀏覽器的Cookie選項,在Firefox中對于新的請求是包含Cookie的信息的,然而IE卻恰恰相反!這也就直接用IE瀏覽一些網站所導致的Cookie校驗失敗問題(比如即使用戶在使用過程中突然關閉Cookie,依然可以自由的訪問網站的各項服務),進一步,如果某個網站是讀取依靠Cookie中的信息來驗證用戶(尤其是那種保存客戶信息的提示),即使這個用戶離開或者關閉窗口,任何一個后來的人都可以輕松的瀏覽這個用戶的私有信息!這就是Cookie的最大的問題所在,當然這是另外的話題,不屬于本次討論的范疇,這一次要說的是
    如何避免IE瀏覽器下,用戶關閉Cookie的問題。

    有一個思路,經過測試我們發現IE瀏覽器只是在首次頁面請求的時候才會去讀取Cookie的值,所以,為了避免IE瀏覽器關閉Cookie的問題,可以在用戶請求某個鏈接的時候,先將用戶的Cookie存放在某處,并清空此時所有的Cookie值,再次的請求頁面,而這樣做的目的就是強迫IE讀取硬盤上的Cookie,如果此時用戶關閉了Cookie,就不會在請求的頭信息中看到Cookie信息;如果沒有關閉Cookie,就可以放心的將原先的Cookie寫回Reponse對象。


    只是不知道Google的網站是否也是按照這個思路去解決此問題的?

    posted @ 2006-07-02 21:53 killvin| 編輯 收藏

    IE在處理COOKIE方面的問題

    1。即使你提高了瀏覽器的隱私登記,在第一次打開窗口的時候,你獲取不到任何的Cookies對象(很顯然的結果),然而當你再次刷新本頁面的,Cookie此時會奇跡般的出現!而在Firefox瀏覽器中按照以上的步驟,是不會出現這樣的情況的。

    2。不僅如此,你還可以透過Action的處理,多次的往Cookie中增加Cookie的數量(當然在Action中你依然可以自由的獲取到Cookie這個數組,并且不為空),然而讓人匪夷所思的是在Cookie的存放目錄下你是找尋不到任何的蛛絲馬跡的。而在Firefox沒有出現以上的情況。

    -解決
    1。在首次進入頁面時查詢客戶端的Cookie,如果不存在則警告用戶,并要求再次的登陸。
    2。在用戶登陸后,如果更改了瀏覽器的隱私級別,對于Firefox標準的瀏覽器,此時肯定不會再找到Cookie數組對象了,你需要做的僅僅只是將頁面調轉到登陸窗口;而在IE下就非常的麻煩了甚至無法解決,因為你依然可以訪問到原來的Cookie數組值,(比如,用IE在CSDN登陸后提高隱私級別,你依然可以登陸到其他的服務區域)此時沒有什么好的辦法,不過Google解決了這樣的問題,只是不知道如何解決的。



    IE在處理Frame標記的問題
    1。如果你在某個頁面中嵌入了Frame標簽,并且希望與這個嵌入的頁面共享某些存放在Session中的數據,此時你需要將外部的sessionId傳入到frame標記的頁面中。然而在IE中你可能并不能完全的享受這樣的邏輯,原因在于IE對于嵌入的頁面不會自動的傳遞sessionId,也許你碰巧可以,也許不行,也就是說完全是在IE的"掌控"之下。而在Firefox沒有出現這樣的情況。

    -解決
    為嵌入的頁面中所有的鏈接增加sessionId參數。


    最好的辦法就是:說服客戶使用標準的瀏覽器Firefox!

    posted @ 2006-07-01 15:53 killvin| 編輯 收藏

    在Struts中我們經常這樣循環的打印Message
    <logic:messagesPresent message="true">
    ? <html:messages id="msg" message="true">
    ??? <div class="success">
    ????? <bean:write name="msg"/>
    ??? </div><br/>
    ? </html:messages>
    </logic:messagesPresent>

    查閱struts tag的文檔我們看到了關于messagesPresent的message屬性注釋如下

    message

    By default the tag will retrieve the request scope bean it will iterate over from the Globals.ERROR_KEY constant string, but if this attribute is set to 'true' the request scope bean will be retrieved from the Globals.MESSAGE_KEY constant string. Also if this is set to 'true', any value assigned to the name attribute will be ignored


    也就是說在將message設置為true時,會去request中尋找Globals.MESSAGE_KEY所代表的bean,然而我們在具體的Action使用ActionMessages類的時候往往是這樣的
    ActionMessages messages = getErrors(request);
    messages.add(ActionMessages.GLOBAL_MESSAGE , new ActionMessage(key , value0));
    saveMessages(request,messages);

    而往往困擾人的就在于為什么要給messages中放入名稱為"ActionMessages.GLOBAL_MESSAGE"的ActionMessage對象,而且還需要再次的調用saveErrors(request,messages)方法?

    首先要說明的是你為ActionMessage起任何的名稱都沒有關系,因為ActionMessages本身維持著一個HashMap,而參數property就是這個HashMap中的key值,如果不存在則會建立相應的key,并將需要保存的ActionMessage對象存入到這個key所對應的List中。
    ??? public void add(String property, ActionMessage message) {

    ??????? ActionMessageItem item = (ActionMessageItem) messages.get(property);
    ??????? List list = null;

    ??????? if (item == null) {
    ??????????? list = new ArrayList();
    ??????????? item = new ActionMessageItem(list, iCount++, property);

    ??????????? messages.put(property, item);
    ??????? } else {
    ??????????? list = item.getList();
    ??????? }

    ??????? list.add(message);

    ??? }

    至于為什么一定要調用saveMessages(request,messages)?看看它具體的實現邏輯就清楚了
    ??? protected void saveMessages(
    ??????? HttpServletRequest request,
    ??????? ActionMessages messages) {

    ??????? // Remove any messages attribute if none are required
    ??????? if ((messages == null) || messages.isEmpty()) {
    ??????????? request.removeAttribute(Globals.MESSAGE_KEY);
    ??????????? return;
    ??????? }

    ??????? // Save the messages we need
    ??????? request.setAttribute(Globals.MESSAGE_KEY, messages);
    ??? }

    再對比前面介紹的messagesPresent標簽的使用,是不是就清楚了呢?原來它是將ActionMessages對象保存在request中,并且名稱是Globals.ERROR_KEY!從而為tag的順利解析鋪平了道路。當然按理你可以選擇將這樣的對象放置在任何的scope中,但Action只是提供了request , session兩種Scope(不過page , application不經常使用,可以理解,但不提供相應的結構就不太好了)

    至于messagesPresent標簽是如何在scope中尋找ActionMessages對象

    org.apache.struts.taglib.logic.MessagesPresentTag
    ??? protected boolean condition(boolean desired) throws JspException {
    ??????? ActionMessages am = null;

    ??????? String key = name;
    ??????? if (message != null && "true".equalsIgnoreCase(message)){
    ?????????? key = Globals.MESSAGE_KEY;
    ??????? }

    ??????? try {
    ??????????? am = TagUtils.getInstance().getActionMessages(pageContext, key);
    ???????????
    ??????? } catch (JspException e) {
    ??????????? TagUtils.getInstance().saveException(pageContext, e);
    ??????????? throw e;
    ??????? }

    ??????? Iterator iterator = (property == null) ? am.get() : am.get(property);

    ??????? return (iterator.hasNext() == desired);

    ??? }

    org.apache.struts.taglib.TagUtils
    ?? public ActionErrors getActionErrors(PageContext pageContext, String paramName)
    ??????????? throws JspException {

    ??????? ActionErrors errors = new ActionErrors();

    ??????? Object value = pageContext.findAttribute(paramName);
    ??????? if (value != null) {
    ??????????? try {
    ??????????????? if (value instanceof String) {
    ??????????????????? errors.add(
    ??????????????????????????? ActionMessages.GLOBAL_MESSAGE,
    ??????????????????????????? new ActionMessage((String) value));

    ??????????????? } else if (value instanceof String[]) {
    ??????????????????? String keys[] = (String[]) value;
    ??????????????????? for (int i = 0; i < keys.length; i++) {
    ??????????????????????? errors.add(
    ??????????????????????????????? ActionMessages.GLOBAL_MESSAGE,
    ??????????????????????????????? new ActionMessage(keys[i]));
    ??????????????????? }

    ??????????????? } else if (value instanceof ActionErrors) {
    ??????????????????? errors = (ActionErrors) value;

    ??????????????? } else {
    ??????????????????? throw new JspException(
    ??????????????????????????? messages.getMessage(
    ??????????????????????????????????? "actionErrors.errors",
    ??????????????????????????????????? value.getClass().getName()));
    ??????????????? }

    ??????????? } catch (JspException e) {
    ??????????????? throw e;

    ??????????? } catch (Exception e) {
    ??????????????? log.debug(e, e);
    ??????????? }
    ??????? }
    ??????? return errors;
    ??? }

    PageContext中的findAttribute會幫你在scope中尋找名稱為Globals.MESSAGE_KEY的ActionMessage對象。


    注意
    雖然Struts已經聲明:不推薦使用ActionErrors & ActionError對象,但在一些遺留的系統中,依然還是可以看到其影子,所以如果你的系統不幸屬于這樣的兩種混合系統,有以下的幾種方法可以參考
    1。兩次調用messagesPresent,如下
    <!-- Print ActionErrors Object -->
    <logic:messagesPresent>
    ? <html:messages id="msg" message="true">
    ??? <div class="success">
    ????? <bean:write name="msg"/>
    ??? </div><br/>
    ? </html:messages>
    </logic:messagesPresent>

    <!-- Print ActionMessages Object -->
    <logic:messagesPresent message="true">
    ? <html:messages id="msg" message="true">
    ??? <div class="success">
    ????? <bean:write name="msg"/>
    ??? </div><br/>
    ? </html:messages>
    </logic:messagesPresent>

    2.分別使用<html:messages> <html:errors>標簽,當然在老系統中需要調用Action的saveErrors方法,而在新的應用中要調用saveMessages方法。

    3.更換所有的ActionErrors為ActionMessages,并將所有調用saveErrors的地方更換成saveMessages,并將<html:errors>標簽相應的更換成<html:messages message="true"> - 推薦!

    ?

    posted @ 2006-06-27 23:02 killvin| 編輯 收藏

    第2章 并發問題及控制手段

    什么是并發問題?假設有這么一家書吧,顧客可以到那里喝茶讀書。顧客拿著選好要讀的圖書到柜臺登記,然后找個地方去閱讀,臨走時將圖書歸還店家。有一天,一個顧客相中了一本書后正要拿去登記,另一個顧客的手也抓住了這僅有的一本書,并發問題出現了。兩個顧客要讀同一本書,互不相讓,這讓店主傷透了腦筋。這個案例僅僅是眾多并發問題中的一個微小部分,但從中我們可以看出并發問題主要出現在多個用戶對有限資源進行訪問的時候,如果解決不好會直接影響系統的有效、正常運行。數據庫是一個共享的資源,并發問題的出現是必不可免的,如何識別并發類型并加以控制是這一章重點要講述的內容。

    本章將分成兩大部分,一部分主要講Visual FoxPro中并發控制機制。VFP中并發控制相對簡單,數據加鎖的形式比較單一,非常適合作為初步了解并發問題的切入點。第二部分以SQL Server 2000、ADO.NET以及C#為主要工具,深入了解并發一致性問題、封鎖協議、事務隔離等內容,難度相對較深。象一些更為深入的并發控制手段,例如多粒度封鎖和意象鎖等內容在本章中將不做深入討論,感興趣可以參考相關書籍。

    ?

    [實在不好意思COPY別人的成果,不過這篇文章出奇的精彩,將并發操作的來龍去脈說的清清楚楚,也是我正要找的,比JAVAEYE上面所謂的專家叫嚷著什么"悲觀鎖"、"樂觀鎖"而不解是原因要強的多!值得收藏]

    posted @ 2006-04-06 10:57 killvin| 編輯 收藏

    痛快!。。。人們總是認為罵人是沒有素質的象征,然而我到覺得要看對誰而言,如果你的面前是個謙虛、嚴謹而又懂得尊重別人的人,你根本就不可能讓自己憤怒,也不可能讓自己表現的很沒有素質;相反如果你的面前是個先入為主、自傲、不懂得尊重別人的人,你的憤怒就會在最短的時間內激發出來。-對于小人,就要罵!

    而這有與北大青鳥有什么關系呢?

    在我的職業生涯里,有一段讓自己憤怒的培訓經歷,到今天我都為自己在這樣的培訓部的學習經歷而感到恥辱!甚至不愿意在簡歷里提及ACCP的字樣,而這樣的培訓經歷就是在北大青鳥(西安兆?。├锒冗^的。

    我參加的是北大青鳥(西安兆隆)的第二期培訓,培訓的課程涉及到數據庫、編程語言、職業素質、項目實踐等,看上去非常的正規,可是真正的學習效果卻是讓人非常的失望的!

    首先,就是它的課程體系,完全的引入印度的教程,而翻譯的質量非常的差,其實這到沒有什么,只是教程的講解范圍和深度都非常的淺(這還是第二期的培訓),如果你只是按照它的教程我可以說你根本學不到任何有價值的東西,只是皮毛而已。-拿著淡薄的教學資料的同時你也就上了賊船了。


    再次,就是它的老師的水平實在不敢恭維,(當然并不是說根本就沒有好的老師,曾經就遇到過一個講解JAVA與UML的老師,水平非常的高深,可惜ACCP沒有留住),比如編程語言中學習了VB,僅僅只是這一門課程就換了3個老師,最后是一位號稱是西工大的博士上的課,然而這位博士顯然是心思不再這里,除了講解課本上已經有的代碼,根本不敢深入的講解原理,這門課程也就這樣混過去了。-如果這樣學習編程語言道不如自學來得輕松。


    還有,就是所謂的項目實踐根本就不是老師帶的項目,而是一些書本上或者捏造出來的一些項目,比如圖書管理系統等,如果學員無法真正的理解需求,怎么去理解設計,又如何來進行實際的編碼呢?如果遇到圖書管理的專業問題來問誰呢?-別指望在他們宣傳的所謂的項目實踐中得到鍛煉。

    在看看他們引以為傲的工作分配問題吧?就我個人的親身感受來言,這里會提供一些工作的機會,但都是一些小公司,而且往往是一些前三個月不給工資的公司;當時我在我們班級里是比較認真的一個(我是班里唯一的一個交了對項目分析文檔的學員,并且是項目實踐中的小組組長),可是在一次很好的面試機會面前,她們竟然由于工作的失誤忘記了給我打電話??。绻@樣的工作分配策略也算作是給予你的答案的話,你一定會變得和我一樣的憤怒!


    我沒有在ACCP學到任何的有用的東西,然而在真實的世界里卻學到了人們對于ACCP的看法,很多的技術經理 、 技術總監從骨子里非常的厭惡從北大青鳥出來的學生,對于他們開設的一些課程也是相當的反感,當時在西部世紀工作的時候,同組的就有和我一起上課的同班同學,在他交了8000多元的學習費用(總共的費用接近1萬多元)后,得到的僅僅只是西部世紀的一張試用通知單,而條件是必須無條件的為公司工作三個月!


    這就是我在北大青鳥的學習經歷,然而憤怒的不是這個,卻是今天參加的面試經歷,對照著自己的個人的工作經歷我希望自己能夠成為一名培訓部的老師,教給那些原來如我一樣的學生一些在實踐當中有用的東西,而不是空洞的理論!所以我給ACCP投了自己的簡歷,然而讓我卻又一次的遇到了那個欺騙過我的咨詢師,不過她的記憶力也是非常的好,一眼就認出了我(當然了我是曾經給她帶來獎金的人),在緊接著的面試過程中已經變了味道,談論的話題也不是具體項目技術的問題,(不是我笑話她與她談論技術問題根本就如同對牛彈琴)她顯然是一個沒有素質的WOMAN,也可以肯定的是她根本沒有在外企工作過的經歷,所以說話的詞匯以及與其如同大街上的潑婦一般,可以想見她一定是一個被ACCP嚴重洗過腦的人,而且是屬于那種技術失敗的產品,她無法接受別的培訓部的名字,一旦你提起別的培訓部的某個老師的水平如何如何的好,她就如同一個快被點燃的鞭炮一般,兩眼充滿了憤怒!

    然而讓我最終和她一般的沒有素質的大罵的是她對于我辭職的評點,我很納悶的是她在不知道一件事情的原因的情況下,可以講述自己的觀點!并且是帶著教育的口吻,實在是人才呀!這就是北大青鳥的"人才力量",對于這樣的白癡級別的任務的觀點,我只好打斷,因為它甚至連我在的那個單位的名字都不知道?。?br />
    實在是一場鬧劇,而她更像是個小丑,除了唧唧喳喳的重復一些"洗腦"的詞匯已經不知道窗外的天氣了,可以預料北大青鳥的牌子必將會毀在這些個白癡的手中,世界需要白癡,那如同鮮花需要牛糞一樣,我們改變不了白癡的世界觀的,你能夠做的就是別被這臭烘烘的牛糞給熏臭了。

    posted @ 2006-04-01 12:53 killvin| 編輯 收藏


    [深入的思考] 為什么要采用java這個平臺?


    從開發項目的類別角度看java平臺

    基于B/S結構的系統,在這個方向上的競爭是激烈的,有專注于此的LAMP(Linux + Apache + Mysql + Php);也有剛剛興起的Rails(Ruby Frameworks)甚至是號稱快速開發的ASP.NET;當然了java在這個領域里的MVC框架數都數不完,比如Struts . Webwork等,然而即便是如此,選擇java作為開發的理由也是不充分的,因為在這個梯隊里java頂多排名最后。

    基于C/S結構的系統,在這個方面java顯然沒有考慮周到,面對VB 、DELPHI、vc這些個如狼似虎的快速開發IDE,JAVA實在是顯得異常的淡薄,即使你找到了一個可以匹敵這些個ide的工具,面對第三方的組件又會成為一大障礙,所以java在這個方面又一次的輸了。


    從java所強調的特性角度看java平臺

    java的重點是業務邏輯?。ㄎ乙郧耙彩侨绱藞孕挪灰疲┛墒钦l有能夠說別的語言不注重業務邏輯呢,業務邏輯只是一個抽象的概念,java只是依靠ejb提出了業務組件而已,其他的語言在實現業務邏輯的時候也可以包裝成POJO的形式,看來這個觀點也是失敗的。

    java強調的是跨平臺的優勢!這可以理解為初級的、商業的、忽悠人的詞匯,面對眾多動態語言如Python,在若干平臺上的表現,java又如何來強調自己這方面的優勢呢?失敗

    java支持分布式應用的項目!可笑的言論,分布式根本不是值得炫耀的資本,在java之前的c/s項目中何嘗不是分布式的應用呢?失敗


    既然沒有了這些個優勢,我們看看java到底還剩下些什么?對了其實就是應用服務器!然而看過J2EE WITHOUT EJB的讀者肯定知道Spring所希望達到的目的,也就是脫離應用服務器概念上的J2EE體系實現,既然在作者的眼里APPLICATION SERVER只不過是一個忽悠人的詞匯,那么任何項目都選擇java作為開發的依據顯然就是自找苦吃,

    那么什么情況下改選擇java作為開發的平臺呢?
    <1> 如果你真的遇到了大型的系統開發任務,恭喜你,你終于可以看到分布式對象、集群的優勢了。
    <2> 客戶是一個java的忠實fans或者是sun、ibm的金牌合作伙伴之類的,選擇java是不得已的,但記住并不能證明java是最好的實現方式
    <3> 如果你只想關心業務邏輯的實現,對于事務、緩存、查找等服務的實現沒有興趣的話,倒是不妨考慮采用ejb的形式,當然前提是你不愿意在尋找合適的替代品的情況下。
    <4> 如果項目迫切的尋找某種框架的支持,選擇java就是對的,你有眾多優秀的、免費的、可擴展的、天才的框架可以選擇,更多的時候你是出于尷尬的境地,因為任何一個都讓你心動、而這樣的選擇往往是最痛苦、和快樂的。


    正確的選擇
    <1>
    條件: 如果項目僅僅只是一個小型的網站系統
    選擇: LAMP、Rails

    <2>
    條件: 項目規模中等
    并且項目的時間比較緊,
    項目可以架構在windows的系統之上,
    選擇: .Net? / Delphi

    <3>
    條件: 大型的系統,有支持分布式對象、集群的要求;或者SUN / IBM的金牌合作伙伴 ; 想要尋找某種優秀的框架來解決問題
    選擇: java是不二的選擇,可是我想問一下,在現實中你能遇到這樣的項目嗎?

    所以,從實際的角度出發,我們面對的99%可能都是一些符合條件1,2的系統,而選擇java實在是得不償失的。最后以一段Code Complete中的話來作為結束語

    每個程序員都有很多的工具,但并不存在任何一個能夠適用于所有工作的工具,因地制宜的選擇正確工具是成為能有效編程的程序員的關鍵。

    posted @ 2006-03-29 13:49 killvin| 編輯 收藏

    ?

    作者簡介
    XincChen是Xtremework公司創始人.a自從.NET推出以來,1他已使用.NET幫助很多行業的用戶開發了體現其商業理念的軟件產品.aXincChen是.NET和EAI方面的專家,1他與Microsoft和Accenture等多家技術領先的公司合作,1為它們的客戶提供了優秀的解決方案.a在工作之余,1他喜歡讀書.c寫書.c以及靜靜地休息.aApress出版社的另一本書——《BizTalkc2002cDesigncandcImplementation》——也是出自他的筆下.aXincChen擁有哥倫比亞大學的統計學文科碩士學位,1現居國內,1正在籌建公司在北京的研發中心.a可通過xchen@Xtremework.com和作者取得聯系.

    個人評論

    這本書本來購買時的期望值非常的高,想必是一本細數.NET框架類的圖書,結果卻大失所望,而這本書也就因為其技術上的薄弱而消弱了其價值的表現力,不過作為一本.NET入門級的圖書還是比較的合適,但在CSDN上竟然有四顆半的星的待遇實在不敢恭維。

    而且作者的代碼顯然非常的粗造,竟然連起碼的業務級數據校驗都沒有,在一些功能的分解上也是抹漿糊,可以想象作者并沒有深入的思考而僅僅是去實現功能而已,猜想這樣的框架很可能是作者對于以前工作的簡單抽象而已。

    整體上本書除了第一章應用框架介紹 、 第二章應用框架解析比較的出彩,尤其是創造性的提出了業務假設(Business assumption)的概念還比較的有深度,其他的章節只是對于其SAF框架的介紹,但在技術實現上只是對于.NET的若干相關服務的簡單包裝而已,對于已經在JAVA世界里的人來說,SAF框架其實就是Spring框架在.NET世界的簡單實現版本,但相對于Spring的技術實力,作者還需要努力才行,不過作者顯然是.NET方面的高手,對于.NET世界提供的雜亂無章的服務特性以及服務的原理非常的清楚,這也難怪在這樣的平臺上再去"多此一舉"的開發另外的框架,無論從技術上還是推廣上都非常的難!不過領略一下..NET的秀發也是多少有所收獲的 :)

    再次強調這本書僅僅只能夠算作.NET方面的初級讀物,個人評星3.5 ,請朋友們謹慎購買。

    posted @ 2006-03-24 14:22 killvin| 編輯 收藏

    wfc是構建在B/S結構上的流程定義工具

    具備以下的功能
    1> 實現了B/S結構上的工作流定義工具(沒有看到同類型的產品)。
    2> 流程定義格式與具體的工作流格式相分離,并可以在此基礎上實現其他的流程定義工具產品。
    3> 采用了Buffalo(XML-RPC的javascript實現)實現與后端Servlet的綁定。
    4> 可以對具體的節點進行屬性的配置(配置后的數據會被綁定為java的List自動傳遞到后端)。

    目前還需要改進的
    1> 修改目前工作流的圖標(需要分支、狀態、合并這樣專業的圖標)
    2> 解決瀏覽器的刷新問題。
    3> 增加線段的表現形式選項
    4> 增加曲線的表現形式,尤其是在多線段的情況下要自動調整為曲線的形式。


    目前的WFC已經被Michael Chen(buffalo的作者)收錄在其網站上!地址如下
    http://demo.amowa.net/wfc

    對于buffalo的關注早在作者剛發布的時候就已經開始了,起初不太了解buffalo的作用,然而在具體的工作中尋找基于XML-RPC協議的產品時突然明白了它的價值,不過說簡單一點buffalo是實現了基于javascript的對象的序列化以及反序列化工作。

    posted @ 2006-03-21 17:58 killvin| 編輯 收藏

    通用類圖書

    卓越獎: Prefactoring by Ken Pugh (O'Reilly)

    生產力獎:
    Innovation Happens Elsewhere: Open Source as Business Strategy by Ron Goldman, Richard P. Gabriel (Morgan Kaufmann)
    Producing Open Source Software: How to Run a Successful Free Software Project by Karl Fogel (O'Reilly)
    The Art of Project Management by Scott Berkun (O'Reilly)

    技術類圖書

    卓越獎:Agile Web Development with Rails by Dave Thomas, David Hansson, Leon Breedt and Mike Clark (Pragmatic Bookshelf)

    生產力獎:
    Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries by Krzysztof Cwalina and Brad Abrams (Addison-Wesley)
    Practical Common Lisp by Peter Seibel (Apress)
    Why Programs Fail: A Guide to Systematic Debugging by Andreas Zeller (Morgan Kaufmann)

    企業項目管理

    卓越獎: WelcomRisk 2.6 (Welcom)

    生產力獎:
    Corticon Business Rules Management 4.0 (Corticon)
    JBoss 2 Portal (JBoss)
    Visual Studio Team System 2005 (Microsoft)


    數據庫引擎和數據工具

    卓越獎: Microsoft SQL Server 2005 (Microsoft)

    生產力獎:
    Berkeley DB 4.4 (Sleepycat Software)
    Google Maps API 2005 (Google)
    MySQL 5.0 (MySQL)

    缺陷跟蹤,變更和配置管理

    卓越獎: Perforce SCM 2005 (Perforce)

    生產力獎:
    FogBugz 4.0 (Fog Creek)
    Guiffy SureMerge 7.0 (Guiffy Software)
    JIRA 3.4 (Atlassian Software)

    設計和建模工具

    卓越獎:Lattix LDM 2.0 (Lattix)

    生產力獎:
    Borland Together 2006 for Eclipse (Borland)
    Enterprise Architect 6.0 (Sparx Systems)
    MindManager Pro 6.0 (Mindjet)

    開發環境

    卓越獎: Visual Studio Team System 2005 (Microsoft)

    生產力獎:
    Eclipse SDK 3.1 (Eclipse.org)
    IntelliJ IDEA 5.0 (JetBrains)
    Komodo 3.5 (ActiveState)

    類庫、框架和組建

    卓越獎: .NET Framework 2.0 (Microsoft)

    生產力獎:
    Dundas Chart for .NET 5.0 (Dundas Software)
    Qt 4.0 (Trolltech)
    Spring Framework 1.2.6 (SpringFramework.org)

    移動開發工具

    卓越獎: Crossfire 5.6 (AppForge)

    生產力獎:
    Carbide.c++ Express (Nokia)
    Flash Lite 2.0 (Adobe)
    NetBeans IDE 4.1 (Sun Microsystems)

    項目質量管理

    卓越獎: Rally 5.6 (Rally Software Development)

    生產力獎:
    CollabNet Enterprise Edition with Project Dashboard/Task Management 2005 (CollabNet)
    QACenter Enterprise Edition 5.1 (Compuware)
    TargetProcess Suite 1.4 (TargetProcess)

    安全工具

    卓越獎: Elemental Compliance System 1.4 (Elemental)

    生產力獎:
    CodeAssure 2.0 (Secure Software)
    DevPartner SecurityChecker 1.0 (Compuware)
    Fortify Security Tester 1.0 (Fortify)

    測試工具

    卓越獎:VMTN Subscription 2005 (VMware)

    生產力獎:
    Agitator 3.0 (Agitar Software)
    AQtime 4.7 (AutomatedQA)
    Clover 1.3 (Cenqua)

    實用程序

    卓越獎:Camtasia Studio 3.0 (TechSmith)

    生產力獎:
    DevPartner Studio 8 (Compuware)
    Fog Creek Copilot 1.2 (Fog Creek Software)
    ReSharper 1.5 (JetBrains)

    Web開發工具

    卓越獎: Rails 1.0 (rubyonrails.org)

    生產力獎:
    JBoss Application Server 4x (JBoss)
    Macromedia Studio 8 2005 (Adobe)
    Zend Studio - Enterprise Edition 5.0 (Zend)

    名人堂產品獲獎者:

    ?Visual Studio Professional Edition (Microsoft)

    感慨
    ??? MS在這次的JOLT中屢獲殊榮,深思。。。

    posted @ 2006-03-19 09:32 killvin| 編輯 收藏

    很久都不罵人了,可是一不留心又看到了他寫了惡心文章,實在是讓人憤慨!這位板橋里人的"大名"是從透明的blog上知道的,首先就是感覺這個人的腦子有點問題!不去評論他發布的那個所謂的框架,就看看他寫的一些文章就足以知道這個人腦袋非常的混亂,在這樣混亂的情況下寫出來的文章也就可想而知了,最近這位所謂的"大俠"又開始害人了

    你還在用if else嗎?- http://www.jdon.com/artichect/ifelse.htm


    面向過程設計和面向對象設計的主要區別是:是否在業務邏輯層使用冗長的if else判斷。如果你還在大量使用if else,當然,界面表現層除外,即使你使用Java/C#這樣完全面向對象的語言,也只能說明你的思維停留在傳統的面向過程語言上。

    -我很納悶了作者怎么可以從是否使用if else來判斷一個設計是否符合OO特性呢?我們看到的if else屬于語言這個層次的東西,而if else僅僅是完成邏輯判斷的語句;如果說作者的這個觀點成立的話,java、c#的語言發明者應該早就明白或者預測到if else 的"不OO"的特性,也會考慮到在語言層面刪除這樣的邏輯判斷語句,但事實呢?發明者非但沒有刪除相反其他語言的發明者也一起發明了if else語句?!難道是大師們錯了?

    還是以大家熟悉的論壇帖子為例子,如ForumMessage是一個模型,但是實際中帖子分兩種性質:主題貼(第一個根貼)和回帖(回以前帖子的帖子),這里有一個樸素的解決方案:
    建立一個ForumMessage,然后在ForumMessage加入isTopic這樣判斷語句,注意,你這里一個簡單屬性的判斷引入,可能導致你的程序其他地方到處存在if else 的判斷。

      如果我們改用另外一種分析實現思路,以對象化概念看待,實際中有主題貼和回帖,就是兩種對象,但是這兩種對象大部分是一致的,因此,我將ForumMessage設為表達主題貼;然后創建一個繼承ForumMessage的子類ForumMessageReply作為回帖,這樣,我在程序地方,如Service中,我已經確定這個Model是回帖了,我就直接下溯為ForumMessageReply即可,這個有點類似向Collection放入對象和取出時的強制類型轉換。通過這個手段我消滅了以后程序中if else的判斷語句出現可能。

    -作者在這里似乎列舉了一個例子,可是對于帖子和回帖這樣簡單的問題,只要遵守OO的設計師都會抽象出一個帖子的父類,然而這又能說明什么呢?在具體的業務邏輯中難道你不判斷傳遞過來的對象的類別?(現在主題貼與回帖的處理方法是不同的),同樣你無法避免在具體的編碼中使用if else的可能?!


    最后總結:將if else用在小地方還可以,如簡單的數值判斷;但是如果按照你的傳統習慣思維,在實現業務功能時也使用if else,那么說明你的思維可能需要重塑,你的編程經驗越豐富,傳統過程思維模式就容易根深蒂固,想靠自己改變很困難;建議接受專業頭腦風暴培訓。

     用一句話總結:如果你做了不少系統,很久沒有使用if else了,那么說明你可能真正進入OO設計的境地了。(這是本人自己發明的實戰性的衡量考核標準)。

    -顯然作者并不是去討論if else的語言問題,而是為自己的"洗腦培訓"打廣告!并講這樣的問題上升到設計模式、禪的境界,可謂是煞費苦心呀,沒有人說設計模式不好也沒有人懷疑禪的境界的高深,但不是作者這樣的人靠讀一兩篇文章或者發布個所謂的"毫無實際意義"的框架就可以領悟到的,還是那句話:長得丑不要緊,不要出來嚇人!不過我還要補充一句就是,不懂不要緊,不要亂說免得害人(因為我們都知道潑婦罵街的道理,雖然沒文化但確實能夠帶來眼球效應)。

    posted @ 2006-03-10 11:49 killvin| 編輯 收藏

    該死,鼠標癱瘓了!
    本來以為是Maxthon的問題(在鼠標單擊的時候會關閉一些窗口,并且是隨機的??。┻€就這樣的問題給作者寫了一封信?汗。。。經過仔細的尋找原因,原來是自己的鼠標的問題(這個鼠標跟了我快7年了,Logitech最好的滾軸鼠標,要300多塊大洋)有點舍不得,又沒有辦法,該下崗了,一同下崗的也許還有我的鍵盤(TCL的手感級差!:( )

    不過為了找原因我倒是廢了一番周折,下載和試用了N多的瀏覽器,也看了很多網上的評論對比文章,現在主流的瀏覽器主要就是Opera , Firefox, Maxthon , GreenBrowser , MyIE , TheWorld 等,然而在資源的利用率上這些個產品都非常的讓人失望,難道就沒有一款適合于程序員的胃口的瀏覽器?!

    可喜的是偶發現了GOSURF這款瀏覽器,在眾多的瀏覽器中它的內存占用率是最低的,而且它的作者似乎在這方面也是煞費苦心,"一味"的強調速度,甚至對于鼠標手這個及其流行的功能的加入也是非常的謹慎,可以看出作者是個很有個性的程序員,值得期待!

    如果你和我一樣對資源的利用率近乎苛刻,可以考慮這樣的一款產品 http://gosurfbrowser.com/?ln=cn

    posted @ 2006-03-07 17:42 killvin| 編輯 收藏

    不錯的文章
    http://www-128.ibm.com/developerworks/cn/java/j-junit4.html

    JUnit 是 Java? 語言事實上的 標準單元測試庫。JUnit 4 是該庫三年以來最具里程碑意義的一次發布。它的新特性主要是通過采用 Java 5 中的標記(annotation)而不是利用子類、反射或命名機制來識別測試,從而簡化測試。在本文中,執著的代碼測試人員 Elliotte Harold 以 JUnit 4 為例,詳細介紹了如何在自己的工作中使用這個新框架。注意,本文假設讀者具有 JUnit 的使用經驗。

    JUnit 由 Kent Beck 和 Erich Gamma 開發,幾乎毫無疑問是迄今所開發的最重要的第三方 Java 庫。正如 Martin Fowler 所說,“在軟件開發領域,從來就沒有如此少的代碼起到了如此重要的作用”。JUnit 引導并促進了測試的盛行。由于 JUnit,Java 代碼變得更健壯,更可靠,bug 也比以前更少。JUnit(它本身的靈感來自 Smalltalk 的 SUnit)衍生了許多 xUnit 工具,將單元測試的優勢應用于各種語言。nUnit (.NET)、pyUnit (Python)、CppUnit (C++)、dUnit (Delphi) 以及其他工具,影響了各種平臺和語言上的程序員的測試工作。

    然而,JUnit 僅僅是一個工具而已。真正的優勢來自于 JUnit 所采用的思想和技術,而不是框架本身。單元測試、測試先行的編程和測試驅動的開發并非都要在 JUnit 中實現,任何比較 GUI 的編程都必須用 Swing 來完成。JUnit 本身的最后一次更新差不多是三年以前了。盡管它被證明比大多數框架更健壯、更持久,但是也發現了 bug;而更重要的是,Java 不斷在發展。Java 語言現在支持泛型、枚舉、可變長度參數列表和注釋,這些特性為可重用的框架設計帶來了新的可能。

    JUnit 的停滯不前并沒有被那些想要廢棄它的程序員所打敗。挑戰者包括 Bill Venners 的 Artima SuiteRunner 以及 Cedric Beust 的 TestNG 等。這些庫有一些可圈可點的特性,但是都沒有達到 JUnit 的知名度和市場占有份額。它們都沒有在諸如 Ant、Maven 或 Eclipse 之類的產品中具有廣泛的開箱即用支持。所以 Beck 和 Gamma 著手開發了一個新版本的 JUnit,它利用 Java 5 的新特性(尤其是注釋)的優勢,使得單元測試比起用最初的 JUnit 來說更加簡單。用 Beck 的話來說,“JUnit 4 的主題是通過進一步簡化 JUnit,鼓勵更多的開發人員編寫更多的測試?!盝Unit 4 盡管保持了與現有 JUnit 3.8 測試套件的向后兼容,但是它仍然承諾是自 JUnit 1.0 以來 Java 單元測試方面最重大的改進。

    注意:該框架的改進是相當前沿的。盡管 JUnit 4 的大輪廓很清晰,但是其細節仍然可以改變。這意味著本文是對 JUnit 4 搶先看,而不是它的最終效果。

    posted @ 2006-03-05 13:10 killvin| 編輯 收藏

    http://blog.raylife.com/?p=336

    在這個暖暖的午后享受著陽光的溫暖的同時,聽著CUSCO 的專輯實在是一種享受,我喜歡那種寧靜的音樂,因為它帶給你的除了純凈還是純凈。。。。

    不想被任何的人打擾,城市的喧鬧讓自己的思想變得復雜,而唯獨音樂才能夠真正的喚醒你心靈深處的東西,

    posted @ 2006-03-04 13:23 killvin| 編輯 收藏

    目前在找工作階段,參加了一些公司的面試,對于目前的一些公司的面試官我真的要說上兩句,比如我去一家"非常著名"(至少在程序界是比較有名氣的)的公司去面試,在回答一些無關痛癢的自我介紹后,技術總監突然問我"Struts是如何解決多配置文件的問題的?",當時就把我給問住了,主要是自己已經很久都沒有做項目了,對于Struts也僅僅停留在使用這樣的基礎上,況且當時接觸到的Struts是不支持多配置文件的,所以我只好亂說“將配置文件放在classpath就行了”,顯然這樣的答案是技術總監不愿意聽到的,(想必很多的程序員也會這樣認為),可是事情真的就是這樣簡單嗎?

    標準答案是:在web.xml中配置ActionServlet的config參數,在經過大腦短暫的思考后我斷定我的回答并沒有錯!

    誰說一定要配置這樣的參數?那只是ActionServlet的"一廂情愿",如果有一天Struts不高興了,將尋找配置文件的策略更改成:從classpath尋找,你認為我說的答案還是錯誤的嗎?

    所以我想說這樣的面試問題是弱智的,一個人不可能知道所有的一切,對于現在這個社會如此眾多的框架產品你怎么可能光靠某個問題就斷定一個人的能力呢?

    其實如果他可以這樣問:你認為配置文件的讀取策略可以有多少種?或者如果是你設計Struts你會如何解決多配置文件的問題?這樣啟發式的詢問更會激起面試者的興趣,難道這些個所謂的技術總監不該反思一下?

    當然,不是僅僅一個這樣的事件引起了自己點憤怒,而是很多很多的所謂的技術專家的弱智問題,不知道磨滅了多少人的熱情!如果你也是遇到了這樣的尷尬,索性問他一個自己非常另類的問題-比如"內部類如何訪問外部類的對象?"

    該死,請不要再問我弱智的問題了

    posted @ 2006-03-03 17:53 killvin| 編輯 收藏

    在OSWorkflow中最讓人惱火的就是它的接口定義!我會就這些接口的混亂展開一系列的分析,今天先說說Configuration接口

    偶繼承了它的Configuration接口

    import com.company.engine.workflow.store.IWorkFlowStore;
    import com.opensymphony.workflow.StoreException;
    import com.opensymphony.workflow.config.Configuration;
    import com.opensymphony.workflow.spi.WorkflowStore;

    public interface IConfiguration extends Configuration
    {
     /**
      * @deprecated getIWorkflowStore()
      */
        WorkflowStore getWorkflowStore() throws StoreException;
       
        /**
         * return WorkFlowStore which implements the interface of IWorkFlowStore
         * @return
         * @throws StoreException
         */
        IWorkFlowStore getIWorkflowStore() throws StoreException;
       
    }

    你可能奇怪我為何要繼承它的接口(肯定是Bad smell),原因如下,

    IWorkFlowStore 接口定義

    import com.opensymphony.workflow.StoreException;
    import com.opensymphony.workflow.spi.Step;
    import com.opensymphony.workflow.spi.WorkflowEntry;
    import com.opensymphony.workflow.spi.WorkflowStore;

    public interface IWorkFlowStore extends WorkflowStore
    {
     
     public Step createCurrentStep(WorkflowEntry _entry , Step _step) throws StoreException;

    }

    WorkflowStore接口定義

        /**
         * Persists a step with the given parameters.
         *
         * @param entryId The workflow instance id.
         * @param stepId the ID of the workflow step associated with this new
         *               Step (not to be confused with the step primary key)
         * @param owner the owner of the step
         * @param startDate the start date of the step
         * @param status the status of the step
         * @param previousIds the previous step IDs
         * @return a representation of the workflow step persisted
         */
        public Step createCurrentStep(long entryId, int stepId, String owner, Date startDate, Date dueDate, String status, long[] previousIds) throws StoreException;

    看到了吧?

    其實我只是希望在createCurrentStep時按照OO的方法執行,而不是傳遞那些"Bad Smell"的參數,而OSWorkflow中的WorkflowStore是需要Configuration來獲取的,此時為了增加一個看似合理的方法,需要分別繼承Configuration與WorkflowStore;這還沒有完,你需要實現一個Configuration實現?。?/P>

    import com.company.engine.workflow.store.IWorkFlowStore;
    import com.opensymphony.workflow.StoreException;
    import com.opensymphony.workflow.config.DefaultConfiguration;
    import com.opensymphony.workflow.spi.WorkflowStore;

    public class DefaultIConfiguration extends DefaultConfiguration implements IConfiguration
    {
     
        public static DefaultIConfiguration INSTANCE = new DefaultIConfiguration();
        private transient IWorkFlowStore store = null;

     /**
      * @deprecated getIWorkflowStore()
      */
     public WorkflowStore getWorkflowStore() throws StoreException
     {
      return null;
     }

     public IWorkFlowStore getIWorkflowStore() throws StoreException
     {
      if (store == null)
      {
       String clazz = getPersistence();

       try
       {
        store = (IWorkFlowStore) Class.forName(clazz).newInstance();
       }
       catch (Exception ex)
       {
        throw new StoreException("Error creating store", ex);
       }

       store.init(getPersistenceArgs());
      }

      return store;
     }

    }

    總結

    1。OSWorkflow與WorkflowStore接口的關系比較的微妙,它需要借助于Configuration接口的實現來獲取到實際的WorkflowStore對象。

    2。由于這樣的一種微妙關系,對WorkflowStore接口的擴展必將連帶著需要擴展Configuration接口,而產生這樣的"果凍效應"的罪魁禍首就是由于WorkflowStore接口與Configuration接口耦合的太緊。

    3。OSWorkflow并沒有很好的遵守OO的設計規則,尤其在它的參數傳遞上,非常的差!

    posted @ 2006-03-02 21:04 killvin| 編輯 收藏

    Workflow Project 目前狀態
    版本 0.11

    已經完成
    1。完成了接口1 和接口2 的方法
    2。完成接口3的默認實現
    3。完成事務回滾的實現方法-等待測試

    未完成
    1。接口3的注冊與實例化解決方案
    2。應用的并發訪問問題以及解決數據的臟讀問題
    3。與具體的某個應用掛接并測試


    -事務的回滾
    OSWorkFlow的事務回滾是依靠WorkflowContext這個接口來實現的,在New出某個WorkFlow的時候需要聲明WorkflowContext的實現類,一般會采用uper.context = new GearWheelWorkFlowContext(_caller);方法
    比如這樣實現:

    public GearWheelWorkFlow(String _caller)
    {
    super.context = new GearWheelWorkFlowContext(_caller);
    }

    但OSWorkFlow的WorkflowContext的默認實現BasicWorkFlowContext中根本沒有實現setRollbackOnly方法,也就沒有了參考的可能

    再看看這個接口的其他實現類也都是建立在JTA這樣的跨Session的事務服務上,比如它的EJB的實現也是要調用容器提供的JTA實現才行!而JTA的實現比如要JNDI到數據庫池,此時的應用光JTA+JNDI就已經宣布 -這樣的例子必須生存在應用服務器的環境下!!

    可是,我不死心,我記得Hibernate可以實現本地事務,也就是依靠JDBC本身的事務處理能力,而要實現這樣的功能就需要在數據庫連接的獲取上下一些功夫,也就是要保證回滾的數據庫連接必須是獲取時的那個連接,而存儲連接就成了一個需要首先解決的問題。

    解決數據庫連接的存儲問題
    目前存儲數據庫連接除了依靠靜態類外,還有一個通用的方法ThreadLocal類,這樣獲取數據庫連接的方法寫成了如下的形式:

    package com.company.common;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import org.apache.log4j.Logger;
    public class DB2ConnectFactory
    {
    /**
    * Logger for this class
    */
    private static final Logger logger = Logger.getLogger(DB2ConnectFactory.class);
    private static ThreadLocal threadLocal = new ThreadLocal();
    //~
    private Connection connect = null;
    private Statement state = null;
    private ResultSet result = null;
    private boolean closeConnWhenDone = false;
    //~
    private String url = "jdbc:db2:WORKFLOW";
    private String user = "";
    private String password = "";
    private String driverClassName = "COM.ibm.db2.jdbc.app.DB2Driver";

    public DB2ConnectFactory() throws SQLException
    {
    this.init();
    }

    /**
    * 獲取數據庫連接
    * @return
    * @throws SQLException
    */
    public Connection getConn() throws SQLException
    {
    return (Connection)threadLocal.get();
    }

    /**
    * 初始化數據庫,并在緩沖中注冊數據庫連接
    * @throws SQLException
    */
    private void init() throws SQLException
    {
    try
    {
    // Get connect object
    Class.forName(driverClassName);
    closeConnWhenDone = true;

    connect = DriverManager.getConnection(url, user, password);
    state = connect.createStatement();

    //Register the connection object in the threadlocal
    threadLocal.set(connect);
    }
    catch (Exception e)
    {
    e.printStackTrace();
    throw new SQLException(e.getMessage());
    }
    }

    }

    解決事務回滾
    剛才說了需要實現WorkflowContext接口
    package com.company.engine.workflow;
    import java.sql.Connection;
    import java.sql.SQLException;
    import org.apache.log4j.Logger;
    import com.company.common.DB2ConnectFactory;
    import com.opensymphony.workflow.WorkflowContext;
    public class GearWheelWorkFlowContext implements WorkflowContext
    {
    /**
    * Logger for this class
    */
    private static final Logger logger = Logger.getLogger(GearWheelWorkFlowContext.class);

    private static ThreadLocal threadLocal = new ThreadLocal();

    // ~ Instance fields
    // ////////////////////////////////////////////////////////
    private String caller;
    // ~ Constructors
    // ///////////////////////////////////////////////////////////
    public GearWheelWorkFlowContext(String caller)
    {
    this.caller = caller;
    }
    // ~ Methods
    // ////////////////////////////////////////////////////////////////
    public String getCaller()
    {
    return this.caller;
    }
    /**
    * Tranaction : Set Roll back
    * @throws SQLException
    */
    public void setRollbackOnly()
    {
    Connection connect = null;
    try
    {
    DB2ConnectFactory factory = new DB2ConnectFactory();
    connect = factory.getConn();
    if(connect != null) connect.rollback();
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    finally
    {
    this.clostConnection(connect);
    }
    }

    private void clostConnection(Connection connect)
    {
    try
    {
    if(connect != null) connect.close();
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    }
    }

    總結
    1。我們可以看到由于接口中setRollbackOnly沒有異常的聲明,方法中即使拋出了異常也要自己"忍了"!看來良好的接口聲明其實是非常重要的。

    2。而且需要重載原來JDBCWorkflow 中的cleanup方法,將其中的代碼屏蔽掉!數據庫的關閉放在了setRollbackOnly訪訪的finally中,原因就是由于我們要統一的管理數據庫連接所引發的,我們不能夠在WorkFlowStore的每一個方法執行完畢后就關閉連接,因為這樣的話你根本沒有了事務回滾的可能,所以此時的連接需要在WorkflowContext中來處理。


    感觸
    OSWorkFlow的實現方法并不是像網上所說的那樣的優秀和文雅,更像是一個未完成任務的"半成品",Heni被網上鼓吹為大牛,但一個不寫注釋和文檔的人,根本稱不上什么大牛!
    OSWorkFlow更多的是實現了一個微內核,而它的用戶模式是與OSUser這樣的框架耦合的(偶已經將這樣的耦合打開了,也就是接口3的定義),它的相關數據是與PropertySet框架耦合的(也就是接口2的定義),而且采用OSWorkFlow要經過很原始的修改(比如我實現了DB2下的WorkFlowStore的實現)。

    不過也好即使以后不采用OSWorkFlow,自己實現一個這樣的引擎也應該沒有什么問題的,有時間了我倒是很想看看別的工作流的產品。

    posted @ 2006-03-02 21:03 killvin| 編輯 收藏

    workflow接口劃分

    1。應用接口 Application Interface
    --interface1 工作流自身提供的服務接口
    --interface2 工作流與應用之間的接口(主要是提供相關數據的調用接口)

    2。擴展接口 PlugIn Interface
    --interface3 工作流與組織機構之間的接口
    --interface4 工作流與其他工作流之間的接口

    將接口劃分成應用接口與擴展接口主要是依據工作流與相關應用的調用關系,比如工作流與組織機構之間,是工作流調用組織機構中的人員信息,所以主動者是WORKFLOW、被動方是組織機構,所以應該采用擴展接口來實現

    在擴展接口上應該采用Adapter模式,從而使工作流不局限于某個特定的實現

    目前的進展
    0。Application Interface接口已經基本實現了
    PlugIn Interface接口目前基本完工,但OSWorkflow的實現實在是非常的丑陋,需要更改的地方太多,而且對于Interface3不可以使用它采用的User / Group模型(而且它使用了OSUser這個框架,對于多數的應用程序基本可以說不適合,而且它的User類竟然是Final ?!而且我發現它的很多類的屬性都是Protected!也就是說除了他們自己根本沒有辦法擴展,即使擴展也是很丑陋的方式)

    1?,F在最大的問題是它的WorkStore接口的擴展,我采用DB2的方式實現了它的接口,但這樣的方式會與DB2綁定在一起,如果自己寫實現就要根據不同的DB采用不同的SQL語言-也就是不同的方言策略?!而且考慮到性能估計不是什么好主意,看來明天需要更換成HibernateWorkStore的形式,這樣工作流的持久層接口將工作在Hibernate之上,看來很完美的解決了這個問題。

    2。而且我擴展了它的PropertySet,使其不再依靠JNDI尋找DataSource,而是通過嵌入在程序內部采用JDBC的形式尋找數據庫連接,這樣我就不必為了驗證一個問題去建立那該死的數據庫緩沖池了(而且JNDI的形式也就不可避免的要用到容器,太重了?。?BR>
    3。我編寫了UserGroupCondition的實現類,這個類的作用就是調用Interface3的方法,從而判斷某個用戶是否屬于某個組(現在的做法是讓WorkStore實現Interface3的偷懶辦法,但很亂,看來還是要寫一個Adapter去實現interface3才對!)

    4。目前工作流引擎的工廠類已經實現完工并測試通過。


    用了近一個月的時間完成了這些工作,看起來很少但是基本上大量的時間花費在熟悉工作流規范、WFMC標準、以及學習和擴展OSWorkflow接口上,不過對OSWorkflow的實現基本上掌握了,如果拋開OSWorkflow自己也可以采用自己的方式去實現,或者會考慮使用Spring的方式(Interface3的Adapter不行就采用Spring實現)。

    BTW:
    OSWorkflow的實現其實比較的丑陋!而且編碼根本沒有什么規范,接口的定義也是天馬行空,看來Heni除了他的大嘴外應該好好的提高自己的技術修養。-實在不敢恭維這位"大師"的編碼水平!

    posted @ 2006-03-02 21:03 killvin| 編輯 收藏

    在Workflow事務回滾中遇到了問題,是這樣的

    DB2ConnectFactory 中getConn方法
    /**
    * 獲取數據庫連接
    * @return
    * @throws SQLException
    */
    public Connection getConn() throws SQLException
    {
    Object obj = threadLocal.get();
    if(obj == null)
    {
    this.initFactoryStack();
    }else
    {
    connect = (Connection)obj;
    }
    connect.setAutoCommit(false); //事務的回滾必須建立在將Commit狀態為False下,默認是true
    logger.debug("Get connect from factory - " + connect.hashCode());
    return connect;
    }

    AbstractWorkflow 的doAction()方法


    try {
    //transition the workflow, if it wasn't explicitly finished, check for an implicit finish
    if (!transitionWorkflow(entry, currentSteps, store, wf, action, transientVars, inputs, ps)) {
    checkImplicitFinish(id);
    }
    } catch (WorkflowException e) {
    context.setRollbackOnly(); // 這里調用WorkContext對象的setRollbackOnly()方法,執行事務的回滾
    throw e;
    }

    GearWheelWorkFlowContext 的setRollbackOnly方法
    /**
    * Tranaction : Set Roll back
    * @throws SQLException
    */
    public void setRollbackOnly()
    {
    logger.debug("Context execute setRollbackOnly() !!");
    Connection connect = null;
    try
    {
    DB2ConnectFactory factory = new DB2ConnectFactory();
    connect = factory.getConn();
    logger.debug("Context get connect " + connect.hashCode());

    if(connect != null) connect.rollback();
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    finally
    {
    this.clostConnection(connect); //這里將關閉數據庫連接
    }
    }


    可是這是"異常"情況下的處理流程,如果正常執行呢?
    剛開始我想寫在CleanUp()方法里,但又一想不行,因為正常執行的流程需要做兩個工作
    1。將Commit狀態更新為true,并提交連接
    2。關閉數據庫連接


    關鍵就是關閉數據庫的連接在哪里寫?!現在寫在CleanUp()不合適,因為每一個WorkStore的方法都要默認(程序已經寫死了,我可不想重載它的所有的方法?。。┑年P閉數據庫連接!
    仔細的分析了一下,其實有兩個方法可以做到
    1。編寫Proxy類
    2。重載所有AbstractWorkflow中設計到事務的方法,(本來可以重載transitionWorkflow但是方法的類型卻為private?!)在它的方法下增加一個"提交"的方法。比如這樣

    try {
    //transition the workflow, if it wasn't explicitly finished, check for an implicit finish
    if (!transitionWorkflow(entry, currentSteps, store, wf, action, transientVars, inputs, ps))
    {
    checkImplicitFinish(id);
    }

    dosubmit();

    } catch (WorkflowException e) {
    context.setRollbackOnly(); // 這里調用WorkContext對象的setRollbackOnly()方法,執行事務的回滾
    throw e;
    }

    可以看到方法2比較"爛",看來下一步即使編寫方法1的實現


    posted @ 2006-03-02 21:02 killvin| 編輯 收藏

    早上的時間被該死的WorkflwoStore里的主鍵生成策略("主鍵生成策略"來源于Hibernate文檔),該死的Sequence,從文檔資料上看到DB2是支持Sequence的,按照db2的文檔我執行了如下的語句:
    create sequence seq_os_wfentry start with 10 increment by 10;
    create sequence seq_os_currentsteps;
    執行-ok

    可是我以前不太了解Sequence的概念,這片資料倒是很有價值
    http://www-128.ibm.com/developerworks/db2/library/techarticle/dm-0407zhang/
    不過我以為查詢Sequence就只需要執行SELECT NEXT VALUE FOR seq_os_wfentry 就ok了,可是誰知道總是報錯?!在比較仔細的看了這片文章之后發現,其實根本就無法執行這條SQL!而需要這樣
    INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME,
    SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Martin', 'Wong', 1000.00)

    可是看看JDBCWorkflowStore的實現,這里是JDBCWorkflowStore的主鍵生成策略!
    protected long getNextEntrySequence(Connection c) throws SQLException {
    if (log.isDebugEnabled()) {
    log.debug("Executing SQL statement: " + entrySequence);
    }

    PreparedStatement stmt = null;
    ResultSet rset = null;

    try {
    stmt = c.prepareStatement(entrySequence);
    rset = stmt.executeQuery();
    rset.next();

    long id = rset.getLong(1);

    return id;
    } finally {
    cleanup(null, stmt, rset);
    }
    }

    c.prepareStatement(entrySequence) - 其實執行了一條SQL語句,所以看來JDBCWorkflow根本不支持Sequence生成策略??!

    該死的實現方式,看來我要重載其實現方式,不過說真的JDBCWorkflow的編碼人員其實水平不匝地!

    posted @ 2006-03-02 21:01 killvin| 編輯 收藏

    OSPropertySet的最新版本是1.3 Date: 9/22/2003 ,不要使用OSWorkflow中自帶的OSPropertySet.jar ,主要是因為它的版本為propertyset-1.3-21Apr04,甚至里面的接口PropertySet竟然私自更換了(這個版本的remove()方法是抽象的,而新版本已經將這個方法命名為Public!!)

    posted @ 2006-03-02 21:00 killvin| 編輯 收藏

    J2EE(即Java 2 平臺企業版)是由Sun公司主持推出的一項中間件技術。從CORBA、IDL到面向消息的系統,中間件技術已經走過了很長的一段路程,如今J2EE作為中間件技術史上的一塊具有決定意義的里程碑,正受到業界越來越廣泛的重視和采納。

    J2EE,一方面有著一套相當龐大的標準體系和數個不同版本,另一方面,由于市場上應用服務器品種多樣,各家開發商使用的術語又不盡相同,因此,圍繞著J2EE,常常有不少被人誤解的地方。本文將深入探討J2EE究竟是什么,它到底能做什么。
    什么是J2EE?
    在試圖給J2EE 下一個明確的定義之前,我們首先要了解J2EE 并不簡單地只是一門語言、一種工具或一套服務。

    · J2EE——Java 2 平臺企業版
    簡單地說,J2EE是一個標準中間件體系結構,旨在簡化和規范多層分布式企業應用系統的開發和部署。J2EE方案的實施可顯著地提高系統的可移植性、安全性、可伸縮性、負載平衡和可重用性。

    J2EE技術出現之前,幾家主要的中間件開發商的產品各自為陣,彼此之間缺乏兼容性,可移植性差,難以實現互操作,沒有一個被普遍認可的行業標準。J2EE的出現標志著中間件技術在經歷了多年的不斷摸索和經驗總結后,正逐步走向成熟。

    J2EE的核心是一組規范和指南,定義了一個使用Java語言開發多層分布式企業應用系統的標準平臺。開發人員在這些規范和指南的基礎上開發企業級應用,同時由J2EE供應商確保不同的J2EE平臺之間的兼容性。由于基于規范的各J2EE平臺之間具有良好的兼容性, 因此J2EE應用系統可以部署在不同的應用服務器上,無需或只需進行少量的代碼修改。


    · J2EE視點
    下面我們將從幾個不同的側面來考察J2EE,以期讀者能對J2EE有個更全面清晰的印象。

    (1)J2EE:多層、分布式中間件語法
    采用多層分布式應用模型,J2EE將應用開發劃分為多個不同的層,并在每一個層上定義組件。各個應用組件根據他們所在的層分布在同一個或不同的服務器上,共同組成基于組件的多層分布式系統。典型的J2EE四層結構包括客戶層、表示邏輯層(Web層)、商業邏輯層和企業信息系統層。

    有了J2EE,分布式系統的開發變得簡單了,部署的速度也可以加快。J2EE組件的分布與服務器環境無關,所有的資源都可通過分布式目錄進行訪問。這意味著開發人員不再需要為組件和資源的分布問題耗費精力,從而可以有更多的時間專注于業務邏輯的實現,提高開發效率。

    (2)J2EE:企業級應用系統開發平臺
    J2EE本身是一個標準,一個為企業分布式應用的開發提供的標準平臺。而J2EE的實施,則具體表現為諸如BEA Web logic或IBM Web sphere之類的特定Web服務器產品。利用J2EE應用-編程模型開發的企業應用系統,可以部署在不同廠商生產的、但相互兼容的J2EE 應用服務器上。

    目前,市場上基于J2EE的Web服務器品種繁多,性能特點各有千秋,每家廠商的產品都有精心設計的獨到之處。但與產品個性無關的是,所有的J2EE應用服務器都為企業級應用系統的開發和部署提供了一個共同的基礎。

    (3)J2EE:電子化應用開發模型
    J2EE應用很容易發布到Web、掌上電腦或移動電話等手持設備上。換言之,應用組件可以很輕松地實現電子化。J2EE的應用-編程模型保證組件在向不同類型的客戶端移植過程中,商業邏輯和后端系統保持不變。

    此外,J2EE平臺的其他主要優點還有:自動負載平衡、可伸縮、容錯和具有故障排除等功能。部署在J2EE環境中的組件將自動獲得上述特性,而不必增加額外的代碼開銷。

    J2EE所有這些特性對于需要構建全天候網絡門戶的企業來說顯得尤為重要。

    (4)J2EE:Web應用服務器上廣泛采用的標準
    可以說,J2EE是首個獲得業界廣泛認可和采納的中間件標準。目前幾乎所有的一流Web應用服務器,如BEA的Web logic、IBM的Web sphere、HP的應用服務器、Sun的iPlanet和Macromedia的Jrun等,都是基于J2EE的。迄今為止,還沒有哪個其他標準能獲得如此眾多的中間件供應商的一致支持。

    而且,有了J2EE,企業的應用開發對于某個特定的開發商或應用服務供應商的依賴性更小。應用組件只要符合J2EE規范,完全可以部署在不同的應用服務器上。為了確保不同廠商的J2EE應用服務器的兼容性和一致性,Sun公司發布了J2EE兼容性測試包。

    · J2EE究竟是什么
    至此,我們可以試著用一句話來概括J2EE,那就是:J2EE是一個中間件基礎架構,有了它,開發者只需要集中精力編寫代碼來表達企業應用的商業邏輯和表示邏輯,至于其他系統問題,如內存管理,多線程,資源分布和垃圾收集等,都將由J2EE自動完成。

    J2EE如何應對挑戰?
    在這一部分里,我們將探討J2EE是如何應對企業開發過程中所面臨的問題,以及如何為企業未來發展之需要提供空間。

    · 獨立于硬件配置和操作系統
    J2EE運行在Java虛擬機(JVM)上,利用Java本身的跨平臺特性,獨立于硬件配置和操作系統。Java運行環境(JRE)——JVM的可安裝版本加上其他一些重要組件——幾乎可以運行于所有的硬件/OS組合。因此,通過采用Java,J2EE使企業免于高昂的硬件設備和操作系統的再投資,保護已有的IT資源。在很多情況下,J2EE還可以直接運行在EIS服務器環境中,從而節約網絡帶寬,提高性能。


    · 堅持面向對象的設計原則
    作為一門完全面向對象的語言,Java幾乎支持所有的面向對象的程序設計特征。面向對象和基于組件的設計原則構成了J2EE應用編程模型的基礎。

    J2EE多層結構的每一層都有多種組件模型。因此,開發人員所要做的就是為應用項目選擇適當的組件模型組合,靈活地開發和裝配組件,這樣不僅有助于提高應用系統的可擴展性,還能有效地提高開發速度,縮短開發周期。此外,基于J2EE的應用還具有結構良好,模塊化,靈活和高度可重用性等優點。

    · 靈活性、可移植性和互操作性
    利用Java的跨平臺特性,J2EE組件可以很方便地移植到不同的應用服務器環境中。這意味著企業不必再拘泥于單一的開發平臺。

    J2EE的應用系統可以部署在不同的應用服務器上,在全異構環境下,J2EE組件仍可彼此協同工作。這一特征使得裝配應用組件首次獲得空前的互操作性。例如,安裝在IBM Websphere環境下的EJB,一方面可以直接與Websphere環境下的CICS直接交互,另一方面也可以通過安裝在別處的BEA Weblogic 服務器上的EJB進行訪問。

    · 輕松的企業信息系統集成
    J2EE技術出臺后不久,很快就將JDBC、 JMS和 JCA等一批標準納歸自身體系之下,這大大簡化了企業信息系統整合的工作量,方便企業將諸如legacy system(早期投資系統),ERP和數據庫等多個不同的信息系統進行無縫集成。

    由于幾乎所有的關系型數據庫系統都支持JDBC,因此只需借助必要的JDBC驅動程序,J2EE應用就可以和所有主流數據庫系統進行通信。類似的,目前業界正冒出一批基于Java連接器體系標準的EI適配器,也用于提供各類legacy system和ERP/CRM的無縫集成。

    · 引進面向服務的體系結構
    隨著Web服務以及SOAP等開放標準的出現,企業異構系統之間的互操作性成為可能。J2EE,作為一個可擴展平臺,很自然需要加入Web服務特性。為此,Sun公司發布了一整套稱為“JAX包”的API,支持從XML語法分析、XML綁定、SOAP消息發送、注冊表查尋、XML RPC到XML消息傳遞等所有各種Web服務需求。

    雖然J2EE平臺的出現早于Web服務技術,但它的可擴展能力使它能很好地適應技術的最新發展。我們有理由相信,在未來,J2EE將引入更多的技術進步而不會動搖它的核心框架和應用-編程模型。

    結束語
    作為一個被業界廣泛采用的中間件標準,J2EE是開發可伸縮的、具有負載平衡能力的多層分布式跨平臺企業應用的理想平臺。J2EE的首要任務在于提供一個標準中間件基礎架構,由該基礎架構負責處理企業開發中所涉及的所有系統級問題,從而使得開發人員可以集中精力重視商業邏輯的設計和應用的表示,提高開發工作的效率。

    J2EE有效地滿足了行業需求,提供獨立于操作系統的開發環境。基于J2EE的應用系統靈活且易于移植和重用,可運行在不同廠家的Web服務器上。更為重要的是,J2EE是一個開放體系,完全有能力適應未來技術的進步和發展。

    posted @ 2006-03-02 20:59 killvin| 編輯 收藏

    該文轉自guty

    O-R Mapping

    J2EE的標準是CMP Entity Bean,而實際應用中受到詬病最多的也是它。我們化了整整半年時間研究CMP2.0的開發方法,目前總算能夠將代碼量減少到70%,并且有希望減少到 90%。我曾經很滿足現有的成績,但是當我真正地閱讀了hibernate后,對CMP2.0的信心徹底動搖了。

    hibernate至少比CMP2.0有以下優點:
    1. 兼容性。 規范一模一樣,實現各有不同,這是CMP的現狀。用第三方O-R Mapping工具可以解決這個問題。
    2. 保護智力投資。在了解了Orion, Weblogic, JBoss的CMP實現后,我不愿意再去學習Websphere 或者Resin的實現了。
    3. 性能。
    a. local v.s. remote, hibernate、JDO、Castor都是本地調用,CMP2.0雖然也有Local接口,但是Web層還是需要通過Remote接口訪問EJB層的數據,序列化、網絡調用、創建大量的對象,都是性能降低的原因。
    b. transaction,J2EE提出了一個全新的事務模型(method-based descriptor),對程序員的開發確實是個“簡化”,記得一本教程建議所有的EJB方法都用Required。但這樣的結果是什么?性能極度降低!互鎖!沒有辦法,我們只有再去調節各個方法的Transaction屬性,然后又出現 新的互鎖...
    新的事務模型是不成功的。它試圖簡化問題,卻引入了更為嚴重的問題。各家廠商的Transaction實現也不盡相同,有的支持Optimistic Lock,有的在VM中同步Entity對象,又是兼容性的一大敵。
    hibernate沒有試圖創造一個更新的模式,相反,它沿用了傳統數據庫的Transaction編程模式,在對J2EE的Transaction傷透腦筋后看到它,真是十分親切,感覺自己確實在編程,而不是碰運氣填代碼了。
    4. 動態Query。
    Entity Bean很難實現動態Query,這是因為它基于代碼自動生成技術,即最終的執行代碼是在部署編譯時生成的。hibernate則有根本的改變,它基于 reflection機制,運行時動態Query是很自然的事。另外,hibernate幾乎支持所有的SQL語法,傳統數據庫可以做的它就可以做。
    5. 發展速度。
    I have a dream, 有一天Entity Bean會變得很好。但至少目前來看,Entity Bean是一個不完善的產品,它是大公司政治斗爭和妥協的產品,而且習慣性將一些問題“無限期擱置”,典型的例子就是Query(之所以不提其他問題,是因為其他都是Entity Bean的致命傷:))
    形成強烈反差的是,hibernate的核心程序員只有一人,但它改進的速度確是Entity Bean無法企及的。
    6. 繼承和多態。
    OO語言的精華在Entity Bean這里是行不通的,我曾經自我安慰將Entity Bean看做一個“內存中的數據表”,才找到了一點平衡。
    但當我看到hibernate時,又開始不平衡了。

    另外,CMP2.0也有一些缺點是可以彌補的。
    1. 代碼維護。
    大量的接口文件和配置文件,開發和維護的工作量很大。
    解決途徑:采用xdoclet,可以自動產生眾多的接口和配置文件,甚至facade, delegate等高級模式。

    至少目前來看,hibernate的缺點有:
    1. 代碼維護
    hibernate提供了自動生成mapping文件“框架”的工具,但還需要手工調節。而這類開發,能想到的最佳模式就是xdoclet的(代碼+注釋)的模式了。幸好,hibernate的程序員已經向xdoclet項目增加了hibernate的模塊。現在需要的是等待xdoclet的下一個 release。

    結論:
    hibernate至少從文檔上超越了Entity Bean很多,我要學習hibernate。

    以下是robbin的觀點

    如果說不使用Session Facade模式的話,我認為EB還是一個很有意義的的東西,因為EB是唯一直接支持跨RMI的持久化方案。但是由于EB的效率和減少跨RMI的網絡調用的原因,EB已經完全被封裝到SB的后面,EB的分布式調用的功能,EB的安全驗證功能,EB的容器事務功能完全被前面的SB給做了,結果EB就只剩下了唯一的ORM功能了,單就ORM這一點來說EB實在是一個非常非常糟糕的東西。那么EB還有什么功能值得我非去用它不可呢?

    用 Session Bean + DAO + Hibernate 來取代 Session Bean + Entity Bean,不但能夠極大降低軟件設計難度,軟件開發難度,軟件調試難度和軟件部署難度,而且還可以提高允許效率,降低硬件要求。

    不要把EB直接拿來和Hibernate做比較,兩者不是一個范疇的東西,而應該整體比較兩種方案:
    Session Bean + DAO + Hibernate
    Session Bean + Entity Bean
    我找不出來第二方案有哪怕一點方面能夠比第一方案好的。

    CMP可以使用CMR來表示多表之間通過外鍵關聯的關系。但是你仍然會遇到即使沒有鍵關聯的表仍然需要連接查詢的情況,這是一個非常普遍的現象。

    如果是Hibernate,可以在HSQL里面定義outer join,BMP也可以寫JDBC,而CMP沒有任何辦法來解決該問題,除非你把需要的連接查詢都定義為CMR,但那樣的話,凡是有需要連接查詢,或者有鍵關聯的表都必須打在一個包里面。你如果不打在一個jar包里面,如果能夠建立CMR?不是我想放在一個jar里面,而是不得不放在一個jar里面?;旧螩MP還是非常笨拙的。

    CMP的另一大缺點是不能動態SQL,guty已經提到了,一個SQL就要定義一個EJBFinder方法,在編譯的時候就確定死了。在實際應用中,經常會遇到不確定查詢條件的查詢,比如說用戶在頁面上用下拉列表來選擇查詢的條件,用戶既有可能什么限制條件都不選,也有可能選擇某幾個條件。這時候你怎么辦?假設有n個查詢條件,你要寫 C1n + C2n + C3n +...+ Cnn(C是組合公式的符合,n是下標,1...n是上標)個EJBFinder方法才行,很恐怖吧。

    其實JDBC的PrepareStatement也不能很好的解決這個問題,因為它是按照1,2這樣的次序來set參數的。用Statement是肯定不行的,會嚴重影響數據庫,甚至會導致數據庫down掉(我的實際經驗)。但是Hibernate就解決的不錯,因為它可以按照 :name 這樣的形式來設定SQL中的Placeholder,這樣set參數就可以按照參數名稱傳遞,因為次序不是死的,在程序里面就很容易根據用戶選擇的查詢條件,動態的產生SQL,動態的set參數了。

    CMP2.0還有一個大問題是不支持order by,當然你可以在Java里面對取出來的集合排序,但是速度和數據庫里面就排好序速度不在一個數量級了。Hibernate不但可以order by,還可以group by,having,子查詢,真是沒有辦法比下去了。

    其實對于動態SQL和排序問題,特定的App Server也可以做,但那不是CMP2.0的規范罷了,所以為了可移植性,也不敢隨便去用。

    在項目開發時, 開發和運行效率以及靈活性是非常重要的指標。由于Entity Bean天生是一種粗粒度的使用方式,這就必定使它在裝載的時候有較長的響應時間,也不能自如的支持懶裝入的方式,使用成細粒度會使程序變得復雜,以及遠程調用細粒度的entity bean是一種非常可怕的行為, 太慢了.

    Hibernate正好滿足開發和運行效率以及靈活性,說來說去,它可以稱做一個OO化的JDBC, 這樣大家就不會對Hibernate產生誤解及恐懼心理。它支持粗細兩種粒度方式,運用起來靈活自如,前提是你必知道如何使用,一個entity bean 實現要N種重復的方法, such as ejbRemove,ejbstore,ejb...., 光類也有一大堆,象Home Interface, Romote Interface..., Primary class if necessary. Hibernate只需要一個就行了。

    CMP在進行O/R Mapping方面只是做了最基礎的工作而已,完全用CMP做數據層,會發現你在把數據庫應該做的工作全部都搬到App Server里面來重新實現一遍,有這必要嗎?

    CMP是把EJBQL寫死在ejb-jar.xml里面的,所以n個條件就需要(c0n+c1n+...cnn )2的n次方個EJBFinder方法,簡直沒有辦法說。

    JDBC實現PrepareStatement的動態SQL構造不是不能夠,而是非常麻煩,需要寫一個非常非常大的if elseif else嵌套的判斷。

    Hibernate實現起來特別簡單,(其實OJB也實現了PrepareStatement的動態SQL構造)這本身并不復雜,但是需要你多寫些代碼而已,由于CMP把EJBQL寫死在配置文件里面了,你連選擇的余地都沒有。

    posted @ 2006-03-02 20:58 killvin| 編輯 收藏

    A reader asked a question via a comment a couple months ago that I didn't really have an answer for (and had always kind of wondered the same thing). In the original post (which showed how to use JDBC with ColdFusion), I used the following snippet of code:

    Class.forName("jdbc.DriverXYZ");
    Connection con = DriverManager.getConnection(url,
      "myLogin", "myPassword");

    and the reader wanted to know what the Class.forName(..) method did. The most common answer you'll hear is that it loads the database driver, which, while technically true, is shallow. Where does it get loaded? How does it happen? And why?

    To answer the question I started with the JavaDoc for the Class.forName() method. According to the documentation, the method:

    ... attempts to locate, load, and link the class or interface
    I wasn't perfectly clear on what "locate, load, and link" meant, so I did a little digging through the Java Language Specification. According to chapter 12 of the JLS:
    Loading refers to the process of finding the binary form of a class or interface type with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a compiler, and constructing, from that binary form, a Class object to represent the class or interface.
    Next, again according to the JLS, it must be transformed from it's binary representation to something the Java virtual machine can use, this process is called linking. Finally, the class is initialized, which is the process that executes the static initializer and the initializers for static fields declared in the class.

    So then back to the original problem, when Class.forName() is called with an argument like this:

    Class.forName("org.gjt.mm.mysql.Driver");

    the classloader attempts to load and link the Driver class in the "org.gjt.mm.mysql" package and if successful, the static initializer is run. The MySQL Driver (download the source code) static initializer looks like this:

    static {
      try {
        java.sql.DriverManager.registerDriver(new Driver());
      } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
      }
    }

    So it calls a static method in the java.sql.DriverManager class which apparently registers a copy of itself when it loads.

    So now I understand the where and the how, what about why? To understand the why you have to look at the next line in the initial code example:

    Connection con = DriverManager.getConnection(url,
      "myLogin", "myPassword");

    The DriverManager class (view DriverManager source here) returns a database connection given a JDBC URL string, a username and a password. In order to create that connection, the DriverManager class has to know which database driver you want to use. It does that by iterating over the array (internally a Vector) of drivers that have registered with it (ie: the registerDriver(Driver driver) method illustrated above) and calls the acceptsURL(url)) method on each driver in the array, effectively asking the driver to tell it whether or not it can handle the JDBC URL.

    So there you have it. Class.forName explained.

    posted @ 2006-03-02 20:57 killvin| 編輯 收藏

    原文: http://forum.javaeye.com/viewtopic.php?t=17912


    摟主的問題問的含含糊糊:flyjie給出了非常詳細的解釋,
    不過就是沒有解釋String實例化的特殊方面以及Intern()方法的含義

    -------
    ---------------------------------------------------------------------------------
    ---------------------------------------------------------------------------------
    java代碼:
    String str ;

    這樣聲明str它只支是一個對象的reference,不會產生實際的對象。如果沒有初始化str,編譯時便會發生錯誤。
    java代碼:
    String str1=new String("test");
    String str2 = "test";

    str1是一個新的對象。new關鍵字的意思就是創建某個新的對象。而str2是一個對象的引用?!∷鼈兊膬热菹嗤?,但內存地址是不一樣的。 java中對象的引用存在Stack(棧)中,而對象由Heap(堆)分配空間。

    3、引用==變量?  不一定
    java代碼:

      public class TestString {
    public static void main(String[] args) {
    String s1 = "test";
    String s2 = new String("test");
    if (s1 == s2)
    System.out.println("s1 == s2");
    else
    System.out.println("s1 != s2");
    if (s1.equals(s2))
    System.out.println("s1 equals s2");
    else System.out.println("s1 not equals s2");
    }
    }

    我們將 s2 用 new 操作符創建程序輸出:s1 != s2 s1 equals s2.
    java代碼:

    s2 = s2.intern();

    在你加上這句話后,上面的程序輸入:s1 == s2 s1 equals s2

    而String a = "test" ; String b = "test" ; a == b 會返回true; 這里a="test"時創建一個在棧中的reference, b=test時jvm發現棧中已存在名為"test"的字符串,直接引用。結論:String 是個對象,要對比兩個不同的String對象的值是否相同明顯的要用到 equals() 這個方法. 而== 比較的是內存地址的值。

    4、private final String a = "test", 這個a屬于常量,存放在常量存儲空間(CS)中。

    5、建議你看看<<深入淺出java虛擬機>>一書。

    -------
    ---------------------------------------------------------------------------------
    ---------------------------------------------------------------------------------



    總結

    1. 在學習JAVA的時候就知道==比較的是內存地址.而equals比較的是內存地址對應的值?。墒沁€是有很多的人問來問去的,真不知道他們JAVA的基礎課程是怎么學的??。?BR>
    2. JAVA所有的對象都是存放在堆中的!你獲取的"對象"僅僅只是對象的引用而已

    3. String是比較特殊的對象,特殊在
    3.1 > String a = new String("test") -此時你是在堆中實例化了一個字符串對象
    3.2 > String b = "test"-此時JVM會先去堆中尋找這樣的對象;如果有就返回此對象的引用;如果沒有就重新實例化一個這樣的對象!基于這樣的一個過程所以JAVA要求String不可以更改值的。

    3.3 >intern()方法就是試圖完成這樣的一個尋找過程
    When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

    這里有一份詳細的參考資料:

    關于Java棧與堆的思考 http://www.javafan.net/article/20051123115654293.html


    posted @ 2006-03-02 20:56 killvin| 編輯 收藏

    URL : http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

    網上的一些關于內部類的概念是不完整的,還是看看SUN的文檔上的標準答案。
    ...

    Like other members, a nested class can be declared static (or not). A static nested class is called just that: a static nested class. A nonstatic nested class is called an inner class.

    - Nested class分為靜態Static nested class 的和非靜態的 inner class, 在SUN的眼里只有Nested Class!!


    As with static methods and variables, which we call class methods and variables, a static nested class is associated with its enclosing class. And like class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class — it can use them only through an object reference.

    - 靜態的Static nested class是不可以直接調用它的外部類enclosing class的,但是可以通過外部類的引用來調用,就像你在一個類中寫了main方法一樣。
    ...

    As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's instance variables and methods. Also, because an inner class is associated with an instance, it cannot define any static members itself.

    -非靜態類inner class 可以自由的引用外部類的屬性和方法,但是它與一個實例綁定在了以其,不可以定義靜態的屬性、方法(這點不是很理解,可能需要看JVM的類實現)

    ...
    class EnclosingClass {
    ...
    class InnerClass {
    ...
    }
    }


    The interesting feature about the relationship between these two classes is not that InnerClass is syntactically defined within EnclosingClass. Rather, it's that an instance of InnerClass can exist only within an instance of EnclosingClass and that it has direct access to the instance variables and methods of its enclosing instance. The next figure illustrates this idea.


    -圖形化的嵌入類與外部類的關系

    posted @ 2006-03-02 20:55 killvin| 編輯 收藏

    初識Java內部類
        提起Java內部類(Inner Class)可能很多人不太熟悉,實際上類似的概念在C++里也有,那就是嵌套類(Nested Class),關于這兩者的區別與聯系,在下文中會有對比。內部類從表面上看,就是在類中又定義了一個類(下文會看到,內部類可以在很多地方定義),而實際上并沒有那么簡單,乍看上去內部類似乎有些多余,它的用處對于初學者來說可能并不是那么顯著,但是隨著對它的深入了解,你會發現Java的設計者在內部類身上的確是用心良苦。學會使用內部類,是掌握Java高級編程的一部分,它可以讓你更優雅地設計你的程序結構。下面從以下幾個方面來介紹:
        ·第一次見面
    public interface Contents {
        int value();
    }
    public interface Destination {
        String readLabel();
    }
    public class Goods {
        private class Content implements Contents {
            private int i = 11;
            public int value() {
                return i;
            }
        }
        protected class GDestination implements Destination {
            private String label;
            private GDestination(String whereTo) {
                label = whereTo;
            }
            public String readLabel() {
                return label;
            }
        }
        public Destination dest(String s) {
            return new GDestination(s);
        }
        public Contents cont() {
            return new Content();
        }
    }
    class TestGoods {
        public static void main(String[] args) {
            Goods p = new Goods();
            Contents c = p.cont();
            Destination d = p.dest("Beijing");
        }
    }
        在這個例子里類Content和GDestination被定義在了類Goods內部,并且分別有著protected和private修飾符來控制訪問級別。Content代表著Goods的內容,而GDestination代表著Goods的目的地。它們分別實現了兩個接口Content和Destination。在后面的main方法里,直接用 Contents c和Destination d進行操作,你甚至連這兩個內部類的名字都沒有看見!這樣,內部類的第一個好處就體現出來了——隱藏你不想讓別人知道的操作,也即封裝性。
        同時,我們也發現了在外部類作用范圍之外得到內部類對象的第一個方法,那就是利用其外部類的方法創建并返回。上例中的cont()和dest()方法就是這么做的。那么還有沒有別的方法呢?當然有,其語法格式如下:
        outerObject=new outerClass(Constructor Parameters);
        outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
        注意在創建非靜態內部類對象時,一定要先創建起相應的外部類對象。至于原因,也就引出了我們下一個話題——
        ·非靜態內部類對象有著指向其外部類對象的引用
        對剛才的例子稍作修改:
    public class Goods {
        private valueRate=2;
        private class Content implements Contents {
            private int i = 11*valueRate;
            public int value() {
                return i;
            }
        }
        protected class GDestination implements Destination {
            private String label;
            private GDestination(String whereTo) {
                label = whereTo;
            }
            public String readLabel() {
                return label;
            }
        }
        public Destination dest(String s) {
            return new GDestination(s);
        }
        public Contents cont() {
            return new Content();
        }
    }
        修改的部分用藍色顯示了。在這里我們給Goods類增加了一個private成員變量valueRate,意義是貨物的價值系數,在內部類Content的方法value()計算價值時把它乘上。我們發現,value()可以訪問valueRate,這也是內部類的第二個好處——一個內部類對象可以訪問創建它的外部類對象的內容,甚至包括私有變量!這是一個非常有用的特性,為我們在設計時提供了更多的思路和捷徑。要想實現這個功能,內部類對象就必須有指向外部類對象的引用。Java編譯器在創建內部類對象時,隱式的把其外部類對象的引用也傳了進去并一直保存著。這樣就使得內部類對象始終可以訪問其外部類對象,同時這也是為什么在外部類作用范圍之外向要創建內部類對象必須先創建其外部類對象的原因。
        有人會問,如果內部類里的一個成員變量與外部類的一個成員變量同名,也即外部類的同名成員變量被屏蔽了,怎么辦?沒事,Java里用如下格式表達外部類的引用:
    outerClass.this
        有了它,我們就不怕這種屏蔽的情況了。
        ·靜態內部類
        和普通的類一樣,內部類也可以有靜態的。不過和非靜態內部類相比,區別就在于靜態內部類沒有了指向外部的引用。這實際上和C++中的嵌套類很相像了,Java內部類與C++嵌套類最大的不同就在于是否有指向外部的引用這一點上,當然從設計的角度以及以它一些細節來講還有區別。
        除此之外,在任何非靜態內部類中,都不能有靜態數據,靜態方法或者又一個靜態內部類(內部類的嵌套可以不止一層)。不過靜態內部類中卻可以擁有這一切。這也算是兩者的第二個區別吧。
        ·局部內部類
        是的,Java內部類也可以是局部的,它可以定義在一個方法甚至一個代碼塊之內。
    public class Goods1 {
         public Destination dest(String s) {
              class GDestination implements Destination {
                   private String label;
                   private GDestination(String whereTo) {
                        label = whereTo;
                   }
                   public String readLabel() { return label; }
              }
              return new GDestination(s);
         }
         public static void main(String[] args) {
              Goods1 g= new Goods1();
              Destination d = g.dest("Beijing");
         }
    }
        上面就是這樣一個例子。在方法dest中我們定義了一個內部類,最后由這個方法返回這個內部類的對象。如果我們在用一個內部類的時候僅需要創建它的一個對象并創給外部,就可以這樣做。當然,定義在方法中的內部類可以使設計多樣化,用途絕不僅僅在這一點。
        下面有一個更怪的例子:
    public class Goods2{
         private void internalTracking(boolean b) {
              if(b) {
                   class TrackingSlip {
                        private String id;
                        TrackingSlip(String s) {
                             id = s;
                        }
                        String getSlip() { return id; }
                   }
                   TrackingSlip ts = new TrackingSlip("slip");
                   String s = ts.getSlip();
              }
         }
         public void track() { internalTracking(true); }
         public static void main(String[] args) {
              Goods2 g= new Goods2();
              g.track();
         }
    }
        你不能在if之外創建這個內部類的對象,因為這已經超出了它的作用域。不過在編譯的時候,內部類TrackingSlip和其他類一樣同時被編譯,只不過它由它自己的作用域,超出了這個范圍就無效,除此之外它和其他內部類并沒有區別。
        ·匿名內部類
        java的匿名內部類的語法規則看上去有些古怪,不過如同匿名數組一樣,當你只需要創建一個類的對象而且用不上它的名字時,使用內部類可以使代碼看上去簡潔清楚。它的語法規則是這樣的:
        new interfacename(){......}; 或 new superclassname(){......};
        下面接著前面繼續舉例子:
    public class Goods3 {
         public Contents cont(){
              return new Contents(){
                   private int i = 11;
                   public int value() {
                        return i;
                   }
              };
         }
    }
        這里方法cont()使用匿名內部類直接返回了一個實現了接口Contents的類的對象,看上去的確十分簡潔。
        在java的事件處理的匿名適配器中,匿名內部類被大量的使用。例如在想關閉窗口時加上這樣一句代碼:
    frame.addWindowListener(new WindowAdapter(){
         public void windowClosing(WindowEvent e){
              System.exit(0);
         }
    });
        有一點需要注意的是,匿名內部類由于沒有名字,所以它沒有構造函數(但是如果這個匿名內部類繼承了一個只含有帶參數構造函數的父類,創建它的時候必須帶上這些參數,并在實現的過程中使用super關鍵字調用相應的內容)。如果你想要初始化它的成員變量,有下面幾種方法:
        1.如果是在一個方法的匿名內部類,可以利用這個方法傳進你想要的參數,不過記住,這些參數必須被聲明為final。
        2.將匿名內部類改造成有名字的局部內部類,這樣它就可以擁有構造函數了。
        3.在這個匿名內部類中使用初始化代碼塊。
        ·為什么需要內部類?
        java內部類有什么好處?為什么需要內部類?
        首先舉一個簡單的例子,如果你想實現一個接口,但是這個接口中的一個方法和你構想的這個類中的一個方法的名稱,參數相同,你應該怎么辦?這時候,你可以建一個內部類實現這個接口。由于內部類對外部類的所有內容都是可訪問的,所以這樣做可以完成所有你直接實現這個接口的功能。
        不過你可能要質疑,更改一下方法的不就行了嗎?
        的確,以此作為設計內部類的理由,實在沒有說服力。
        真正的原因是這樣的,java中的內部類和接口加在一起,可以的解決常被C++程序員抱怨java中存在的一個問題——沒有多繼承。實際上,C++的多繼承設計起來很復雜,而java通過內部類加上接口,可以很好的實現多繼承的效果。

    posted @ 2006-03-02 20:54 killvin| 編輯 收藏

    引言
    在使用response的過程中經常會遇到跳轉頁面的事情,這個時候有兩種情況供你選擇
    1。就是調用
    ServletContext.getRequestDispatcher(java.lang.String).forward(request ,
    response) ;
    2。就是調用response.setRedirect(),可是這兩個方法有什么不同呢?


    看看TSS上關于這個問題的解釋:
    Difference request.forward() and response.sendRedirect .
    Posted by: Kapil Israni on August 24, 2000 in response to Message #2253 1
    replies in this thread
    i suppose u r talking bout requestDispatcher.forward() here.

    well basically both method calls redirect u to new resource/page/servlet.

    the difference between the two is that sendRedirect always sends a header
    back to the client/browser. this header then contains the
    resource(page/servlet) which u wanted to be redirected. the browser uses
    this header to make another fresh request. thus sendRedirect has a overhead
    as to the extra remort trip being incurred. its like any other Http request
    being generated by ur browser. the advantage is that u can point to any
    resource(whether on the same domain or some other domain). for eg if
    sendRedirect was called at www.mydomain.com then it can also be used to
    redirect a call to a resource on www.theserverside.com.

    where as in case of forward() call, the above is not true. resources from
    the server, where the fwd. call was made, can only be requested for. but
    the major diff between the two is that forward just routes the request to
    the new resources which u specify in ur forward call. that means this route
    is made by the servlet engine at the server level only. no headers r sent
    to the browser which makes this very eficient. also the request and
    response objects remain the same both from where the forward call was made
    and the resource which was called.

    i hope i have hit ur question right.

    posted @ 2006-03-02 20:54 killvin| 編輯 收藏

    Java Language Keywords

    Here's a list of keywords in the Java language. These words are reserved — you cannot use any of these words as names in your programs. true, false, and null are not keywords but they are reserved words, so you cannot use them as names in your programs either.

    abstract    |   continue    |   for    |    new     |   switch
    assert***  |   default     |   goto*     |   package     |   synchronized
    boolean    |   do     |   if     |   private     |   this
    break         |   double     |   implements    |   protected    |   throw
    byte            |   else     |   import     |   public  throws
    case          |   enum****    |   instanceof    |   return     |   transient 
    catch         |   extends     |   int     |   short     |   try
    char           |   final     |   interface    |   static     |   void
    class         |   finally     |   long     |   strictfp**    |   volatile
    const*       |   float     |   native     |   super     |   while


    *   not used
    **   added in 1.2
    ***   added in 1.4 
    ****   added in 5.0                                    

     

    Key: strictfp**

    使用對象:類、方法

    自Java2以來,Java語言增加了一個關鍵字strictfp,雖然這個關鍵字在大多數場合比較少用,但是還是有必要了解一下。

    strictfp的意思是FP-strict,也就是說精確浮點的意思。在Java虛擬機進行浮點運算時,如果沒有指定strictfp關鍵字時,Java的編譯器以及運行環境在對浮點運算的表達式是采取一種近似于我行我素的行為來完成這些操作,以致于得到的結果往往無法令你滿意。而一旦使用了strictfp來聲明一個類、接口或者方法時,那么所聲明的范圍內Java的編譯器以及運行環境會完全依照浮點規范IEEE-754來執行。因此如果你想讓你的浮點運算更加精確,而且不會因為不同的硬件平臺所執行的結果不一致的話,那就請用關鍵字strictfp。

    你可以將一個類、接口以及方法聲明為strictfp,但是不允許對接口中的方法以及構造函數聲明strictfp關鍵字,例如下面的代碼:

    1. 合法的使用關鍵字strictfp

    strictfp interface A {}

    public strictfp class FpDemo1 {
        strictfp void f() {}
    }

    2. 錯誤的使用方法

    interface A {
        strictfp void f();
    }

    public class FpDemo2 {
        strictfp FpDemo2() {}
    }

    一旦使用了關鍵字strictfp來聲明某個類、接口或者方法時,那么在這個關鍵字所聲明的范圍內所有浮點運算都是精確的,符合IEEE-754規范的。例如一個類被聲明為strictfp,那么該類中所有的方法都是strictfp的。

     

    Keys: volatile

    使用對象:字段

    介紹:因為異步線程可以訪問字段,所以有些優化操作是一定不能作用在字段上的。volatile有時

    可以代替synchronized。

     

    Keys:transient

      使用對象:字段

      介紹:字段不是對象持久狀態的一部分,不應該把字段和對象一起串起。

    posted @ 2006-03-02 20:53 killvin| 編輯 收藏

    引言

     記得當初參與某公司的ERP項目中,接觸過異常框架這個概念,可是似乎并沒有感覺到當時技術經理提出這個概念的意義,而且他也對這個概念似乎很"保守",雖然按照他的思路去執行,但沒有理解的概念再實施起來的時候總是覺得很"別扭",而如今面對自己要設計咚咚了,不得不重新審視異常這個概念,JAVA異常的介紹文章在網絡上非常的少,而對于如何構件J2EE的異常處理框架更顯的稀少,于是就促使自己寫下了這樣的文章。

     本文只是自己的一些愚見,希望和大家相互學習。Email:Killvin@hotmail.com

    概念

    什么是異常?

    異常(exception)應該是異常事件(exceptional event)的縮寫。
    異常定義:異常是一個在程序執行期間發生的事件,它中斷正在執行的程序的正常的指令流。
    當在一個方法中發生錯誤的時候,這個方法創建一個對象,并且把它傳遞給運行時系統。這個對象被叫做異常對象,它包含了有關錯誤的信息,這些信息包括錯誤的類型和在程序發生錯誤時的狀態。創建一個錯誤對象并把它傳遞給運行時系統被叫做拋出異常。
    一個方法拋出異常后,運行時系統就會試著查找一些方法來處理它。這些處理異常的可能的方法的集合是被整理在一起的方法列表,這些方法能夠被發生錯誤的方法調用。這個方法列表被叫做堆棧調用(call stack)

    運行時系統搜尋包含能夠處理異常的代碼塊的方法所請求的堆棧。這個代碼塊叫做異常處理器,搜尋首先從發生的方法開始,然后依次按著調用方法的倒序檢索調用堆棧。當找到一個相應的處理器時,運行時系統就把異常傳遞給這個處理器。一個異常處理器要適當地考濾拋出的異常對象的類型和異常處理器所處理的異常的類型是否匹配。異常被捕獲以后,異常處理器關閉。如果運行時系統搜尋了這個方法的所有的調用堆棧,而沒有找到相應的異常處理器。

     

    怎么設計異??蚣?/P>

    任何的異常都是Throwable類(為何不是接口??),并且在它之下包含兩個字類Error / Exception,而Error僅在當在Java虛擬機中發生動態連接失敗或其它的定位失敗的時候,Java虛擬機拋出一個Error對象。典型的簡易程序不捕獲或拋出Errors對象,你可能永遠不會遇到需要實例化Error的應用,那就讓我們關心一下Exception

    Exception中比較重要的就是RuntimeException-運行時異常(當然這個名字是存在爭議的,因為任何的異常都只會發生在運行時),為什么說這個類時很重要的呢?因為它直接關系到你的異??蚣艿脑O計,仔細看RuntimeException

    A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught.

    -可能在執行方法期間拋出但未被捕獲的 RuntimeException 的任何子類都無需在 throws 子句中進行聲明。

    也就是說你的應用應該不去“關心”(說不關心是不服責任的,但只是你不應該試圖實例化它的字類)RuntimeException,就如同你不應該關心Error的產生與處理一樣!RuntimeException描述的是程序的錯誤引起來的,因該由程序負擔這個責任!(<B>從責任這個角度看Error屬于JVM需要負擔的責任;RuntimeException是程序應該負擔的責任;checked exception 是具體應用負擔的責任</B>)

    那就有人會問,那我該關心什么!答案就是除了Error與RuntimeException,其他剩下的異常都是你需要關心的,而這些異常類統稱為Checked Exception,至于Error與RuntimeException則被統稱為Unchecked Exception.


    異常的概念就這些了,即使你在網絡上搜索也就不過如此,是不是感覺到有點清晰又有點模糊?那么怎么該如何在這樣單薄而模糊的概念下設計J2EE的異??蚣苣兀?/P>


    解決方案:J2EE異常框架

    我們拿一個模擬的例子來說明異??蚣艿脑O計過程,比如我們要對外提供doBusiness()這個業務方法

    public void doBusiness() throws xxxBusinessException

    當客戶端調用這樣的方法的時候應該這樣處理異常(包括處理RuntimeException , checked exception)
    <B>記住,無論如何我們都不希望或者確切的說是不應該將RuntimeException這樣的異常暴露給客戶的,因為他們沒有解決這個問題的責任!</B>
    我們暫時將Struts中的某個Action看作時客戶端,其中doExecute(....)要調用doBusiness()這個方法

    public void doAction(......)
    {
     try
     {

      xxx.doBusiness();
     }
     catch(Exception e)
     {
       if(e instanceof RuntimeException) 
       {
        // catch runtime exception
        // 你可以在這里將捕獲到的RuntimeException
        // 將異常通知給某個負責此程序的程序員,讓他知道他
        // 自己犯了多么低級的錯誤!


       }else
       {
        //checked exception such as xxxBusinessException
        //將這樣的異常暴露給客戶顯示    

       }

     }
    }

    我們可以這樣設計xxxBusinessException

    public class xxxBusinessException extends ApplicationException
    {
        public xxxBusinessException(String s){
            super(s);

    };

    import java.io.PrintStream;
    import java.io.PrintWriter;
    public class ApplicationException extends Exception {
           /** A wrapped Throwable */
           protected Throwable cause;
           public ApplicationException() {
               super("Error occurred in application.");
           }
           public ApplicationException(String message)  {
               super(message);
           }
           public ApplicationException(String message, Throwable cause)  {
               super(message);
               this.cause = cause;
           }
           // Created to match the JDK 1.4 Throwable method.
           public Throwable initCause(Throwable cause)  {
               this.cause = cause;
               return cause;
           }
           public String getMessage() {
               // Get this exception's message.
               String msg = super.getMessage();
               Throwable parent = this;
               Throwable child;
               // Look for nested exceptions.
               while((child = getNestedException(parent)) != null) {
                   // Get the child's message.
                   String msg2 = child.getMessage();
                   // If we found a message for the child exception,
                   // we append it.
                   if (msg2 != null) {
                       if (msg != null) {
                           msg += ": " + msg2;
                       } else {
                           msg = msg2;
                       }
                   }
                   // Any nested ApplicationException will append its own
                   // children, so we need to break out of here.
                   if (child instanceof ApplicationException) {
                       break;
                   }
                   parent = child;
               }
               // Return the completed message.
               return msg;
           }
           public void printStackTrace() {
               // Print the stack trace for this exception.
               super.printStackTrace();
               Throwable parent = this;
               Throwable child;
               // Print the stack trace for each nested exception.
               while((child = getNestedException(parent)) != null) {
                   if (child != null) {
                       System.err.print("Caused by: ");
                       child.printStackTrace();
                       if (child instanceof ApplicationException) {
                           break;
                       }
                       parent = child;
                   }
               }
           }
           public void printStackTrace(PrintStream s) {
               // Print the stack trace for this exception.
               super.printStackTrace(s);
               Throwable parent = this;
               Throwable child;
               // Print the stack trace for each nested exception.
               while((child = getNestedException(parent)) != null) {
                   if (child != null) {
                       s.print("Caused by: ");
                       child.printStackTrace(s);
                       if (child instanceof ApplicationException) {
                           break;
                       }
                       parent = child;
                   }
               }
           }
           public void printStackTrace(PrintWriter w) {
               // Print the stack trace for this exception.
               super.printStackTrace(w);
               Throwable parent = this;
               Throwable child;
               // Print the stack trace for each nested exception.
               while((child = getNestedException(parent)) != null) {
                   if (child != null) {
                       w.print("Caused by: ");
                       child.printStackTrace(w);
                       if (child instanceof ApplicationException) {
                           break;
                       }
                       parent = child;
                   }
               }
           }
           public Throwable getCause()  {
               return cause;
           }
    }

    而"聰明"的讀者肯定要問我那doBusiness()這個業務方法該如何包裝異常呢?

     public void doBusiness() throw xxxBusinessException
     {
       try
       {
         execute1(); // if it throw exception1

         exexute2(); // if it throw exception 2

         .... .....

       }
       catch (exception1 e1)
       {
        throw new xxxBusinessException(e1);
       }
       catch(exception2 e2)
       {
        throw new xxxBusinessException(e2);
       }
       ........
     }

     也可以這樣

     public void doBusiness() throw xxxBusinessException
     {
       try
       {
         execute1(); // if it throw exception1

         exexute2(); // if it throw exception 2

         .... .....

       }
       catch (Exception e)
       {
        // 注意很多應用在這里根本不判斷異常的類型而一股腦的采用
        // throw new xxxBusinessException(e);
        // 而這樣帶來的問題就是xxxBusinessException"吞掉了"RuntimeException
        // 從而將checked excption 與unchecked exception混在了一起!

        // 其實xxxBusinessException屬于checked excpetion ,它根本不應該也不能夠理睬RuntimeException
        if(! e instanceof RuntimeException) throw new xxxBusinessException(e);
       }
     }


    總結
     1。JAVA的異常分為兩類: checked exception & unchecked excpetion
     2。應用開發中產生的異常都應該集成自Exception 但都屬于checked excpetion類型
     3。應用中的每一層在包裝并傳遞異常的時候要過濾掉RuntimeException!
     4。從責任這個角度看Error屬于JVM需要負擔的責任;RuntimeException是程序應該負擔的責任;checked exception 是具體應用負擔的責任
     5。無論如何我們都不希望或者確切的說是不應該將RuntimeException這樣的異常暴露給客戶的,因為他們沒有解決這個問題的責任!

    posted @ 2006-03-02 20:50 killvin| 編輯 收藏

    Nasted Class 的介紹,請詳見參考

    今天討論的不是不是內部類的概念,而是具體使用的一個場景-如何在內部類中返回外部對象

    看一段代碼

    import java.util.LinkedList;
    import java.util.List;


    public class OuterClass
    {

     private List listeners = new LinkedList();

     public void addListeners(IListener listener)
     {
      this.listeners.add(listener);
     }


     private OuterClass outer = this;  (1)
     private class InnterClass
     {
      public void publish()
      {
       //將事件發布出去 (2)
       for(int i=0;i < listeners.size();i++)
       {
        IListener listener = (IListener) listeners.get(i);
        listener.receiveEvent(outer);
       }

      }
     }


     public void execute()
     {
      InnterClass in = new InnterClass(); (3)
      in.publish();

     }
    }

    public interface IListener
    {

    public void receiveEvent(OuterClass obj);
    }

    你可能覺得這個例子很別扭,在哪里讓你覺得難受呢?其實問題的關鍵就在于接口IListener的定義,這里需要給receiveEvent方法傳遞的參數是外部對象?。▌e激動,下面我會說明需要傳遞的一個場景)

    場景

    在一個GUI系統中,我們要在畫板WorkSpace(WorkSpace實現了IListener接口)上產生一顆樹,但樹中的每個節點的產生(繪圖)是我們不知道的算法,系統只為我們提供了一些繪圖的接口,并返回元素的句柄!看來我們需要"包裝"一下這個繪圖的句柄Brush(其實我把它叫做筆刷,因為它只知道如何"刷"出圖像來,就這點本事?。┎ν馓峁┕濣cNode這樣一個通用的類。

    此時Node與Brush的關系就很微妙了,不過我們可以拋開這些外表,看到Node與Brush其實就是外部類與內部類的關系?。?STRONG>第一步完成了:確定了兩者的關系

    然而,事情沒有這么簡單,Node類必須處理一些事件,而這些事件理所當然只有Brush能夠看懂,而Node根本不知道這樣的事件處理過程,現在有兩個辦法:辦法一,讓Node實現Brush所有的事件;辦法二,把Brush返回回去,讓它來處理自己的事件,看來辦法二是個好主意,因為我可以不關心事件的種類?。?STRONG>第二步完成了:確定了事件處理的責任

    還沒完呢,你肯定不希望畫板WorkSpace面對的是繪圖的句柄Brush這樣的對象,相反你只希望WokSpace只知道Node的存在!IListener接口中receiveEvent方法的參數定義為OuterClass 就由此而來?。?STRONG>第三步完成:接口的定義

    public interface IListener
    {

    public void receiveEvent(OuterClass obj);
    }

     既然說清楚了這個問題(應該比較清楚了吧?)那改如何實現這樣一個蹩腳而有無可奈何的設計呢?讓我們回憶一下內部類,內部類擁有訪問外部類的方法與屬性的權限

     private OuterClass outer = this;  - 這個對外部類的引用就是為內部類的訪問準備的

     private class InnterClass
     {
      public void publish()
      {
       //將事件發布出去 

       for(int i=0;i < listeners.size();i++)
       {
        IListener listener = (IListener) listeners.get(i);
        listener.receiveEvent(outer);  - 這里不可以返回this,因為this代表的是內部類自己
       }
      }

     

     

    參考

    Java Nested class   http://blog.csdn.net/Killvin/archive/2006/01/10/574983.aspx

    初識Java內部類    http://blog.csdn.net/killvin/archive/2006/01/10/574991.aspx

    posted @ 2006-03-02 20:49 killvin| 編輯 收藏

    當你顯式的使用session.save()或者session.update()操作一個對象的時候,實際上是用不到unsaved-value的。某些情況下(父子表關聯保存),當你在程序中并沒有顯式的使用save或者update一個持久對象,那么Hibernate需要判斷被操作的對象究竟是一個已經持久化過的持久對象,是一個尚未被持久化過的內存臨時對象。例如:

    java代碼: 

      Session session = ...;
      Transaction tx = ...;
      
      Parent parent = (Parent) session.load(Parent.class, id);
      
      Child child = new Child();
      child.setParent(parent);
      child.setName("sun");
      
     10 parent.addChild(child);
     11 s.update(parent);
     12 
     13 s.flush();
     14 tx.commit();
     15 s.close();



    在上例中,程序并沒有顯式的session.save(child); 那么Hibernate需要知道child究竟是一個臨時對象,還是已經在數據庫中有的持久對象。如果child是一個新創建的臨時對象(本例中就是這種情況),那么Hibernate應該自動產生session.save(child)這樣的操作,如果child是已經在數據庫中有的持久對象,那么Hibernate應該自動產生session.update(child)這樣的操作。

    因此我們需要暗示一下Hibernate,究竟child對象應該對它自動save還是update。在上例中,顯然我們應該暗示Hibernate對child自動save,而不是自動update。那么Hibernate如何判斷究竟對child是save還是update呢?它會取一下child的主鍵屬性 child.getId() ,這里假設id是 java.lang.Integer類型的。如果取到的Id值和hbm映射文件中指定的unsave-value相等,那么Hibernate認為child是新的內存臨時對象,發送save,如果不相等,那么Hibernate認為child是已經持久過的對象,發送update。

    unsaved-value="null" (默認情況,適用于大多數對象類型主鍵 Integer/Long/String/...)

    當Hibernate取一下child的Id,取出來的是null(在上例中肯定取出來的是null),和unsaved-value設定值相等,發送save(child)

    當Hibernate取一下child的id,取出來的不是null,那么和unsaved-value設定值不相等,發送update(child)

    例如下面的情況:

    java代碼: 

      Session session = ...;
      Transaction tx = ...;
      
      Parent parent = (Parent) session.load(Parent.class, id);
      Child child = (Child) session.load(Child.class, childId);
      
      child.setParent(parent);
      child.setName("sun");
      
     10 parent.addChild(child);
     11 s.update(parent);
     12 
     13 s.flush();
     14 tx.commit();
     15 s.close();



    child已經在數據庫中有了,是一個持久化的對象,不是新創建的,因此我們希望Hibernate發送update(child),在該例中,Hibernate取一下child.getId(),和unsave-value指定的null比對一下,發現不相等,那么發送update(child)。

    BTW: parent對象不需要操心,因為程序顯式的對parent有load操作和update的操作,不需要Hibernate自己來判斷究竟是save還是update了。我們要注意的只是child對象的操作。另外unsaved-value是定義在Child類的主鍵屬性中的。

    java代碼: 

     1 <class name="Child" table="child">
     2 <id column="id" name="id" type="integer" unsaved-value="null">
     3   <generator class="identity"/>
     4 </id>
     5 ...
     6 </class>



    如果主鍵屬性不是對象型,而是基本類型,如int/long/double/...,那么你需要指定一個數值型的unsaved-value,例如:

    java代碼: 

     1 unsaved-null="0"



    在此提醒大家,很多人以為對主鍵屬性定義為int/long,比定義為Integer/Long運行效率來得高,認為基本類型不需要進行對象的封裝和解構操作,因此喜歡把主鍵定義為int/long的。但實際上,Hibernate內部總是把主鍵轉換為對象型進行操作的,就算你定義為int/long型的,Hibernate內部也要進行一次對象構造操作,返回給你的時候,還要進行解構操作,效率可能反而低也說不定。因此大家一定要扭轉一個觀點,在Hibernate中,主鍵屬性定義為基本類型,并不能夠比定義為對象型效率來的高,而且也多了很多麻煩,因此建議大家使用對象型的Integer/Long定義主鍵。

    unsaved-value="none"和
    unsaved-value="any"

    主主要用在主鍵屬性不是通過Hibernate生成,而是程序自己setId()的時候。

    在這里多說一句,強烈建議使用Hibernate的id generator,或者你可以自己擴展Hibernate的id generator,特別注意不要使用有實際含義的字段當做主鍵來用!例如用戶類User,很多人喜歡用用戶登陸名稱做為主鍵,這是一個很不好的習慣,當用戶類和其他實體類有關聯關系的時候,萬一你需要修改用戶登陸名稱,一改就需要改好幾張表中的數據。偶合性太高,而如果你使用無業務意義的id generator,那么修改用戶名稱,就只修改user表就行了。

    由這個問題引申出來,如果你嚴格按照這個原則來設計數據庫,那么你基本上是用不到手工來setId()的,你用Hibernate的id generator就OK了。因此你也不需要了解當

    unsaved-value="none"和
    unsaved-value="any"

    究竟有什么含義了。如果你非要用assigned不可,那么繼續解釋一下:

    unsaved-value="none" 的時候,由于不論主鍵屬性為任何值,都不可能為none,因此Hibernate總是對child對象發送update(child)

    unsaved-value="any" 的時候,由于不論主鍵屬性為任何值,都肯定為any,因此Hibernate總是對child對象發送save(child)

    大多數情況下,你可以避免使用assigned,只有當你使用復合主鍵的時候不得不手工setId(),這時候需要你自己考慮究竟怎么設置unsaved-value了,根據你自己的需要來定。

    BTW: Gavin King強烈不建議使用composite-id,強烈建議使用UserType。

    因此,如果你在系統設計的時候,遵循如下原則:

    1、使用Hibernate的id generator來生成無業務意義的主鍵,不使用有業務含義的字段做主鍵,不使用assigned。

    2、使用對象類型(String/Integer/Long/...)來做主鍵,而不使用基礎類型(int/long/...)做主鍵

    3、不使用composite-id來處理復合主鍵的情況,而使用UserType來處理該種情況。


    那么你永遠用的是unsaved-value="null" ,不可能用到any/none/..了。

    posted @ 2006-03-02 20:45 killvin| 編輯 收藏

    先來點概念:

    在Hibernate中,最核心的概念就是對PO的狀態管理。一個PO有三種狀態:

    1、未被持久化的VO
    此時就是一個內存對象VO,由JVM管理生命周期

    2、已被持久化的PO,并且在Session生命周期內
    此時映射數據庫數據,由數據庫管理生命周期

    3、曾被持久化過,但現在和Session已經detached了,以VO的身份在運行
    這種和Session已經detached的PO還能夠進入另一個Session,繼續進行PO狀態管理,此時它就成為PO的第二種狀態了。這種PO實際上是跨了Session進行了狀態維護的。

    在傳統的JDO1.x中,PO只有前面兩種狀態,一個PO一旦脫離PM,就喪失了狀態了,不再和數據庫數據關聯,成為一個純粹的內存VO,它即使進入一個新的PM,也不能恢復它的狀態了。

    Hibernate強的地方就在于,一個PO脫離Session之后,還能保持狀態,再進入一個新的Session之后,就恢復狀態管理的能力,但此時狀態管理需要使用session.update或者session.saveOrUpdate,這就是Hibernate Reference中提到的“requires a slightly different programming model ”

    現在正式進入本話題:

    簡單的來說,update和saveOrUpdate是用來對跨Session的PO進行狀態管理的。

    假設你的PO不需要跨Session的話,那么就不需要用到,例如你打開一個Session,對PO進行操作,然后關閉,之后這個PO你也不會再用到了,那么就不需要用update。

    因此,我們來看看上例:
    java代碼: 

     1 Foo foo=sess.load(Foo.class,id);
     2 foo.setXXX(xxx);
     3 sess.flush();
     4 sess.commit();



    PO對象foo的操作都在一個Session生命周期內完成,因此不需要顯式的進行sess.update(foo)這樣的操作。Hibernate會自動監測到foo對象已經被修改過,因此就向數據庫發送一個update的sql。當然如果你非要加上sess.update(foo)也不會錯,只不過這樣做沒有任何必要。

    而跨Session的意思就是說這個PO對象在Session關閉之后,你還把它當做一個VO來用,后來你在Session外面又修改了它的屬性,然后你又想打開一個Session,把VO的屬性修改保存到數據庫里面,那么你就需要用update了。

    java代碼: 

      // in the first session
      Cat cat = (Cat) firstSession.load(Cat.class, catId);
      Cat potentialMate = new Cat();
      firstSession.save(potentialMate);
      
      // in a higher tier of the application
      cat.setMate(potentialMate);
      
      // later, in a new session
     10 secondSession.update(cat)// update cat
     11 secondSession.update(mate); // update mate
     12 



    cat和mate對象是在第一個session中取得的,在第一個session關閉之后,他們就成了PO的第三種狀態,和Session已經detached的PO,此時他們的狀態信息仍然被保留下來了。當他們進入第二個session之后,立刻就可以進行狀態的更新。但是由于對cat的修改操作:cat.setMate(potentialMate); 是在Session外面進行的,Hibernate不可能知道cat對象已經被改過了,第二個Session并不知道這種修改,因此一定要顯式的調用secondSession.update(cat); 通知Hibernate,cat對象已經修改了,你必須發送update的sql了。

    所以update的作用就在于此,它只會被用于當一個PO對象跨Session進行狀態同步的時候才需要寫。而一個PO對象當它不需要跨Session進行狀態管理的時候,是不需要寫update的。

    再談談saveOrUpdate的用場:

    saveOrUpdate和update的區別就在于在跨Session的PO狀態管理中,Hibernate對PO采取何種策略。

    例如當你寫一個DAOImpl的時候,讓cat對象增加一個mate,如下定義:
    java代碼: 

    1 public void addMate(Cat cat, Mate mate) {
     2     Session session = ...;
     3     Transacton tx = ...;
     4     session.update(cat);
     5     cat.addMate(mate);
     6     tx.commit();
     7     session.close();
     8 };



    顯然你是需要把Hibernate的操作封裝在DAO里面的,讓業務層的程序員和Web層的程序員不需要了解Hibernate,直接對DAO進行調用。

    此時問題就來了:上面的代碼運行正確有一個必要的前提,那就是方法調用參數cat對象必須是一個已經被持久化過的PO,也就是來說,它應該首先從數據庫查詢出來,然后才能這樣用。但是業務層的程序員顯然不知道這種內部的玄妙,如果他的業務是現在增加一個cat,然后再增加它的mate,他顯然會這樣調用,new一個cat對象出來,然后就addMate:

    java代碼: 

     1 Cat cat = new Cat();
     2 cat.setXXX();
     3 daoimpl.addMate(cat,mate);



    但是請注意看,這個cat對象只是一個VO,它沒有被持久化過,它還不是PO,它沒有資格調用addMate方法,因此調用addMate方法不會真正往數據庫里面發送update的sql,這個cat對象必須先被save到數據庫,在真正成為一個PO之后,才具備addMate的資格。

    你必須這樣來操作:

    java代碼: 

     1 Cat cat = new Cat();
     2 cat.setXXX();
     3 daoimpl.addCat(cat);
     4 daoimpl.addMate(cat, mate);



    先持久化cat,然后才能對cat進行其他的持久化操作。因此要求業務層的程序員必須清楚cat對象處于何種狀態,到底是第一種,還是第三種。如果是第一種,就要先save,再addMate;如果是第三種,就直接addMate。

    但是最致命的是,如果整個軟件分層很多,業務層的程序員他拿到這個cat對象也可能是上層Web應用層傳遞過來的cat,他自己也不知道這個cat究竟是VO,沒有被持久化過,還是已經被持久化過,那么他根本就沒有辦法寫程序了。

    所以這樣的DAOImpl顯然是有問題的,它會對業務層的程序員造成很多編程上的陷阱,業務層的程序員必須深刻的了解他調用的每個DAO對PO對象進行了何種狀態管理,必須深刻的了解他的PO對象在任何時候處于什么確切的狀態,才能保證編程的正確性,顯然這是做不到的,但是有了saveOrUpdate,這些問題就迎刃而解了。

    現在你需要修改addMate方法:

    java代碼: 

    1 public void addMate(Cat cat, Mate mate) {
     2     Session session = ...;
     3     Transacton tx = ...;
     4     session.saveOrUpdate(cat);
     5     cat.addMate(mate);
     6     tx.commit();
     7     session.close();
     8 };



    如上,如果業務層的程序員傳進來的是一個已經持久化過的PO對象,那么Hibernate會更新cat對象(假設業務層的程序員在Session外面修改過cat的屬性),如果傳進來的是一個新new出來的對象,那么向數據庫save這個PO對象。

    BTW: Hibernate此時究竟采取更新cat對象,還是save cat對象,取決于unsave-value的設定。

    這樣,業務層的程序員就不必再操心PO的狀態問題了,對于他們來說,不管cat是new出來的對象,只是一個VO也好;還是從數據庫查詢出來的的PO對象也好,全部都是直接addMate就OK了:

    posted @ 2006-03-02 20:44 killvin| 編輯 收藏

    今天同事問我Struts如何解決文件上傳的問題,一時間沒有想起來,就到google查了一下,果然在Struts Wiki上就有非常詳細的解釋,抄錄如下,詳細的請看(http://wiki.apache.org/struts/StrutsFileUpload)



    StrutsFileUpload
    File Upload - Simple Example
    HTML
    This isn't specific to Struts, but gives a simple example of the HTML required to upload a single file.

    Two things are needed in the html page. Firstly, the form needs to specify an enctype of multipart/form-data and secondly an form control of type file.

    JSP
    The above HTML can be generated using the Struts tags in the following way

    <html:form action="/uploadMyFile.do" enctype="multipart/form-data">

    Select File: <html:file property="myFile">

    <html:submit value="Upload File"/>

    </html:form>

    ActionForm
    The ActionForm needs a property of type FormFile.

    Regular ActionForms
    import org.apache.struts.upload.FormFile;

    public class MyActionForm extends ActionForm {

    private FormFile myFile;

    public void setMyFile(FormFile myFile) {
    this.myFile = myfile;
    }

    public FormFile getMyFile() {
    return myFile;
    }
    }

    Dyna ActionForms
    In the struts-config.xml

    <form-bean name="myForm" type="org.apache.struts.action.DynaActionForm">
    <form-property name="myFile" type="org.apache.struts.upload.FormFile"/>
    </form-bean>

    Whats Needed in the Action
    Nothing special really, just retrieve the FormFile from the ActionForm, as you would any other property, and process it as you like. You can get the file name, size and file contents from the FormFile.

    public ActionForward execute(ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response) throws Exception {

    MyActionForm myForm = (MyActionForm)form;

    // Process the FormFile
    FormFile myFile = myForm.getMyFile();
    String contentType = myFile.getContentType();
    String fileName = myFile.getFileName();
    int fileSize = myFile.getFileSize();
    byte[] fileData = myFile.getFileData();
    ...
    }

    File Upload Configuration
    The following parameters can be set in the <controller> element of the struts-config.xml to configure file upload:

    bufferSize - The size (in bytes) of the input buffer used when processing file uploads. Default is 4096.

    maxFileSize - The maximum size (in bytes) of a file to be accepted as a file upload. Can be expressed as a number followed by a "K", "M", or "G", which are interpreted to mean kilobytes, megabytes, or gigabytes, respectively. Default is 250M.

    multipartClass - The fully qualified Java class name of the multipart request handler class to be used with this module. Defaults is org.apache.struts.upload.CommonsMultipartRequestHandler.

    tempDir - Temporary working directory to use when processing file uploads.

    Above taken from the Configuration section in the User Guide.

    Plugging in an Alternative File Upload Mechanism
    By default Struts uses Commons File Upload.

    Alternative implementations can be plugged as long as they implement the org.apache.struts.upload.MultipartRequestHandler interface and Struts configured to use that implementation by specifying it in the multipartClass parameter in the <controller> element of the struts-config.xml

    Fair Warning: The MultipartRequestHandler interface is almost certain to change in a Struts 1.3 or higher release.

    posted @ 2006-03-02 20:43 killvin| 編輯 收藏

            以前總是認為php只是一種jsp的翻版,沒有啥意思!不象J2EE的體系結構那樣,有應用服務器的概念,只是一個簡單的服務器腳本,而昨天的看法卻讓我大跌眼鏡,其實php和java、python都處在一個層次上,甚至比java更有優勢!不應該僅僅只是將php和jsp看作是同樣的檔次,這就好比是告訴寫python的程序員,你們開發不了應用程序一樣的讓別人笑掉大牙,甚至懷疑你的腦子有毛病,zope就是一個很好的例子?。炐愕膽梅掌?,就是用Python開發的!
            
            我們更應該將php看作是一個語言,和python同樣優秀(當然php和python的定位不同,也就是因為這樣的定位,而讓人產生的錯覺?。。┤绻鸓hp將自己定位如python一樣!并在語言這個級別更貼近面向對象的思維模式,那Python也許就不會如此的火爆!
            
            看來php之父并沒有將語言向python靠攏的意思,也就是說這樣的語言目前的生存空間僅僅只限于服務器端的腳本!這是它的領地!但除非是它閉著眼鏡,不然沒有人會愿意看到html的代碼里寫這<?php>這樣的代碼!這就好比是在沒有webwork 、struts這樣優秀的web框架出現之前,jsp讓人惡心的原因,如果Php的領域沒有這樣的MVC框架出現,那我情愿還是選擇jsp!!出于學習我會選擇Python,但永遠不會選擇php......
            
            告訴我你知道的php世界里優秀的web框架!也許我會改變看法!但我不會選擇重新發明輪子!!

     

    posted @ 2006-03-02 20:26 killvin| 編輯 收藏

    Thinking in Python

    You can download the current version of Thinking in Python here. This includes the BackTalk comment collection system that I built in Zope.

    The page describing this project is here.

    The current version of the book is 0.1. This is a preliminary release; please note that not all the chapters in the book have been translated.

    The source code is in the download package. When you unzip everything (remember to use the -a flag if you're on Unix), the code will be put into subdirectories for each chapter.

    This is not an introductory Python book. This book assumes you've learned the basics of Python elsewhere. I personally like Learning Python by Lutz & Ascher, from O'Reilly. Although it does not cover all the Python 2.0, 2.1 and 2.2 features that I use in this book, most books don't at this point.

    However, Learning Python is not exactly a beginning programmer's book, either (although it's possible if you're dedicated). If you're just getting started with programming you might try the free, downloadable A Byte of Python.

    posted @ 2006-03-02 20:23 killvin| 編輯 收藏

    Python是一種不但成熟而且功能也十分強大的面向對象程序設計語言。它的功能性現在已經可以與Java相媲美了。盡管Python有著這樣或者那樣的優點,但是它仍然面對著這樣的一種現實:真正了解Python的人給予了它很高的評價,但是實際上真正了解它的人并不多。在本篇文章里,我將會向您介紹一系列的Python編程資源,這樣您就可以對這種編程語言有更多的了解,并且能夠逐步的開始使用這個優秀的編程語言了。

    開始的第一個步驟

    如果您是剛剛開始了解Python編程語言,你現在就需要一套Python編程軟件,這樣能夠讓你更好的掌握它。正式的Python軟件中包括了程序指令行,圖形化的用戶界面,范例程序代碼以及一整套的相關文件。還有另外一個版本的Python軟件,它的正式名稱是ActivePython。這個叫做ActivePython的版本是由ActiveState公司提供的。ActiveState是生產Python軟件工具以及相關軟件的一家公司。但是,這兩種不同的版本區別何在呢?

    ActivePython版本對于Windows,Linux, 以及Solaris操作系統的二進制形式是可以直接使用的。但是常規的Python軟件雖然比ActivePython支持的操作系統要稍微多一些,但是卻只能夠作為這些操作系統的源代碼來進行下載。ActivePython版本比常規的Python軟件增加了一些內容,并且附帶有更大的標準資源庫。如果對于Python軟件來說您僅僅還處于入門階段的話,我推薦您應該跳過對ActivePython的學習。

    當你安裝了Python軟件之后,你會希望掌握它的編程方法,除非你只是喜歡單純的在你的桌面上增加一個很少使用的可愛快捷方式而已。在國際互聯網上已經有了不少關于Python編程語言的使用指南,請看下面的這些內容:

    • 正式的Python文件中包含了范圍很大的使用指南,并且對您來說,可能是首選的應該瀏覽的網址。
    • Python即時通是一個“Python編程語言的最快的速成班”,這個網址讓學習者通過在一些頁面上對一些程序代碼范例進行結構,輸入,輸出,分類以及變量的控制來熟悉這種編程語言。
    • 投身于Python編程語言的世界是一個綜合性的Python軟件使用指南。它是以電子書的形式來與大家見面的。這款電子書是為了那些有經驗的使用Perl, Java,或者 Visual Basic編程語言的軟件設計人員轉向使用Python而編寫的,所以它是很有針對性的。
    • Python編程語言的全方位接觸是一篇在UnixWorld上登出的時間已經比較久的有關Python編程語言的文章。這篇文章向讀者們講述了如何使用Python編程語言來完成一些比較簡單的小任務,比如說編制一個善意的電子郵件玩笑軟件來讓你的朋友們嚇一跳或者大吃一驚。
    有關Python編程語言總的說明以及資源

    如果您已經開始接觸Python編程語言并且已經有了一定的使用Python進行程序設計的經驗之后,您可能會在這個過程中產生一些問題,或者您會希望得到有關Python編程語言更多的相關資料。為了滿足您這方面的需要,我會向您提供一些網絡上的資源作為您的文獻資料庫。下面列出的這些條都是其中比較好的一些網絡資源:

    • Parnassus山的拱頂是一個巨大的Python程序代碼庫,其中包含了成千上萬可以使用的Python程序模塊。這些程序模塊中有程序設計,圖形化處理方法以及國際互聯網程序設計數據庫。在這個站點上還有一些已經完成的應用軟件,并且可以作為程序源代碼讓您下載下來。
    • Python號星際旅行船是Digital Creations公司制作的一個有關Python編程語言的免費網絡社區。其中包含了總的數據信息資料庫,并且還有成員或者“其星際旅行船宇航員”目錄,在這里能夠和一些Python編程高手進行交流。
    • O’Reily網絡的網絡站點ONLamp.com中擁有關于Python程序開發的部分,在這個部分中,學習者能夠找到有關的一些新聞,論壇,問題解答留言板以及許多相關的文章。
    • Faqts.com中有一個Python程序設計知識數據庫,其中包含了可以隨意查詢的有關Python程序設計的問題以及解決方法。這些資料都是使用過Python語言的程序設計人員提供的。
    • Python-URL!是Dr.Dobb周刊的電子郵件摘要,其中包含了與Python程序設計語言相關的新聞,公告,以及國際互聯網新聞組活動。
    • 有不少針對Python編程語言的電子郵件討論列表。如果您不能在國際互聯網上找到您所需要的答案,您可以通過這種方法從遠方獲取正確的解決方法。
    一些植入Python軟件的有用資源

    Python編程語言擁有極好的擴充性,并且是一種很好的腳本語言。Python作為編程語言能夠非常好的植入大量的應用軟件。這是近來有關Python編程語言活動的中心內容,我們能夠從theKompany中的VeePee了解到更多的信息。VeePee是一種使用圖形用戶界面的腳本環境,這是被設計用來植入Linux GNOME或者 KDE應用軟件,并且能夠對這兩種應用軟件提供腳本支持。我要一再提示您的是,ActiveState對于在.NET中使用Python編程語言擁有經過實驗室檢驗的具體經驗。這樣就允許Python編程語言能夠繼承并且訪問在.NET框架中的對象,并且為其它的.NET編程語言提供了一種腳本支持。

    IDE(集成開發環境)以及編輯器

    就像大家知道的那樣,我是一個沉迷于IDE的人:當我編制程序的時候,如果有code completion,彈出式對話框,菜單以及工具條,這樣我才會感覺舒服一些。我清除的知道在這個世界上還有許許多多像我一樣的人。很幸運的一點是,對我們這些人來說,雖然我們沉迷于此,但是這個世界上有數量繁多的非常優秀的Python軟件的IDE能夠供我們使用,雖然其中的相當大一部分并不是免費的:

    • PythonWorks軟件作品是一個具有商業品質的IDE。它具有一個Tkinter的圖形化用戶界面,程序代碼倉庫,程序調試器,“聰明的”編輯器以及在線幫助功能。并且它是同時支持windows,Linux,以及Solaris這三種操作系統的。其零售價格為395美元。其中還包括免費的評估服務。
    • Wing IDE是一個用戶化的IDE,它能夠支持宏,code completion,多面手程序調試器以及源文件管理工具。Wing對于Linux 以及Windows操作系統的零售價格大約為100美元,但是對于使用公開化源程序來說是免費提供的?,F在,免費版本已經可以從多種渠道獲得了。
    • PythonWin是一個免費的只能在Windows操作系統中使用的圖形化用戶界面創建工具。這個工具向程序設計人員提供了一個界面資料庫,這個資料庫只是針對微軟公司的系列產品的。
    • 從我們在ActiveState公司中的朋友(你能證明他們百分之一百的熱愛Python語言么?),我們能夠獲得免費的,經過實驗室驗證的插件IDE程序。這些程序都是為Visual Studio.NET特別設計的,并且已經被命名為Visual Python。請不要把Visual Python和theKompany的VeePee軟件混為一談,它們是兩個完全不同的產品。

    在本文中提到的這些資源能夠幫助你正式的開始使用Python語言進行程序設計的工作,并且還能夠幫助你找到獲取這些資源的途徑。你只要記住的一點是:沒有人喜歡Spanish Inquisitionn(西班牙宗教法庭,在1480-1834年之間存在的天主教法庭,以殘酷迫害異端著稱)。你不會真的期望只是通過一篇文章就能夠獲取所有有關Python編程語言的知識吧?這種愿望是非常不切合實際的。

    posted @ 2006-03-02 20:22 killvin| 編輯 收藏

    PHP5的面向對象特性增強了很多,包括功能上和性能上。但是如果用一門面向對象語言來說,PHP還差很遠,簡單的舉個例子,Java和Python都可以通過import來導入類,但是PHP只能用include這種原始的辦法,因而沒有辦法區分命名空間,做大項目還是不行的。 此外PHP的致命缺陷是它只是Web Server的一個module存在的,沒有App Server來進行復雜業務的管理。因此沒有辦法承擔大型項目的開發和運行。像國內的eachnet原來是用PHP的,交易量和復雜度上升以后,后臺全部用BEA的Tuexdo來做,PHP只是調用。(不過也有人認為,沒有App Server也是一種優勢,就是部署簡單) Python和PHP的本質區別就在于Python有Zope這樣的App Server,因此Python應該劃入Java/C#的陣營,而和PHP要劃清界限了。

    posted @ 2006-03-02 20:20 killvin| 編輯 收藏

    接觸 Python 是兩年前的事了。那時因為要寫個程式來分析數值資料,開始的時候打算用自己最熟的 PHP,試了幾下,很不順手。后來轉用了 Perl,開始的時候都挺順利的。直至有一天整理程式,想把寫好的幾個東西整理成一個工具箱給自己用,試了半天就放棄了:根本是精神虐待。反省后覺得自己學 Perl 只會走捷徑,打算上網找些基本的學習文件從頭努力,但竟然找到了 Python 的網頁。就這樣開始了學 Python,而且是欲罷不能。現在我要寫什么,基本上都是先考慮用 Python,一來實在好用,二來用 Python 寫的程式總會有重用的機會,所以手頭的工具越來越多,用起來就更得心應手了。

    如果你以前只會用 Shell Script 或是 DOS Batch File 而想學些其他的東西,Python 會是你的首選,如果你己經會 Perl 或是 Tcl 或是別的什么,那 Python 會令你改變對 scripting language 的想法。

    為什么要學 Python?

    答:好玩。就是這樣簡單。無論是學 Python 還是用 Python,都是一件賞心樂事。當你寫 shell scripts 寫得鬼哭神號時,當你改 perl scripts 改得頭昏腦漲時,有沒有想過寫 script 其實是很好玩的一件事。你可能從來不相信當你寫一套嚴肅而功能強大的程式,你可以用一個如此好玩的程式語言來開發。更難令你相信的是,就算在程式開發的后期,甚至是幾個月以后當你再次想修改它的時候,你依然都會覺得是一件好玩的事。

    Python 能做什么?

    答:倒不如問,Python 不能做什么? Python 已經差不多把一個 scripting language 作為整合工具 (gluing tool) 的強大功能完全發揮了出來,任何程式,只要有個給 Python 用的接口,Python 就可以把它據為己用,和其他的程式一塊使用。而且

      · Python 有發展 XML 的大型函式庫,并開始有大型系統的構建初型。 · 用 Python 寫的 Zope,可以用來架設整個網站 · Python 語言自己是用 C 來寫的,但現已有用純 Java 寫的 Python 語言實現,近期更有用微軟 .NET 方式實現的 Python 翻譯器,由此證明了 Python 語言定義的高度完整。 · Python 已移植到許多的操作平臺上,包括 Linux、Windows、HP、Solaris、Irix、AIX、BeOS、甚至是 Cray。幾乎和 Perl 一樣多。 ·由于 Python 本身是 object oriented,所以很容易和 C++,Java 等整合,尤其在 Visual 當道的 Windows 系統。這點要比 Perl 好許多。

    和其他語言的比較

    一般的說法的這樣的:

    • Shell script 太舊了,無論如何不能用來寫程式。
    • Perl 擅長做服務器的管理,因為它的字串處理功能特別強大,同時有全面的編程接口和操作系統相連。自己用沒問題,就是不懂人家在寫什么。
    • PHP 擅長于網頁,因為它可以嵌入在 HTML 文件中并支援各類資料庫。但一離開網頁就左支右絀。
    • Python 特別適合一般的需求,因為它語法簡明,容易維護,有很多模組可用,而且容易做出圖形介面。但它并不是 object oriented 并不純,速度也較 Perl 慢一些。
    • Tcl/Tk 基本變數形態只有文字串,每次算術都是從文字變數字再變文字,鳴,沒什么好提的了吧。
    • Java 太偉大了,我可不想每次開始寫程式時都幻想要完成一個跨企業的解決方案,引入幾十個標頭檔,寫完了才明白原來我只需它讀一個用戶指定的文件,畫兩條直線就行了。唉,落差太大。
    • Ruby 太新了,要正式應用是將來的事。

    但事實上我個人的感受最深的是它比較好玩的說法。雖然像是不認真,但一種可以讓人寓編程于娛樂的程式語言,正是程式語言的設計終極嘛。

    Python 的作者是數學家,而 Perl 的作者則是個天才程式員。因此這兩種語言的風格回異:Python 語法鋪排的很整潔,相同功能的程式由不同人寫出來的樣子都很像,這些一致的、前后呼應的程式對于系統的拓展和整合十分重要。因此閱讀 Python 的程式感覺上很暢順。Perl 的語法則很濃縮,而且經常會有些意想不到的巧思,可惜這些每每體現在字里行間的妙著對整個程式的可讀性帶來負面影響。因而一個短小的 Perl 程式往往令人愛不釋手,但當它越來越長時總會變成程式員的永痛。

    至于PHP,因它的發展歷史一直是圍繞著 web page,所以做網頁設計是十分方便的,但是寫一般的工作則平平無奇。比如說控制執行緒,處理進程間的訊息傳送以致做圖形用戶介面等都久奉,這是因為從 WEB 的角度,這些并不重要:圖形介面,HTML 就己經是了,而程式的執行和互相合作則是 Web Server 要考慮的事。

    我推薦 Python 是因為我覺得它比較適合用作一般性程式編寫和系統整合,尤其是在科學和教育領域中。我并不是說大家沒本事學好 Perl,只是很多時候我們并不需要用到 Perl 的精妙之處,更多的時候用一個清楚直接的描述要比用一個智力題級的招數更有好處。除了語法的設計較為整潔外,Python 還有很好的對電腦圖形的支援,也有十分快的數學陣列模組。

    而它最方便的地方是很容易的把其他的程式整合在一起,譬如說你有個幾年前寫的 C 程式,只要修改很小的一部份就可以編譯成 Python 可用的模組。事實上已經有自動幫你做這些修改的程式了,人們也己為許多各種好用的函式庫做出了 Python binding,如 OpenGL、GTK、QT、GD、甚至于解方程組的、找頻譜的都有。

    而在使用了一段時間后,我開始體會下面的說法:

      ‘Python 是設計出來的,而 PHP 和 Perl 是累積而來的?!?/B>

    雖然它們各自都有類似的功能,但在使用時會發現 Python 的寫法總是較為一致,這是因為 Python 從一開始便考慮了它整個的架構。相反,像 Perl 或 PHP 則有很多功能都是后來慢慢加上去的,所以到后期 Perl 的發展越來越難,以致 Perl 6 幾乎要從頭設計。而 PHP 則現在才開始頭痛于構思新的函數名稱 (所有的 PHP 函數都是全域定義的,所以每加一個函數都有可能和已經定義了的名字重復)。為了解決問題,它們各自都開始注意加強 class, object 的支援。但很明顯的是,在程式成熟后再加這些基本功能會令整個語言的框架變得頭重腳輕,無論是寫起來還是用起來都有些不穩的感覺。

    然而 Python 在一開始的時候就把這些考慮了進去:Python 根本就是以物件導向作為設計的基礎。因此在加了許許多多的功能和模組后,它的風采仍舊,清新依然。

    好話說了不少,光說不練可是不行。參考程式示范區的實際例子,看看 Python 到底是怎樣一回事。

    posted @ 2006-03-02 20:17 killvin| 編輯 收藏

    javascript的調試相當繁瑣,alert不能有效地起到調試作用,原因在于:
    1, alert只能在開發期使用,不能在產品交付中繼續alert debug info
    2, 就算在開發期,頻繁的alert會導致調試效率低下。
    一個簡單的log就會有很大幫助。下面就是我在現實生活中使用的javascript log方法。
    把它包含在一個全局import的js里面,以后代碼就可以采用log()的方式來進行了。

    當然,若誰有時間改造為log4j那樣的可以按照分類打開或關閉log,再分上info,error什么的log級別就更好了。不過下面的簡單方法就夠我用拉。

    common.js

    function log(v)
    {
         var s = "<li><font color=blue>"+new Date()+"</font> "+v+"</li>";
         logPanel_log.innerHTML+=s;
    }

    window.onhelp = function()
    {
         logPanel.style.display=logPanel.style.display=='none'?"block":"none";
         return false;
    }

    document.write(
     "<div id=logPanel style='display:none;text-align:right'>"+
        "<button onclick=\"logPanel_log.innerHTML='';\">clear</button>&nbsp;"+
        "<button onclick=\"logPanel_log.innerHTML+='<hr>';\">HR</button>"+
        "<div id='logPanel_log'></div></div>");

    它覆蓋了默認的IE 按F1鍵顯示IE help的行為,現在按下F1就能夠顯示log信息了。

    摘自 redsaga滿江紅 http://www.blogcn.com/User6/caoxg/blog/5705836.html

    posted @ 2006-03-02 20:15 killvin| 編輯 收藏

    JavaScript說分明 

    http://taiwan.cnet.com/enterprise/technology/0,2000062852,20103386,00.htm
    朱仲傑撰  2005/12/28

     

    最近因為AJAX受到Google等網路服務業者的愛用而聲名大噪。也讓這個其實存在很久的程式語言再度成為討論話題。


    JavaScript可以說是全世界使用最普遍的一個語言。雖然就統計資料(註)上來看,用JavaScript為主的專案數量只排在第九名,但我相信就應用的層面來看,絕對遠超過Java與.Net。JavaScript也是最讓人混淆的一個語言,即便是專業的網頁工程師,也不見得每位都能正確的了解它。網頁工程師對它又愛又恨,使用者最能體驗到它所帶來的好處,但常常也是被它搞的昏頭轉向。到底它是個什麼樣神奇的語言呢?

    JavaScript的前世今生

    提到JavaScript,大家第一個聯想到的就是Java,但一切的誤解也就從這開始。要說它跟Java完全沒關係嘛,也不盡然。JavaScript原來的名稱叫LiveScript,是Netscape(網景)為了它的LiveWare平臺所發展出來的一套語言。後來之所以改名叫JavaScript,主要是因為當年Netscape與Sun合作,共同開發網頁上的腳本語言,而剛好Sun正在開發Java語言,初期而且也是以網頁上的應用Java Applet為主,所以LiveScript也就改名為JavaScript。第一個擁有執行JavaScript的瀏覽器Netscape 2.0,發表日期也跟Java一樣,同樣都是1995年,所以算起來今年也是JavaScript的十歲生日。

    JavaScript的版本非常多種,最原始的是由Netscape一路發展過來的,後來NetScape把它交給ECMA製定為標準(ECMA-262),而ECMA所製定出來的稱之為ECMAScript;最喜歡自己也搞一套的微軟,也有自訂的Jscript。理論上,有了標準大家就應該要遵守,不過到目前看來,實際上並非如此。各家瀏覽器相容的程度不一,造成現在混亂的局面,同一個網頁用不同的瀏覽器瀏覽有時結果不盡相同。

    JavaScript沒有你想像中的簡單

    JavaScript是屬於prototype-based的一種語言,而prototype-based又是物件導向程式設計語言(Object-Oriented Programming Language)的一個分枝,所以嚴格說起來,JavaScript也是一種物件導向程式設計語言。光看到「物件導向」這四個字,我想很多人頭皮都已經開始發麻了,不過JavaScript之所以不簡單並不是因為這一點,而是因為它的結構鬆散和型態鬆散(loose typing)所導致。

    你若寫過Java程式的話或許就知道,Java程式必需要Java虛擬機器(JVM)上執行,在執行Java程式之前,它必需是被編譯過的;在編譯的過程中,Java會檢查許許多多的條件,例如最基本的語法、變數的宣告等等。

    JavaScript一樣得依存在一個容器(Container)中才能執行,我們常見到的容器就是瀏覽器(Web Browser),跟Java不同的是,JavaScript的程式不需要事先編譯,它是屬於直譯式的語言,你可以把它想像成看到一行程式碼就執行一行。因為少了編譯時期的檢查,JavaScript就比Java「自由」許多,你可以在網頁上隨意的位置上安插JavaScript程式、可以隨意的建立新的變數來使用;你不用分清楚什麼是數值,什麼是字串、什麼又是物件,通通可以加加(+)在一起,這就會造成相當不嚴謹的開發過程。

    自由是必需要付出代價的,前面提到相容性的混亂,在這又有語法結構上的混亂,這下子可以說是亂上加亂了。網頁工程師之所以對JavaScript又愛又恨,愛是因為透過適當的JavaScript,可以在網頁上創造出許許多多特殊的效果,讓使用者在瀏覽這個網頁時,能有更多的互動。恨是因為市場上極少有以JavaScript為主的整合開發工具(IDE),因為剛剛所說的混亂,造成JavaScript程式很難開發和除錯。

    JavaScript之所以普及,是因為寫JavaScript程式的使用者,不見得是寫Java、C/C++、VB等受過基本程式設計訓練的程式設計師,往往網頁的美工人員才是使用JavaScript的大宗。他們不會也沒時間去學習完整的JavaScript,而是有需要時才在網頁上東加一些、西加一點,這些加上去的JavaScript可能是網頁編輯器所提供的元件、或是網路上分享出來的程式片段。JavaScript一點都不簡單,只是大家都很偷懶,用最簡單的方式來使用它罷了。

    AJAX應用為JavaScript注入強心劑

    JavaScript十年來沒有特別受歡迎,因為WWW盛行以來它和HTML一樣已變成網頁裡最基本的元素,直到AJAX的出現,讓JavaScript大大的受到眾人的注視。AJAX的全名是 Asynchronous JavaScript and XML,JavaScript就是其中之一的主角,AJAX並不是一項新的技術,而是將一些舊有的技術結合起來做一種巧妙的應用,賦予網頁的一股新動力。

    AJAX這個名詞最早是出現在Adaptive Path網站上,一篇由 Jesse James Garrett於2005年2月18號所寫的文章中所提出的(http://www.adaptivepath.com/publications/essays/archives/000385.php)。AJAX是客戶端(瀏覽器)的技術,簡單的說,就是用JavaScript與後端伺服器做資料的溝通,資料採用XML的格式,JavaScript動態地從後端拿到XML格式的資料,重新整理、重組後再顯示到網頁上。AJAX基本上是無關Java或.Net的,甚至PHP也可以跟AJAX結合在一起使用。網路上、雜誌裡介紹AJAX的文章相當的多,在這我就不贅述。在這要提醒大家注意的是,因為AJAX,JavaScript又開始大量的應用在網頁裡,前面所提及的混亂又被突顯了出來。混亂不一定只是負面的,所謂時勢造英雄,我們期待有人能出來一統江湖。

    JavaScript的其它應用

    JavaScript只能夠運用在網頁上嗎?其實蘋果電腦(Apple)除了硬體上的創意外,在作業系統上的表現一直以來也是使用者的最愛。在新的Mac OS X 10.4裡,有個新的功能叫-Dashboard,它可以讓你執行一些小程式,例如月曆、天氣預報、地圖查找等。這些小程式稱為Widget,而撰寫Widget的主要語言就是JavaScript,當然還可以配合HTML、CSS甚至Java Applet。隨著Mac OS X的使用者日益成長,撰寫Widget已成為一個新興的軟體業。Dashboard其實不是第一個實踐這個概念的程式,早在Mac OS X 10.2時,就有一個叫Konfabulator的軟體做出這樣的平臺,在當時大受歡迎。後來Apple在Mac OS X 10.4內建一樣功能的Dashboard時,還飽受外界的批評和指責,認為Apple是抄襲Konfabulator的。隨著Mac OS X直接支援Wedget,Konfabulator漸漸的淡出Mac的舞臺。不過Yahoo看上了它,把它給買了下來,而在不久前重新推出Windows/Mac版的Yahoo!Webget,這下Windows的使用者也能享受Widget所帶來的便利和好處。

    下一步

    世界上有所謂的Java工程師、C/C++工程師、VB工程師等?,好像沒有JavaScript工程師這樣的頭銜,我個人覺得他應該是被需要的。JavaScript其實並不好學,教育訓練是個商機,開發除錯平臺也是一個商機。這年頭流行認證,它也是可以著墨的,例如辦理類似SCJP、MCSE這樣的認證考式(事實上是有JavaScript Certification,不過沒像其它認證這麼有公信和正式就是了)。唯有專門的訓練課程及認證才能真正落實JavaScript的標準化,這樣寫出來的JavaScript程式才不會相容性不足,造成用某種瀏覽器才能觀看的特定網頁的情況。

    而另一種是像無障礙網頁這種的認證,若你的網頁通過JavaScript相容性審合,那麼在網頁上就可以放上一個標章,証明你的網頁是相容於各種瀏覽器的。以上這些如果能由國際大廠來推動,成功的機會才會比較大,好奇的是,這麼多年來,怎麼沒人想幹這件事?!也許是吃力不討好吧。

    註: TIOBE Programming Community Index for December 2005 http://www.tiobe.com/tpci.htm 相關連結:

    ECMA-262
    http://www.ecma-international.org/publications/standards/Ecma-262.htm

    Spidermonkey
    http://www.mozilla.org/js/spidermonkey/

    Rhino
    http://www.mozilla.org/rhino/

    Dashboard
    http://www.apple.com/macosx/features/dashboard/

    Yahoo!Wedgets
    http://widgets.yahoo.com/

    posted @ 2006-03-02 20:09 killvin| 編輯 收藏

    Buffalo介紹

    Web正隨著Amowa/Ajax的引入開發開始變得有趣!

    Buffalo是Amowa思想實現的關鍵組件。Buffalo中定義了Web遠程調用的傳輸基礎,并且將遠程調用對象完整的序列化到了本地,成為可以被JavaScript編程觸及的對象。Buffalo中的重要組件 - BuffaloBinding,提供了將JavaScript對象綁定到HTML元素的能力。這種綁定將是無侵入的,只需要在HTML元素中加入若干個不影響排版的屬性,即可將數據與界面綁定。

    Buffalo1.1之前的版本重要的功能主要集中于遠程調用與Java對象/JavaScript對象之間的雙向序列化,這個特性已經被眾多的應用久經考驗,被認為是健壯的。它大大減少了在Java對象與JavaScript對象之間需要考慮的細節,開發者可以更用心地進行業務設計和界面設計。1.2版本的推出,引入了新的眾多的特性,包括:新的重新改寫的Spring集成;與流行javascript庫prototype的集成;支持瀏覽器前進后退按鈕,等等。Buffalo努力使Web2.0的概念在這個小小的輕量級的框架中得到體現,讓你的應用具備更豐富的表現。

     

    問題

    可是在使用Buffalo傳輸對象的過程中我發現Buffalo并不能夠很好的傳遞用戶自定義的對象,不光如此在與作者的討論過程中我還發現很多的人對于Javascript程序的規范根本不屑一顧,似乎選擇了Javascript本身就是選擇了"自由編程",可是真的如此嗎?

    其實Javascript是一個比較純粹的OO語言,并不想一些報道中所說的那樣,對于封裝、繼承、多態Javascript同樣可以完成,既然它是OO的語言,就理所應當的按照某種規范來編寫代碼,正如JavaScript說分明 中說得那樣,編寫Javascript不是一件簡單的事情,關鍵是很多人亂用了這個語言,從而導致給很多的朋友以為寫Javascript就是很沒有水準的事情(偶以前也是如此),但我最近發現用Javascript這樣的動態語言沒有一個很好的語言規范,你開發出來的應用只能夠停留在小型系統之上,而對于比較大型的應用開發,這樣寫出來得代買根本無法復用!

    再來說說Buffalo傳輸對象的問題,以下是我自定義的Node對象

    function Node(_id , _type)
    {

     //log("create node object!!!");
     //============================== property =================================//
        //=========================================================================//
     var id = _id;
     var type = _type; //<- type的出現在OO世界里本身就是背叛
     var brush = null //節點的物理表現句柄
     
     var rList = new Array();//左節點列表
     var lList = new Array();//右節點列表
     
     var rLine = new Array();//節點的左線列表
     var lLine = new Array();//節點的右線列表

     var x = 0;//節點的x坐標
     var y = 0;//節點的y坐標

     var m = false; //標識節點是否可移動
     var r = false; //標識節點是否能夠建立聯系

     var name = ""; //節點的名稱

     //================================ method =================================//
        //=========================================================================//

     this.getType = function()
     {
      return type;
     }
     this.setType = function(_t)
     {
      type = _t;
     }
     this.setName = function(_n)
     {
      name = _n;
     }
     this.getName = function()
     {
      return name;
     }
     this.setId = function(_i)
     {
      id = _i;
     }
     this.getId = function()
     {
      return id;
     }
    }

    然而在使用Buffalo的時候Node對象根本就無法傳遞,而導致這個問題的原因竟然是我沒有"自由的編寫Javascript"!其實問題就出在doStructXML 方法上,作者在這里有意的"避開"了這個問題,所以在傳遞Node的時候對于其私有的方法Buffalo是根本就不考慮的,FAINT!

    原先

     doStructXML : function(data){
      var boClass = data[Buffalo.BOCLASS];
      var boType = boClass ? boClass : "java.util.HashMap";
      var xml = "<map>\n";
      xml += "<type>" +boType+ "</type>\n";

      for (var i in data){
       if (data[i] != boType) {
        if (typeof(data[i]) == "function") continue; /* the function shouldn't transfered. */
        xml += this.getParamXML(this.dataTypeOf(i),i)+"\n";
        xml += this.getParamXML(this.dataTypeOf(data[i]),data[i]) + "\n";
       }
      }
      xml += "</map>\n";
      return xml;
     },

    修改后

     doStructXML : function(data){
      var boClass = data[Buffalo.BOCLASS];
      var boType = boClass ? boClass : "java.util.HashMap";
      var xml = "<map>\n";
      xml += "<type>" +boType+ "</type>\n";

      for (var i in data){
       if (data[i] != boType) {
        if (typeof(data[i]) == "function")
        {
         /*判斷方法名稱*/
         if(i.substring(0,3) == 'get')
         {
          var _name = i.substring(3,i.length).toLowerCase();
          var _value = data[i]();
          //alert("functin name[" + this.dataTypeOf(_name) + "]=" + _name
          // + " | value[" + this.dataTypeOf(_value) + "]=" + _value);

          xml += this.getParamXML(this.dataTypeOf(_name),_name)+"\n";
          xml += this.getParamXML(this.dataTypeOf(_value),_value) + "\n";
         }
         continue; /* the function shouldn't transfered. */

        }
        
        xml += this.getParamXML(this.dataTypeOf(i),i)+"\n";
        xml += this.getParamXML(this.dataTypeOf(data[i]),data[i]) + "\n";
       }
      }
      xml += "</map>\n";
      return xml;
     }

     

    總結

    1。Javascript的編寫是否應該遵守一定的規范,其實屬于程序員自身休養的問題,但我還是覺得復用JAVA的編碼規范不是壞事情!

    2。時刻記住Buffalo是建立在burlap協議的基礎上的,我們應該把注意力放在burlap上而不僅僅只是Buffalo!

    posted @ 2006-03-02 19:46 killvin| 編輯 收藏

    不知道已經被欺騙了多少次了,自己依然還是很留戀CSDN的Blog空間,是不是自己太虛榮了?無論如何都不想再次的被忽悠!

    posted @ 2006-03-02 19:24 killvin| 編輯 收藏

    主站蜘蛛池模板: 国产国拍亚洲精品mv在线观看| 亚洲一区精品视频在线| 亚洲免费闲人蜜桃| 免费人妻精品一区二区三区| 亚洲午夜免费视频| 成年女人永久免费观看片| 亚洲av伊人久久综合密臀性色| 亚洲日本中文字幕天天更新| 中文无码成人免费视频在线观看| 好男人视频在线观看免费看片| 久久九九亚洲精品| 亚洲成AV人片在WWW| 久久成人a毛片免费观看网站| 日韩免费视频在线观看| 亚洲一区二区三区四区在线观看| 亚洲AV无码专区在线厂| 1000部无遮挡拍拍拍免费视频观看| 亚洲高清国产拍精品青青草原| 亚洲欧洲国产经精品香蕉网| 亚洲免费无码在线| 免费激情视频网站| 亚洲精品在线免费观看| 在线免费视频你懂的| 国产精品四虎在线观看免费| 亚洲沟沟美女亚洲沟沟| 好紧我太爽了视频免费国产| 免费真实播放国产乱子伦| 亚洲av日韩av无码av| 午夜不卡久久精品无码免费| 亚洲欧洲日产国码高潮αv| 亚洲乱妇熟女爽到高潮的片| 911精品国产亚洲日本美国韩国| caoporn国产精品免费| 国产伦精品一区二区三区免费迷| 亚洲国产精品综合福利专区| 中文无码成人免费视频在线观看| 亚洲一级片内射网站在线观看| 亚洲国产日韩a在线播放| **aaaaa毛片免费同男同女| 国产亚洲精AA在线观看SEE| 国产成人综合亚洲绿色|