PowerBuilder被忽略的技術

1.1 把Object看做類 
Completed: 100 % 
類(Class),僅僅在概念的層次上,是不能直接使用的,類只有在具體化(實例化)後才能使用,實例化的類咱們稱之爲對象(Object); 
在PB的幫助中經常出現Object這個詞,例如介紹菜單時使用的名稱是Menu Object 而不是 Menu Class。(我認爲嚴格講,在幫助中這樣叫有不妥之處;Sybase可能會有它本身的說法,這些咱們暫且無論),爲了能合理解釋Powerbuilder的面向對象的繼承、多態、封裝等特性,如今咱們做以下假設: 
咱們暫且把Powerbuilder中的Object統一稱做Class; 
本次約定在接下來的幾回討論中都有效,若是我能記得我會每次都強調一下; 
1.2 窗口的控件建立和釋放 
Completed: 100 % 
控件是咱們在開發中最經常使用的了,可是放在窗口上的控件什麼時候被建立,什麼時候有被銷燬? 
建立過程: 
在windows中,控件被建立時必須指明用來承載控件的窗口對象,因此能夠確定的是窗口的建立過程確定在全部準備使用它來承載的控件以前完成建立; 
細心的話你會注意到控件的Constructor事件會在窗口的Open以前執行,事情就是這樣子的,那是由於窗口Open事件並非窗口的Constructor事件,窗口在完成本身的建立後,再把Control[]中的對象一一建立,在這時控件的Constructor事件會被觸發,在全部的控件成功建立完畢後窗口才會觸發Open事件。 
用流程圖表示: 

釋放過程: 
釋放過程正好跟建立順序相反; 
在窗口收到WM_CLOSE消息前會先收到WM_CLOSEQUERY消息,來給開發人員一個阻止WM_CLOSE發生的時機;在Powerbuilder中WM_CLOSE <=> Event Close() ,WM_CLOSEQUERY <=> Event CloseQuery()。 
因此在窗口關閉時首先觸發本身的CloseQuery事件,在CloseQuery贊成後觸發Close事件,以後纔會把Control[]中的控件一一釋放,這時各個控件的destructor事件會被觸發。等全部的控件被釋放後,再觸發窗口的Destructor。完成釋放過程。 
用流程圖表示: 


注意:控件建立的前後順序要看控件怎樣存儲在Control[]中,Powerbuilder不管是建立過程仍是釋放過程,都是從下表1開始循環建立的,能夠查看源碼知道各個控件的下標。通常規律是後放入的控件縣建立。 

1.3 謹慎使用Post 
Completed: 100 % 
談到Post,就會想起來是用來向控件消息隊列發送消息,經過Post發送的消息處理時間是不能肯定的,這由操做系統是如何調度的,以及當時計算機的運行狀況來決定。在PowerScript中Post的語法很靈活,表現形式多種多樣。 
Post以關鍵字的形式出現: 
發送消息給以Powerbuilder事件形式出現的消息處理函數:this.post event open() 
發送消息給以Powerbuilder函數形式出現的消息處理函數:this.Post classname( )(這種方法有別於 this.classname()) 
以關鍵字形式出現通常用在有參數的消息處理函數中; 
而且使用Post是不能獲得函數或事件的返回值的; 

Post以函數形式出現 
this.postevent( "XXXX ",{Word},{Long}) 
XXXXX 是函數名或事件名 

Windows是消息驅動的操做系統,因此Windows下的Powerbuilder程序也不例外,Powerbuilder中使用Post()和Send()來完成消息的發送。熟悉SDK編成的能夠直接使用Post和Send來發送消息,Send發出的消息會當即被處理,因此這個咱們不討論,可是Post是咱們的導論範圍. 
一般狀況下Post發送的消息在被對應的消息處理函數處理的時刻要比直接調用消息處理函數晚一些。這就會存在潛在危險,若是發出消息對應的消息處理函數所在的對象已經被釋放,這時將可能會出現運行錯誤或者消息不被處理的危險;因此在使用了Post以後應該注意目標對象是否存在對象提早被釋放的狀況。特別須要注意的是在窗口的Close中使用Post,由於發出Post時沒有辦法知道消息什麼時候會被處理。因此在Close中儘可能不要使用Post。 
這裏順便提到一點: 
在控件的代碼中,尤爲是按鈕的Clicked事件中,寫了以下代碼將會出現運行錯誤: 
CLose(Parent) 
this.text = " " 
若是出現下面代碼則不會出現運行錯誤: 
CLose(Parent) 
MessageBox( " ", " ") 
若是出現下面代碼則不會出現運行錯誤: 
Post CLose(Parent) 
MessageBox( " ", " ") 
這些就是Post應用的一些臨屆狀況。 

