分享

delphi随笔

 frie 2005-08-09

一、老生长谈
    Delphi VS VC已经是很古老的话题了,但是我还是想在这里谈一下,全是一家之言,如果不同意,请一笑之。
    RAD的好处是很易见的,界面的设计上Delphi实在是高过VC。我要写一个非常规的透明按钮,Delphi只要找个控件,点一下鼠标,修改一下Caption就OK了,VC呢?至少要写上10几行代码才能搞定,当然MFC的做法让人了解windows底层的原理,但是OOP的封装性没有很好的体现,开发者要了解所有的底层才能写代码,对我对大多数的入门者是个折磨,因为没必要,现在是开发期限永远紧张,时间永远不够,我们不能指望程序员知道Windows编程的所有的方面,有人对视频很了解,有人对网络很在行,但是没有人是全才,样样在行是不现实的,所以封装是很重要的。如果每次开发新产品我都要在一个透明按钮或者一个漂亮的界面上花30%甚至更多的时间,那我就去跳河:)Delphi在界面上的确比MFC好!当然不是说MFC就设计不出漂亮的界面,只是花费的时间太长,不值得。
    RAD就真的全是好处?也不是。至少对于初学者不是,因为它让人误会编程只是动动鼠标,拉拉框架,最后的结果就是让人觉得,Delphi就是用来写界面的,底层什么都不能做。好像MFC程序员都是这么讲Delphi程序员的:“你们的程序除了界面漂亮,还能做什么?”错!其实除了DDK,Delphi什么不能开发?API的头文件Delphi哪个没有?Borland没有转换的,JEDI都转换了,即使JEDI没有转,自己动手也是一样的,只要给我C的头文件,我就可以转换,JEDI上那篇短短的转换说明应该是每个Delphi程序员的必备文档。头文件转换了,剩下的就是开写了,MFC能做的,Delphi也可以!视频?网络?directx?Audio?哪个Delphi不能做?

二、子过程
    写一个事件,很多人就是直接开写,不管代码有多长,做的事情有多少,只要是在一个事件里做的,一古脑写下去,结果是几个月后重新修改的时候无从下手,因为代码段实在太长了。那么为什么不把代码段拆开呢?人的注意力有有限的,超过100行的代码一口气看下来会晕的,Delphi的前辈告诉我一件事情:所有的过程(这里的过程包括procedure和function)不要超过25行!因为这个长度的代码看起来不会让你头晕,你会很容易了解这个过程要做的事情。
    那么怎么把原本在一个事件做的事情拆开呢?方法很多,我的经验是模块化。比如一个事件里要做很多不同的事情,那么就把不同的事情化为不同的子过程,然后在主过程里调用,主过程里大多数就是一些判断和循环,不会出现具体的实现过程,这样会生出很多的代码段,但是会让你的注意力集中!
    原则:一个过程只做一件事情,并且做好它。
    参考:VCL的源代码。看看VCL的源代码,很少有超过25行的代码段!

三、参数名
    记得当初学SDK的时候,我一看到匈牙利表示法就头晕,太多了!记不住啊!所以我恨那个发明者:)终于Delphi出现了,戴着镣铐跳舞的日子过去了!在Delphi里,定义一个字符串用strDoSometing这样的变量名是可笑的和不必要的。只要你的过程很短,不出现全局变量,就不需要这样的前缀。比如:
procedure SubPro;
var
  i : byte;
  Width, Height : integer;
begin
  Width := GetWidth;
  Height := GetHeight;
  for i:=0 to 9 do
  begin
    DrawThread := TDrawThread.Create;
    DrawThread.Width := Width;
    DrawThread.Height := Height;
    DrawThread.Start;
  end;
end;
    我想这样的代码段虽然没有注释,也很容易知道他要做的事情。所以,请去掉所有的前缀和下划线,Delphi的程序不需要这些!我们的参数名只要动词+名词就可以,只要说明这个参数的作用就可以,多余的东西我们不要,简单就是美,Pascal的好处就在于代码像在说话,而不是读天书,你的脑袋里是怎么想的,代码就是什么样子的。优美、简单,这是Pascal的好处,请遵守!
    原则:简单就是美!

四、子窗口
    很多人在调用子窗口的时候是直接对子窗口里的控件操作的,比如:
  if SetAlarmParamDlg.ShowModal = MrOK then
  begin
    AlarmTimes := StrToInt(SetAlarmParamDlg.Edit1.Text);
    AlarmArea  := SetAlarmParamDlg.SpinEdit1.Value;
  end;
    天,假如明天用户觉得你用的Edit或者SpinEdit的样子难看,换了一个漂亮的控件,你怎么办?不但要修改子窗口的代码,还要修改主窗体的代码。一两个子窗口的程序当然不会让你痛苦,假如是一个有二十多个子窗体的程序呢?花一天的时间,原因却只是因为换了一个控件!为什么不换一个方法?把要用的参数用属性表示,你会少写无数的代码。
