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 %
通用菜单
咱们不须要解释什么叫菜单,因此就直接不如正题,在实际的开发过程当中菜单是必不可缺的,有时为了方便会把全部弹出式菜单都坐在一个类中,须要时用那个弹那个。有时也会做一些通用菜单做为公用组件使用。
下面列出咱们常见的菜单编程方法来引出这个问题:
• 调用指定窗口对象的函数:
w_main.relogon()
• 直接调用调用全局函数:
gf_relogon()
• 使用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,而后在程序运行的时候能够把每一个函数的运行时间和次序打印到文件中。面试