悟透DELPHI》一书第16章的部分内容

来源:互联网 发布:java短信发送接口开发 编辑:程序博客网 时间:2024/06/10 03:46
第十六章 操作界面与操作逻辑
我们在前面的第八章曾经讨论过,用户界面与商业逻辑分离的好处。这样的分离可以让
软件体系结构更加合理,结构易于理解,从而增强软件的灵活性和可维护性。
正如我谈到过,我们讨论的目的是为了寻找将软件结构从混沌归于有序的实用方法,这
是编写本书的主要目的之一。有序的东西易于理解,易于理解就便于掌握,掌握之后你
将会发现其背后的哲理是那样的简单,从而升华到更高的境界去感受良好软件结构的协
调美。
本章的话题将重点讨论如何将客户端的程序有序化,希望我们的讨论对所有的软件同行
和朋友都有所帮助。
第一节 分离操作界面与操作逻辑
怎样设计友好的软件操作界面,是人机工程学研究的一个重要课题。好用的软件都有清
秀可爱的面容,不但布局匀称合理,对你的操作意图也是善解人意,并且处理大方得体
,就连对你的回答也体贴入微。如果,你编写的软件象这样将关键字重复的错误告诉你
的用户:“亲爱的,你以前已经输入了编号为991028的出库单,不可重复输入。是否想
查看以前的出库单呢?”,你的软件一定会销量大增。可能当年IBM的OS2就是因为说了
很多用户看不懂的废话,才被用户遗弃,失去民心,而被Bill Gates一统天下。
我们今天不打算讨论怎样打扮软件的容颜,而要谈谈在漂亮的界面后面,应该如何更好
地组织软件结构,大大方方地处理用户的操作命令。
假如,你已经规划好软件初步的操作模式,用带图标的菜单作为用户发号司令的工具,
并在MenuItem的OnClick事件中编写完成业务处理的过程语句。一切都不错,而商业的逻
辑处理也是放到服务器端的RemoteDataMoudle中处理的,经典的三层体系结构。可是你
的老板不喜欢菜单,他对按钮情有独钟,于是你需要将Menu换成Button,然后将操作实
现代码移到Button的OnClick事件过程中,或者将Button的OnClick事件指向原来的Menu
ItemClick事件处理过程。软件提交用户试用之后,用户强烈要求将软件修改为Microso
ft Word 2000那样的界面,既有菜单又有按钮,而且还能提供键盘的快捷键输入(因为
他们的老总习惯了原来DOS下那套老程序的键盘输入方式)。于是,你又不得不修改软件
的界面控件,并再次将操作处理代码调整,以便每种命令输入方式都能被响应。最后,
历经千辛万苦终于能完成整个软件界面的修改工作。
由此可见,不同的人有不同的爱好和需求,他们各自都有操作软件的习惯。但是他们使
用软件的目的是相同的,就是,向你的软件发出指定的操作指令,使用该操作所代表的
软件功能!
在种种不同的用户的需求之中,搞清所有用户的最终目的之后,我们应该仔细的想一想
,不同的用户为了达到相同目的,使用了不同的手段。哦!原来目的和手段是两回事,
只不过每个人用不同的手段达到相同的目的。
因此,我们应该将操作界面与操作逻辑分离,应该首先分清手段和目的,然后将手段与
目的联系起来。这样,操作界面的更改将不会影响到操作逻辑,手段是手段,目的是目
的。为了达到目的,可以不择手段,就是这个道理!
说道这里,也许有人会说:这个道理谁都懂。那么,如果在我们开发软件的过程中,多
运用这些浅显的道理,就不难设计出简单而又实用的软件结构。
操作界面与操作逻辑的分离可以为我们带来哪些好处呢?
1. 易于理解
你可以在编写业务操作代码时,不关心软件的界面操作模式,而在美化你的软件时,只
考虑界面的美学问题。外表是外表,内心是内心,只有你知道,外表美观的界面连接着
精巧内秀的操作代码,表里如一。你的朋友们即可欣赏到漂亮的软件外表,也容易读懂
你的内心。
2. 易于维护
不管将来流行什么软件界面,是时新HomePage式样还是OutLook的经典面版。你可以只修
改与界面有关的部分,就可满足要求。也可将你的软件彻底改头换面,以崭新的面容出
现。甚至,可表演川剧名家传内不传外传男不传女的独门绝技-变脸。但软件还是软件,
软件的内在功能逻辑并未改变。不过,由于满足了用户喜新厌旧,追逐流行时尚的习惯
,将使软件公司的老板们的腰包越来越鼓。
3. 可扩充性强
人机工程学不断取得的新突破,将为软件使用者提供更多的命令输入方式。现在,语音
识别技术已逐渐商用化。你已经可以利用IBM ViaVoice提供的语音识别支持模块,开发
能识别语音命令的控件。将这样的控件加入你的软件中,并与你以前编写的操作逻辑关
联,你的软件就象长了耳朵,有了灵气,可以倾听你的述说。我也相信,总有一天可编
写一种可以看懂你的表情的控件,那么,软件将可以根据你的情绪对你抚以不同冷暖的
关怀。但软件的灵魂还是原来的操作逻辑代码!
4. 适合大规模软件开发
大规模软件开发讲求分工协作,各尽其才。如果你是软件公司的老板,那么,在系统分
析设计人员完成软件设计之后,你可以找一批时装设计师为你的软件设计漂亮性感的界
面,请精于编码的程序员编写操作逻辑。而你可以轻松地管理和控制各道工序的质量,
而且责任分明。
尽管天涯海角的吹了一通,但我们还得回到我们主题。操作界面与操作逻辑的分离,到
底要遵循哪些方法和原则呢?
首先,你应当归纳整理当前模块要向用户提供的功能,划分出明确的操作命令集。也就
是说,设计可提供给用户的命令,这有点象老式命令行程序的可用命令清单列表。记住
,这些命令集体现的是用户执行操作的目的。
然后,在根据命令集中的每一命令规划操作逻辑代码,按照编写程序的一般原则编制操
作逻辑代码。有关编写程序的一般原则,可参阅《初恋DELPHI》一书。
最后,要提醒一点的就是,操作命令有时是受程序状态控制的。有的程序员为了实现在
某中状态下禁止用户发出指定的操作命令,想尽办法控制菜单、按钮等命令控件的属性
,封杀和打压所有可执行命令的控件。但是,你想禁止的是命令,而不是控件!
下面我们将会讨论在分离操作界面和操作逻辑方面,DELPHI为我们提供的很有价值的东
西,即TActionList和TAction。
第二节 使用TActionList和TAction
“Action”一词的含义是行为、行动或动作,TAction就是抽象了用户想要通过软件界面
表现的行为、开展的行动或执行的动作,而TActionList就描述了动作的命令清单。你可
以将一个TActionList元件加入到你的Form、Frame或DataModule中,双击此元件可定义
需要的TAction对象。
缔造DELPHI大厦的设计师和工程师们,为绝大多数命令控件(如TButton,TMenuItem等
等)设计了Action属性,并且常常碰巧出现在Object Inspecter窗口属性页面的第一项
上,由此可见他们的用心良苦。但不管怎样,他们为我们提供了一个简单的方法,将命
令控件的Action属性与TActionList元件中定义的TAction对象关联。
由于,在TAction对象的OnExecute事件中编写的操作逻辑代码可自动被关联的命令控件
调用,因此,更换命令控件的类型,不用修改或调整操作逻辑代码。这样就可将操作界
面与操作逻辑分开。
如果在某些状态下你要禁止用户执行某种操作,可设置相关TAction对象的Enabled属性
为FALSE,则相关的命令控件将通通被冷冻,该变白脸的变脸白,该变灰脸的变灰脸,而
不管他是生旦净末丑。
再次感谢缔造DELPHI的大师们,是他们在台后为我们做了一切,而且还将他们的九阴真
经放到Source/VCL/ActnList.pas单元中。
既然有大师们的九阴真经,我们就应该拿来修炼,增强我们的内功。
首先,在ActnList单元中,TActionList是这样声明的:
  TActionList = class(TCustomActionList)
  published
    property Images;
    property OnChange;
    property OnExecute;
    property OnUpdate;
  end;