1.4 避免類重名 
Completed: 100 % 
這裏主要討論兩種狀況的重名: 
一、不一樣的PBL重名類 
Powerbuilder容許在同一個Target中出現多個Powerbuiler Library (PBL文件),在不一樣的PBL中間出現相同的類別名稱,就算是出現同一種類型的類,Powerbuiler不會做出判斷,這樣就存在了一個潛在的危險,PBL(或PBD)在Target中出現的前後順序將會影響在運行時引用的對象;而且產生編譯警告。因此這種必定要避免。 
二、同一個PBL中出現重名 
若是是相同類型的重名出如今同一個PBL中,Powerbuiler會做出提示,詢問是否要覆蓋。但若是出現同名的類不是同一種類型,Powerbuilder不會做出提示,這樣就會帶來許多沒必要要的麻煩。例如:在同一個PBL中出現了ww窗口,同時也出現了ww菜單,在ww窗口引用ww菜單時編譯器會提示ww不是一個菜單類型。 
綜上所述第一種重名狀況比較致命,不易被發現。第二種狀況也應該堅定避免。好在Sybase在引導開發時,都提倡加前綴,這樣就能夠避免第二種問題。因此要強調的就是手誤。 

1.5 隱藏的全局類和局部類 
Completed: 100 % 
隱藏的全局類 
打開Powerbuilder相關資料,很容易看到「Powerbuilder一個面向對象的開發工具」之類的話。絕大多數人寫過的第一個程序就是在從window創建一個w_main,而後在Application中調用Open(w_main),這樣一個程序就產生了 
若是有面向對象編成的思想,那麼這個時候應該有個疑問,我創建的w_main是個什麼東西啊,類?對象? 
衆多書籍上會說創建一個window對象,那麼這時Open(w_main)沒有任何疑問,由於w_main是一個全局對象阿,做爲參數傳給Open固然沒有任何疑問。能夠你還可能會看到過一下代碼: 
w_main w_a,w_b 
open(w_a) 
open(w_b) 
上面的代碼不但不會產生編譯錯誤,而且可以執行成功。執行後你會看到兩個窗口。 
你可能會覺得對象也能做類別使用,難道Powerbuilder超越了面向對象? 
這時應該懷疑的是w_main也是個類,而且有一個與w_main同名的全局w_main對象。只有這樣能解釋上面提到的兩種代碼。帶着疑問打開w_main的源碼: 
global type ww from window 
global ww ww 
看到這裏,應該能夠證明咱們以前的猜想是正確的,Powerbuilder確實建立了window的子類w_main,而且同時又聲明與類(Class)同名的全局變量(全局對象 Object)w_main。除了structure(不是類)以外都全部出如今PBL中的類都具備這樣的特性。 
隱藏的局部類 
更深刻的編程尤爲是寫通用代碼時,會須要使用ClassName()來判斷對象的類別,而後根據類別作出判斷。接下來咱們仍然以一個具體的例子來展開討論。 
如今有一個窗口w_main,在窗口上方防置了一個按鈕cb_1,一般咱們寫代碼都是象下面同樣的寫法: 
cb_1.Text = "XXXX " 
cb_1.Enabled = False 
String ls_clsNm 
ls_clsNm = cb_1.ClassName() 
這時就會發現ls_clsNm是「cb_1」,怎麼不是CommandButton?難道ClassName()不是取的類別名?查一下幫助,能夠證明ClassName()的功能,就是取類名。因此咱們又要有猜想,是否是又有局部的類被 cb_1,因爲cb_1能夠直接用,因此也應該有一個與cb_1同名的局部對象存在。爲了證明咱們的想法,讓咱們打開源碼分析: 
cb_1 cb_1 
type cb_1 from commandbutton within w_main 
須要注意:若是控件是動態建立的,則ClassName()獲得的將是CommandButton。 
撥雲見日,知道了這些隱藏的類和對象,至少能夠確定Poswebuilder面向對象的特性,咱們在之後的編碼中巧妙的使用這些類或對象。web