// 主窗体
  if SetAlarmParamDlg.ShowModal = MrOK then
  begin
    AlarmTimes := SetAlarmParamDlg.AlarmTimes;
    AlarmArea  := SetAlarmParamDlg.AlarmArea;
  end;

// 子窗体
interface
private
  FAlarmTimes : integer;
  FAlarmArea : integer;
published
  property AlarmTimes : integer read FAlarmTimes write FAlarmTimes;
  property AlarmArea : integer read FAlarmArea write FAlarmArea;

implementation
...
  FAlarmTimes := StrToInt(Edit1.Text);
  FAlarmArea := SpinEdit1.Value;
 
  ModalResult := MrOK;
...

    只要这样坚持下来,你会发现好处大大的,一个子窗口只做他自己的事情,主窗口和他的交互是通过属性来做的,只要接口不变,子窗口的修改不会影响到主窗口的代码,不管子窗口的样子怎么变换,控件怎么更换,代码怎么修改,整个程序都还是老样子,只是界面变了而已。
    原则:模块化你的子窗口,窗口也是类,类之间怎么通信,窗口之间就应该怎么通信


  

对该文的评论
xrenwu ( 2002-04-21)
不错,非常好!
oldsword ( 2002-04-01)
duxinrun:
  我写的这些的确是很小儿科的东西,你既然看不上,就欢迎你写点好东西出来让大家学习学习,谢谢.
duxinrun ( 2002-03-31)
这些原则也并非仅仅适用于Delphi,所有的OOP不都应该是这样么?
这也不是Delphi自己的特色阿。
aifudi ( 2002-03-31)
学好delphi是我得心愿 我相信微软并没有让所有的人都去学vb vc的实力~!~
igand ( 2002-03-30)
我一向就认为DELPHI 比VC好,再接触程序以前,一直听别人讲VC好,后来就去学了,越学越学不懂,后来改行学BCB现在再学DELPHI,很好啊

一、关于界面
  界面对于一个程序,仿佛就是容貌对于一个人,重要性是不言而喻的。
  一个程序的界面做的很漂亮是很好,但是如果界面不能很好的反映功能,那再漂亮的界面都是垃圾一堆,这方面我是有体会的,当你的大部分精力全放在如何

做一个漂亮的界面的时候,灾难就降临了,你将永远无法把程序的功能做好!如果想把程序做好,就不要考虑漂亮的界面,等到功能全部实现的时候,界面自然

就会出现,到时候美化也不迟。
  这个问题上,Delphi的程序员可能遇到的最多,因为满天飞的都是漂亮的控件,这个也好,那个也好,不用真是可惜,于是找个一堆控件,一个个的试验过来

,功能全然不顾。其实用户用一个软件,不是因为界面漂亮,而是因为功能强。即使是在DOS窗口下跑的程序,只要功能强,用户照样会津津有味的使用,如果功

能弱的一塌糊涂,即使界面做成无比酷,最后的命运也是被删除。所以,写一个软件的时候,请首先从功能入手,而不是界面!
  那么是不是不用考虑漂亮的界面呢?也不是。终究漂亮的界面是很吸引眼球的,但是要是按照我的意思,先弄个普通的界面,最后美化,会造成一个问题,就

是要修改很多代码,比如:

procedure TMainForm.Button1Click(Sender : TObject)
begin
  FWidth := StrToInt(Edit1.Text);
  FHeight := StrToInt(Edit2.Text);
  FScreenSize := FWidth * FHeight;
end;

  上面的代码运行的时候是没有什么问题的,但是一旦你换了一个Edit控件,或者是只是换了Edit的名字,工作量就出来了,一个两个的变化还没有问题,假如

这个Edit1.Text在代码里出现了100多个地方呢?怎么办?我的方法是把程序的界面和功能分开!代码尽量不要和具体的控件相关联。如下:
procedure TMainForm.SetParam(const Width, Height : string);
begin
  FWidth := StrToInt(Width);
  FHeight := StrToInt(Height);
  FScreen := FWidth * FHeight;
end;
procedure TMainForm.Button1Click(Sender : TObject)
begin
  SetParam(Edit1.Text, Edit2.Text);
end;
  这个例子有点极端,好像是画蛇添足,但是当你大量的修改控件或者更换控件名字的时候,作用就显示出来了。到你最后想美化界面的时候,修改代码的工作

量会减轻很多。因为只要传入的参数更换一次就可以,而不是在无数的地方进行修改。
  原则:界面和实现分开

二、全局变量
  不要使用全局变量!!!即使设定的全局变量你认为一万年也不会变,也不要使用,因为说不定那天修改了一个功能,这个全局变量就要拆成两个变量了,那

么问题就会出现,还是通过参数传递吧。还是来个例子:
//main
const
  ShowWidth = 384;
  ShowHeight = 288;
...

// SetParamDlg 子窗口
  SpinEdit1.Value := MainForm.ShowWidth;
  SpinEdit2.Value := MainForm.ShowHeight;

  上面的代码工作的不错,但是假如有一天客户说我要分辨率可变,怎么办?在const里重新加入ShowWidth1、ShowWidth2、、、?最后const的体积会越来越大