TActionList只发表了TCustomActionList的一些属性和事件。其实,TActionList就是用
来装TAction的容器,并提供了管理TAction的方法。有关TActionList的详细用法,我们
将在随后开发的TActionManager控件中讨论。
再来看看TAction的声明的:
  TAction = class(TCustomAction)
  published
    property Caption;
    property Checked;
    property Enabled;
    property HelpContext;
    property Hint;
    property ImageIndex;
    property ShortCut;
    property Visible;
    property OnExecute;
    property OnHint;
    property OnUpdate;
  end;
TAction是从TCustomAction类继承的,与前面一样,只不过将父类的一些特征发表。我
不想逐一解读TAction的各个类层次的原代码,相信读过《初恋DELPHI》一书的朋友都能
够解读这些代码。我只想讨论TAction发表给我们的这些特性,能为我们提供什么功能。
由于还有一些特性是在TAction的父辈类中发表的,在讨论时也将一起谈到,不过一些不
重要的特性就不再废话了。
Caption属性:填写此属性,你可以用简短的标题向用户说明命令操作的含义。此属性将
自动更新相关命令控件的Caption属性,所以按钮和菜单的Caption属性要在此输入。
Category属性:操作命令的类别,主要是为了方便你分类管理你的操作命令集。此属性
是在TAction的爷爷辈TContainedAction中发表的,在我们随后开发的TActionManager控
件中,你将看到如何此属性管理操作命令。
Enabled属性:控制命令操作是否可执行,可自动控制关联命令控件是否允许执行。封杀
命令控件应该从这里下手。
ImageIndex属性:表示操作命令的图标索引,与TActionList的Images联系。有图标的命
令控件可以享受到他的恩惠。
ShortCut属性:用键盘发出操作命令的快捷键,为喜好键盘操作的人们带来的福音。
Visible属性:控制Enabled可以让命令控件变灰,控制此属性可以让命令控件消失在用
户面前。注意,虽然命令控件消失,但用户仍可通过快捷键发出操作命令,而Enabled才
可真正封杀命令。
OnExecute事件:可以在此事件中编写操作逻辑代码,用户触发相关的命令控件时,将自
动调用此事件的操作逻辑代码。
其他的特性就自己查看DELPHI的联机帮助吧!
初步了解TActionList和TAction之后,我们将开始实践。下面的PRETTYPOEM程序演示了
TActionList和TAction的基本用法,很简单,如果你不想浪费时间可直接跳到后面的MU
LTIFACE程序,看看如何表演川剧的绝活儿-变脸。
你可以在随书所附的光盘中找到此程序的原代码。
文件PRETTYPOEM.DPR
program PRETTYPOEM;
uses
  Forms,
  Main in 'Main.pas' {fMain};
{$R *.RES}
begin
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
文件Main.pas
unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Menus, ActnList, ComCtrls, ToolWin, StdCtrls, ImgList;
type
  TfMain = class(TForm)
    MainMenu: TMainMenu;
    ActionList: TActionList;
    actGuCheng: TAction;
    actXiMuRong: TAction;
    actLiQingZhao: TAction;
    actSuShi: TAction;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    N4: TMenuItem;
    N5: TMenuItem;
    N6: TMenuItem;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    cbDisableLiQingZhao: TCheckBox;
    actDisableLiQingZhao: TAction;
    actHideLiQingZhao: TAction;
    cbHideLiQingZhao: TCheckBox;
    ImageList: TImageList;
    labPoem: TLabel;
    labShadow: TLabel;
    procedure actGuChengExecute(Sender: TObject);
    procedure actXiMuRongExecute(Sender: TObject);
    procedure actDisableLiQingZhaoExecute(Sender: TObject);
    procedure actHideLiQingZhaoExecute(Sender: TObject);
    procedure actSuShiExecute(Sender: TObject);
    procedure actLiQingZhaoExecute(Sender: TObject);
  private
    { Private declarations }
    procedure RecitePoem(Poem:string);
  public
    { Public declarations }
  end;