1.6 全局Message對象 
Completed: 100 % 
首先能夠確定的是 Message是一個Message類型的全局變量(講到這裏咱們也許能夠理解PowerBuilder幫助中出現的Object是能夠理解的,由於不少類都是以全局對象出現的)。這裏咱們不討論Message類,而是在討論Message對象。Message對象在程序啓動時自動被建立。首先咱們看一下Message類的成員變量: 
---------------------------------- 
Handle Long //消息目標對象句柄 
Number UnsignedInt//消息 
WordParm Long //參數 
LongParm Long //參數 
ClassDefinition owerObject //暫不討論 
DoubleParm Double //用來傳遞數字 
StringParm String //用來傳第字符 ,字符在傳遞過程當中是複製的 
PowerObjectParm owerObject //用來傳暫且遞人意的Powerbuilder對象 
Processed BooleanA boolean value set in the script for the user-defined event or the Other event. Values are:TRUE - The script processed the event; do not call the default window process (DefWindowProc) after the event has been processed.FALSE - (Default) Call DefWindowProc after the event has been processed. 
ReturnValueLong牋When Message.Processed is true, specifies the value you want returned to Windows. This property is ignored when Message.Processed is false. 
---------------------------------- 
從上面的成員變量能夠看出,藍色標出的最上面的四個成員變量和Send函數中的參數是一致的,幫助上也是這麼聲明的.若是Message向咱們原來設想的那樣,是用來在不一樣對象之間傳遞,那麼這些成員就顯得有些多餘(相信有不少人在傳遞參數過程當中會誤用Message.Number,Message.WordParm,Message.LongParm,用這些來接收數字,結果都失敗了),因此他必定有其餘用途,那麼它是如何和windows消息聯繫到一塊兒的呢?這個暫且放一下,咱們繼續看下面的成員變量,綠色標出的是咱們最爲熟悉的參數了,我就不作詳細解釋,最後面的兩個我對他的功能有些質疑,待會兒我會驗證個人說法。 
Message對象的第一種功能: 
通過Debug你會發現,Powerbuilder程序受到任何一個消息之後Message內容都有變化,仔細觀察就會發現變化的只是前四個參數,這樣咱們就能夠肯定這個全局對象就像一個梭子同樣跟着每一個消息在程序中傳來傳去,Powerbuilder在接收到消息後會先初始化Message對象,而後再把消息繼續傳遞。這種特性對於自定義消息也一樣有效。 
我上面提到,最後兩個成員變量的功能我有質疑,緣由是我在實際測試中發現,當我把Processed設置爲False時,ReturnValue仍然會起做用。還有,事件中的返回值回覆蓋ReturnValue中的值,即使是使Processed爲True。這個有興趣能夠作一個測試看看。 
注意接收Message的返回值須要使用函數Send,而不能使用Post。 
在這種狀況下,DoubleParm、StringParm、PowerObjectParm是不會被修改的,這個是確定的,要否則咱們就會因消息過多而不能使用燤essage傳遞參數了。 
Message對象的第二種功能: 
這種功能也是Powerbuilder開發人員經常使用的功能。 
例如OpenWithParm(w_a, "ACB ") 
在w_a的窗口中可使用Message.StringParm來獲得ABC, 
但是咱們在實際的工做中,尤爲在通過屢次封裝後的窗口中會發現獲得的Message不是咱們須要的,多是一個空的(不是非法),也許有了其餘的內容,到底什麼時候把全局Message對象重置又什麼時候把Message對象填充? 
問題已經引出,這就是咱們要討論的內容: 
咱們能夠確定: 
OpenWithParm 
OpenSheetWithParm 
OpenUserObjectWithParm 
可是會不會清空咱們能夠做一些測試來驗證: 
Open 
OpenSheet 
OpenUserObject 
通過驗證發現他們均可以把Message對象清空的; 
目前我只發現這些。 
因此能夠有結論,當調用以上六個函數中任意一個時,就會把Message重置。 
理清楚這些,就很容易把Message被偷天換日的狀況完全杜絕。 
須要注意:第二種功能中只會影響到DoubleParm、StringParm、PowerObjectParm三個參數。 
綜合來說Message分爲了兩個部分,一部分用在系統傳遞消息時,一部分用在開發程序中傳遞參數。 

1.7 關於菜單編程 
Completed: 75 % 
通用菜單 
咱們不須要解釋什麼叫菜單,因此就直接不如正題,在實際的開發過程當中菜單是必不可缺的,有時爲了方便會把全部彈出式菜單都坐在一個類中,須要時用那個彈那個。有時也會做一些通用菜單做爲公用組件使用。 
下面列出咱們常見的菜單編程方法來引出這個問題: 
&#8226; 調用指定窗口對象的函數: 
w_main.relogon() 
&#8226; 直接調用調用全局函數: 
gf_relogon() 
&#8226; 使用Parentwindow動態調用: 
Parentwindow.Dynamic relogon() 
這幾種作法的靈活性從上而下,但是這些作法都不足以保證靈活性,而且業務處理代碼相對分散,而且若是髮菜單放到沒有定義相對應的函數的窗口中會出現運行錯誤。這種也是通用菜單不該該出現的錯誤; 
受系統菜單的啓發 