,查const的时候会累死你,那换一种方法:
//main
function ReadWidth : integer;
function ReadHeigth : integer;

SetParamDlg.Width := ReadWidth;
SetParamDlg.Height := ReadHeight;

//SetParamDlg
  SpinEdit1.Value := FWidth;
  SpinEdit2.Value := FHeight;

  是不是比较好一点?
  原则:尽量在一开始设计的时候把所有看起来目前不会变化的参数也做成动态的。

三、一个小问题
  这是我在一个网站上看到的,比较有意思,而且感觉很容易犯,这里就抄袭一下了,不过这只是编程技巧,不是方法
function sum(a : array of word; count : word) : longword;
var
 i : word;
begin
 result := 0;
 for i := 0 to count - 1 do inc(result, a[ i ]);
end;

  上面的代码有问题么?乍看是没有问题,但是说不定什么时候他就当掉了。为什么?因为Count是个WORD,假如传入参数的时候Count=0会发生什么事情呢?对

了,WORD翻转了,那就等他循环FFFF次吧:)所以请注意无符号类型的操作!


累了,自己都感觉没写好,休息ing,欢迎砖头,欢迎高手来发表意见....


对该文的评论
xrenwu ( 2002-04-21)
写的好!
myun ( 2002-04-03)
very good,希望这位大侠多多发表相关文章啊!
hobo2000 ( 2002-04-03)
非常赞同agui!!!
我平时编程也那样子。
ngkai ( 2002-04-03)
第三个问题的错误不是出在count的类型为word,而是i的类型为word引起的。
count -1的值仍是正确的-1不过由于i是无符号的,所以对于i来说就变成了FFFF
这里应该用integer做为i的类型,一般的情况下都不会用word类型的。
(所有的书上或borland的demo中都不会找到用unsigned类型做计数器的)
用无符号类型进行运算不需要注意任何问题,关键是对无符号类型进行赋值时要注意。
agui ( 2002-04-02)
说两句:

1、界面
最好是熟悉很多标准界面,熟悉诸多控件,这样在设计界面时才能得心应手。最好是用比较标准化的界面,当然这跟需求很有关系,需求应尽可能详尽。如果,碰上一个对奇异界面有偏好的客户(而且说不服他),就只怪自己倒楣了。不要更换控件,除非它的功能不够使了(这肯定也是不熟悉控件所带来的)。尽量使用熟悉的控件,不熟悉的控件会给你带来很多麻烦。即使象老兄这样精心设计了代码,修改起来也是很费劲的。不过,非要换控件时,我通常不是换控件的名字,而只是更换控件的类别,方法有:剪切到文本编辑器中,修改控件类的名字,然后删除不应有的属性,再粘贴回来。而且我一般不使用Edit1, Edit2这类名字,一般是edtName, edtPassword, edtAddress等等有意义的名字。

2、全局变量
赞同尽量减少使用,比较能用得糟的情况,是使用Form变量。除了主Form和DataModule,我一般不会让窗体自动创建,而且几乎不会使用那些自动产生的Form变量。我反对在Form中来回调用,如兄台所举的例子SpinEdit1.Value := MainForm.ShowWidth,这造成了SetParamDlg的不通用。试想,如果想把SetParamDlg用到另外一个地方,另一个工程,该工程的主窗口变量不叫MainForm,结果该语句就通不过编译。这样做的结果是:要么SetParamDlg不能通用,要么主窗体就得改名字。而且SetParamDlg必须要Uses MainForm所在的单元,形成了一个循环Uses。

再设想一下,如果想让不同的Form或甚至非Form的地方调用SetParamDlg,如Form1也调用,Form2也调用,而把SpinEdit1.Value := MainForm.ShowWidth改成SpinEdit1.Value := Form1.Param1,那么如果在Form2调用时,Form1并未创建或已经销毁,这一句肯定就会出错了。

我的一贯做法是:Form都是单向调用的,这样被调用的Form可以复用到其它场合。参数通过被调用Form的某方法或可写属性传入,而结果参数的传回可以通过方法的引用参数、返回值(想想Form的ShowModal的返回值)、或属性。还有一个方法是事件,可以在事件处理方法的参数中传回,这种方法适用于被调用的Form是模式的(用ShowModal显示的,控制不能马上返回)且需要多次返回结果或反馈被调用Form的变化(你可以弹出一个窗体设置工具栏按钮有没有文字标签,而且能够马上在调用窗体上反馈给用户)。

但有时候会无可奈何地碰上,这时候我会把所有的全局变量做成一个类中的数据成员,通过属性或方法接口取得。而因为类的实例只有一个,可以在程序开始时创建及返回时销毁。

3、数组参数
在Delphi中,不需要传递数组的数目,可以这么写:
function sum(a : array of word): longword; 
var 
  i: Integer; // 不要用word做循环变量的类型
begin 
  result := 0; 
  for i := Low(a) to High(a) do // Low和High函数可以取得数组下标的边界值 
    inc(result, a[ i ]); // 对不起,我喜欢在这里折行
end; 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多