var
  fMain: TfMain;
implementation
{$R *.DFM}
procedure TfMain.RecitePoem(Poem: string);
begin
  labPoem.Caption:=Poem;
  labShadow.Caption:=Poem;
end;
procedure TfMain.actGuChengExecute(Sender: TObject);
begin
  RecitePoem('黑夜给我黑色的眼睛  我却用她寻找光明');
end;
procedure TfMain.actXiMuRongExecute(Sender: TObject);
begin
  RecitePoem('溪水急着要流向大海  而浪潮却渴望重归土地');
end;
procedure TfMain.actSuShiExecute(Sender: TObject);
begin
  RecitePoem('人有悲欢离合  月有阴晴圆缺');
end;
procedure TfMain.actLiQingZhaoExecute(Sender: TObject);
begin
  RecitePoem('生当做人杰  死亦为鬼雄');
end;
procedure TfMain.actDisableLiQingZhaoExecute(Sender: TObject);
begin
  actLiQingZhao.Enabled:=not cbDisableLiQingZhao.Checked;
end;
procedure TfMain.actHideLiQingZhaoExecute(Sender: TObject);
begin
  actLiQingZhao.Visible:=not cbHideLiQingZhao.Checked;
end;
end.
文件Main.dfm
object fMain: TfMain
  Left = 192
  Top = 107
  Width = 442
  Height = 195
  BorderIcons = [biSystemMenu, biMinimize]
  Caption = '谈古论今'
  Color = clBtnFace
  Font.Charset = GB2312_CHARSET
  Font.Color = clWindowText
  Font.Height = -12
  Font.Name = '宋体'
  Font.Style = []
  Menu = MainMenu
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 12
  object labShadow: TLabel
    Left = 17
    Top = 41
    Width = 380
    Height = 20
    Caption = '古今诗词名句欣赏,请选择你喜爱的诗人。'
    Font.Charset = GB2312_CHARSET
    Font.Color = clBlack
    Font.Height = -20
    Font.Name = '隶书'
    Font.Style = []
    ParentFont = False
    Transparent = True
  end
  object labPoem: TLabel
    Left = 16
    Top = 40
    Width = 380
    Height = 20
    Caption = '古今诗词名句欣赏,请选择你喜爱的诗人。'
    Font.Charset = GB2312_CHARSET
    Font.Color = clBlue
    Font.Height = -20
    Font.Name = '隶书'
    Font.Style = []
    ParentFont = False
    Transparent = True
  end
  object ToolBar1: TToolBar
    Left = 0
    Top = 0
    Width = 434
    Height = 26
    AutoSize = True
    ButtonWidth = 63
    Caption = 'ToolBar'
    EdgeBorders = [ebTop, ebBottom]
    Flat = True
    Images = ImageList
    List = True
    ShowCaptions = True
    TabOrder = 0
    object ToolButton1: TToolButton
      Left = 0
      Top = 0
      Action = actGuCheng
    end
    object ToolButton2: TToolButton
      Left = 63
      Top = 0
      Action = actXiMuRong
    end
    object ToolButton3: TToolButton
      Left = 126
      Top = 0
      Action = actSuShi
    end
    object ToolButton4: TToolButton
      Left = 189
      Top = 0
      Action = actLiQingZhao
    end
  end
  object Button1: TButton
    Left = 12
    Top = 80
    Width = 75
    Height = 25
    Action = actGuCheng
    TabOrder = 1
  end
  object Button2: TButton
    Left = 116
    Top = 80
    Width = 75
    Height = 25
    Action = actXiMuRong
    TabOrder = 2
  end
  object Button3: TButton
    Left = 12
    Top = 112
    Width = 75
    Height = 25
    Action = actSuShi
    TabOrder = 3
  end
  object Button4: TButton
    Left = 116
    Top = 112
    Width = 75
    Height = 25
    Action = actLiQingZhao
    TabOrder = 4
  end
  object cbDisableLiQingZhao: TCheckBox
    Left = 232
    Top = 116
    Width = 81
    Height = 17
    Action = actDisableLiQingZhao
    TabOrder = 5
  end
  object cbHideLiQingZhao: TCheckBox
    Left = 328
    Top = 116
    Width = 81
    Height = 17
    Action = actHideLiQingZhao
    TabOrder = 6
  end
  object MainMenu: TMainMenu
    Images = ImageList
    Left = 332
    Top = 4
    object N1: TMenuItem
      Caption = '现代情怀'
      object N2: TMenuItem
        Action = actGuCheng
      end
      object N3: TMenuItem
        Action = actXiMuRong
      end
    end
    object N4: TMenuItem
      Caption = '古典风范'
      object N5: TMenuItem
        Action = actSuShi
      end
      object N6: TMenuItem
        Action = actLiQingZhao
      end
    end
  end
  object ActionList: TActionList
    Images = ImageList
    Left = 360
    Top = 4
    object actGuCheng: TAction
      Category = '现代情怀'
      Caption = '顾城'
      ImageIndex = 0
      ShortCut = 16455
      OnExecute = actGuChengExecute
    end
    object actXiMuRong: TAction
      Category = '现代情怀'
      Caption = '席慕容'
      ImageIndex = 1
      ShortCut = 16472
      OnExecute = actXiMuRongExecute
    end
    object actSuShi: TAction
      Category = '古典风范'
      Caption = '苏轼'
      ImageIndex = 2
      ShortCut = 16467
      OnExecute = actSuShiExecute
    end
    object actLiQingZhao: TAction
      Category = '古典风范'
      Caption = '李清照'
      ImageIndex = 3
      ShortCut = 16460
      OnExecute = actLiQingZhaoExecute
    end
    object actDisableLiQingZhao: TAction
      Category = '控制'
      Caption = '禁止李清照'
      OnExecute = actDisableLiQingZhaoExecute
    end
    object actHideLiQingZhao: TAction
      Category = '控制'
      Caption = '隐藏李清照'
      OnExecute = actHideLiQingZhaoExecute
    end
  end
  object ImageList: TImageList
    Left = 388
    Top = 4
Bitmap = { ......}
原创粉丝点击