爲了解決代碼分散,容易出現運行錯誤等問題,咱們採用Windows的消息路有機制來完成菜單的功能。 
具體作法: 
一、能夠定義好一些全局常量,如: 
CONSTANT UINT WM_USER = 1024 
CONSTANT UINT WM_M1 = WM_USER + 1 
CONSTANT UINT WM_M2 = WM_USER + 2 
CONSTANT UINT WM_M3 = WM_USER + 3 
爲了跟windows消息區別,必定要在WM_USER上增長。 
在須要處裏該消息的窗口上寫好處理代碼就好了: 
Choose Case Message.number 
Case WM_M1 
MEssageBox( " ", 'wm_m1 ') 
Return 1 
Case WM_M2 
MEssageBox( " ", 'wm_m2 ') 
Return 1 
Case WM_M3 
MessageBox( " ", 'wm_m3 ') 
End Choose 
這樣只要會發送這樣消息的過來,就會自動處理。最大限度的讓菜單代碼靈活。 
彈出式菜單 
在PB幫助中這樣寫道 
The coordinates you specify for PopMenu are relative to the active window. In an MDI 
application, the coordinates are relative to the frame window, which is the active window. 
To display a menu at the cursor position, call PointerX and PointerY for the active window 
(the frame window in an MDI application) to get the coordinates of the cursor. (See the 
examples.) 
右鍵菜單彈出時系統管理的,因此須要屏幕座標才能準確; 
因此怎樣可以快速準確的找到父窗口對象就成了解決問題的關鍵。 

未解決問題 
菜單動態禁用的代碼可能會比較分散,不便於管理;若是Powerbuilder可以支持 ON_UPDATE_COMMAND_UI這樣的功能就好處理了。 

1.8 SQLCA對象 
Completed: 100 % 
SQLCA是Powerbuilde應用程序中 ,Transobject類別的一個全局對象,負責與數據庫通信,通常的用法我相信各位都在熟悉不過,在這裏主要討論一下存儲過程的用法; 
本身定義用戶對象Transobject 的子類My_Trans;而後再定義外部函數的地方打開右鍵菜單。使用以下菜單項能夠在這裏定義存儲過程和函數;把須要的存儲過程都在這裏作聲名; 

而後把全局對象SQLCA的類改稱My_Trans,這樣就能夠像使用SQLCA的通常方法同樣來使用存儲過程; 

1.9 雙向第歸,事半功倍 
Completed: 100 % 
想法是在該面試人員的PB答卷中被激發的,答卷最後一題是這樣的: 
運用第歸算法寫一個函數實現1到100的累加。 
在這份答卷中是這樣寫的 
Long uf_add(long al_Start, long al_End) 

if al_Start > al_End then Return 0 
if al_Start = al_End then Return al_End //或者 al_End 
Return al_Start + uf_add(al_Start + 1, al_End) 

初一看覺得是的錯誤的算法,立即我給了0分。 
給分後心理已知不踏實,便仔細看了一下,才發現這是個正確的算法,算法很簡練,只是在寫法上有別於我下面給出的算法: 
Long add1 (Long aparm) 

if aparm = 1 Then Return 1 
Return aparm + add1(aParm - 1) 

單從形式上看這兩種寫法,後者可能更簡單些; 
隨後我把這兩種算法都上級作了測試,結果都是5050,第歸次數都是100次。 
在我準備刪除這些測試函數時,試卷上的函數忽然給了我啓發:al_Start 能加 , 那麼al_End爲和不能減,兩端都向中間靠攏,這樣第歸次數就能夠減小一倍,再看看試卷上函數的第歸結束條件,不正好知足這樣的要求嗎?因而便有了下面的經典(我自封的)第歸算法: 
Long add2 (Long a1, Long a2); 

if a1 > a2 then return 0 
if a1 = a2 then return a1 
return a1 + add2(a1+ 1, a2 - 1) + a2 

經測試結果爲5050,第歸次數爲51; 
雖然這種算法在咱們的編碼中可能不多用到,可是這種「雙向第歸」的方法卻可讓效率按倍數提高; 

1.10 Tracing 
Completed: 100 % 
使用如圖所示的Profiling,而後在程序運行的時候能夠把每一個函數的運行時間和次序打印到文件中。面試

相關文章
相關標籤/搜索