代码重构——之获得封装性DELPHI编码实例 (引用)

原创 2005年05月04日 11:41:00
代码重构——之获得封装性DELPHI编码实例
 
代码重构是获得结构良好的方法,通过重构,我们在保持功能不变的情况下,改善代码的质量,提高代码的复用程度。下面是一个获得改善代码质量和获得封装性的一个具体的例子。(例子使用DELPHI)
代码功能:
       给数据集设(TClientDataSet)置过滤器,用户可以在一个TComboBox中选择要过滤的字段,然后在一个Tedit框中输入要过滤的值。如图一:
最常见的做法就是在TComboBox的Items属性中硬码写入我们数据集中的字段名称,然后在代码中加入一大堆case或者if语句在判断用户选择的字段来给数据集设置过滤器。
……
      case ComboBox1.ItemIndex of
0:
             ClientDataSet.Filtered := False;
        ClientDataSet.Filter := ' F_CODE = ''' + Edit2.Text + '''';
              ClientDataSet.Filtered := True;
1:
             ClientDataSet.Filtered := False;
        ClientDataSet.Filter := ' F_CHINESE_NAME = ''' + Edit2.Text + '''';
              ClientDataSet.Filtered := True;
……       
end;                
或者用
….…
       if ComboBox1.Text = '物料编码' then
    begin
              ClientDataSet.Filtered := False;
        ClientDataSet.Filter := ' F_CODE = ''' + Edit2.Text + '''';
              ClientDataSet.Filtered := True;
end
else if ComboBox1.Text = '名称' then
begin
ClientDataSet.Filtered := False;
        ClientDataSet.Filter := ' F_CHINESE_NAME = ''' + Edit2.Text + '''';
              ClientDataSet.Filtered := True;
 
end
……
这样的代码通过硬码同样也实现了这个给数据集设置过滤器的功能,满足了需求,但是上面这段代码是不灵活的。如果数据集的字段很多就要求编码人员一个一个字段录入在Items中,而且在写case必须核对好顺序,不然设置的过滤器就是错误的也就很容易由开发人员引入BUG。用if语句时也一样维护一个大量的if同样是痛苦的,而且不支持需求变化,当用户要求改变数据集字段的中文显示名称时必须也要记住更改TComboBox. Items中的硬码数据,如果一旦忘记就会引入BUG。
 
于是我在第一次重构中,尝试动态的加载TComboBox. Items中的数据,同时为了实现加载后用户选择时实现对照。我在这个查询FORM中加了一个 私有FFields: array[0..20, 0..2] of string; 字段来保存数据集中的字段信息数据。同时实现了一个加载数据的过程:
 
procedure TFrmSPARealStorageQuery.GetQueryFields;
var
  i, iFieldsCount: Integer;
begin
  iFieldsCount := 0;
  with DBGride1.DataSource.DataSet do
  begin
    for i := 0 to Fields.Count - 1 do
      if Fields[i].Visible then
      begin
        FFields[iFieldsCount, 0] := Fields[i].FieldName;
        FFields[iFieldsCount, 1] := Fields[i].DisplayLabel;
        Inc(iFieldsCount);
      end;
    ComboBox1.Items.Clear;
    for i := 0 to iFieldsCount - 1 do
      ComboBox1.Items.Add(FFields[i, 1]);
  end;
end;
 
这样就实现了在运行时动态加载字段信息。这样我的过滤器设置就变成了这样的。
 
if ComboBox1.Text <> '' then
begin
ClientDataSet.Filtered := False;
    ClientDataSet.Filter := FFields[ComboBox1.ItemIndex, 0] +  '''' + Edit2.Text + '''';
       ClientDataSet.Filtered := True;
end;
 
本方法无疑增加了代码的灵活性,同时增加了代码的复用度,因为代码很好的隔离了变化的数据。因此只要在另一个也是要实现这种的功能的FORM中增加私有字段FFields: array[0..20, 0..2] of string 和使用上面的动态加载数据集字段过程,就可以说方便的实现了重用。但是这种重用并不是很好的,因为我们没有实现很好的封装性。导致在你的程序中到处散落有重复的代码(你常常会通过COPY来获得这个函数的重用,因为上面的代码是没有好的封装性)。如果有一天你要修改数据装载函数你就必须到处去找那里拷贝了该函数——你也得修改散落在其他地方的代码。于是我进行了再一次的重构,并对代码进行了进一步的封装。
代码如下:
unit uDataSetFieldsInfo;
// Description:单元包括 TDataSetFieldsInfo 类,该类封装了获得数据集子段信息。
// 并提供了在combobox列表显示字段显示信息和获得对应子段名称的方法接口
// Created : wuchhao
// Date : 2003.5
 
interface
uses Classes, DBClient, StdCtrls;
type
  TDataSetFieldsInfo = class
  private
    FFieldsList: TStrings;
  public
    constructor Create;
    destructor Destroy; override;
    procedure GetDataSetFields(Source: TClientDataSet);
    procedure ShowFieldsInfo(Target: TComboBox);
    function GetFieldsNameByDisplayLabel(DisplayLabel: string): string;
  end;
 
implementation
 
{ TDataSetFieldsInfo }
constructor TDataSetFieldsInfo.Create;
begin
  FFieldsList := TStringList.Create;
end;
 
destructor TDataSetFieldsInfo.Destroy;
begin
  FFieldsList.Free;
  inherited;
end;
 
procedure TDataSetFieldsInfo.GetDataSetFields(Source: TClientDataSet);
var
  i: Integer;
begin
  FFieldsList.Clear;
  with Source do
  begin
    for i := 0 to Fields.Count - 1 do
      if Fields[i].Visible then
      begin
        FFieldsList.Add(Fields[i].DisplayLabel);
        FFieldsList.Add(Fields[i].FieldName);
      end;
  end;
end;
 
function TDataSetFieldsInfo.GetFieldsNameByDisplayLabel(
  DisplayLabel: string): string;
var
  index: Integer;
begin
  Result := '';
  index := FFieldsList.IndexOf(DisplayLabel);
  if index <> -1 then
    Result := FFieldsList.Strings[index+1]  ;
end;
 
procedure TDataSetFieldsInfo.ShowFieldsInfo(Target: TComboBox);
var
  i: Integer;
begin
  Target.Items.Clear;
  i:=0;
  while i <  FFieldsList.Count do
  begin
    Target.Items.Add(FFieldsList.Strings[i]);
    i:= i+ 2;
  end;
end;
end.
 
单元uDataSetFieldsInfo 封装了与实现本文所述功能相关的数据和方法,把它们封装在一个类里面,从而实现了面向对象设计里面的 Open - Close 原则。类变成了一个黑盒,于是就可方便的重用(black-box reuse),而不必担心代码的重复。同时因为封装了与功能相关的信息,类的职责定义明确(单职责),并有了足够合适的粒度和好的封装性。TdataSetFieldsInfo 很好的把组合框与变化的数据隔离开来,最终提高了代码的复用程度,同时减少了FORM类的职责和 magic number硬编码的量。下面是新的代码:
首先在FORM中声明TdataSetFieldsInfo类的一个引用。
……
在FORM创建的时候调用:
FFieldsInfo := TDataSetFieldsInfo.Create;
FFieldsInfo.GetDataSetFields(cdMaster);
FFieldsInfo.ShowFieldsInfo(ComboBox1);
这时候我的过滤器设置就变成了:
if ComboBox1.Text <> '' then
begin
ClientDataSet.Filtered := False;
    ClientDataSet.Filter := FFieldsInfo.GetFieldsNameByDisplayLabel(ComboBox1.Text) +  '''' + Edit2.Text + '''';
       ClientDataSet.Filtered := True;
end;
 
通过调用FfieldsInfo对象的接口过程来获得对应的子段名称。
 
本文是一个重构代码的简单例子,我想上面我实现的这个类还可以有很多种写法和更好的算法。这里只是提供一种关于重构代码的思路,为提高我们的编写代码质量和它的可维护性、扩展性,探讨OOD编程方式上的思路。

代码重构——之获得封装性DELPHI编码实例

  • zgqtxwd
  • zgqtxwd
  • 2008年04月30日 13:51
  • 98

面向对象三大特性---封装性

思路:1,封装是什么?-->为什么要封装(即封装的好处)--
  • u014167212
  • u014167212
  • 2014年04月22日 16:36
  • 2320

谈谈代码重构

开发人员可能听到过"bad smell"这个词,这个词用在软件编码中是什么意思呢? 代码还有smell吗?当然没有,正如计算机病毒,bug等词一样,这只是个形象的说法。这个词在这里的意思是代码实现了需...
  • weiky626
  • weiky626
  • 2007年05月10日 10:03
  • 31317

Delphi通过机器码获得注册码的完整方案

通过机器码获得注册码的完整方案(转贴过来,留着看看)想加密自己的软件,看过一些软件的作法,思路如下:1、用户安装后,得出本机的机器码(硬盘的序列号,不想用网卡,因为很多机器没有网卡)。生成机器码代码2...
  • lhq_215
  • lhq_215
  • 2014年04月27日 13:55
  • 3710

关于封装--一个有趣的C++例子

    一个有趣的现象,摘自CSDN 吹云Blog《C++从零开始(十二)——何谓面向对象编程思想》原文http://blog.csdn.net/chuiyun/archive/2004/11/26/...
  • baodi_z
  • baodi_z
  • 2004年12月09日 14:09
  • 2409

delphi下中文转UFT-8编码

delphi下中文转UFT-8编码
  • rznice
  • rznice
  • 2016年01月08日 14:50
  • 4013

聊聊HTML静态页面编码规范和前端代码重构

彪悍的人生不需要解释,彪悍的代码不需要注释!作为程序猿,工作中我们都要涉及到与团队合作,为了让团队合作的效率提高,我们在写代码的时候都应该讲究规范,比如注释,html代码的嵌入规则,class命名的规...
  • Qianliwind
  • Qianliwind
  • 2016年06月09日 22:48
  • 1588

c++初级 之 c++的封装性

c++作为面向对象的程序语言,有三大特性:封装性、继承性、多态性。 此篇说明封装性:什么是封装?自己的理解就是将很多同类事物(比如狗类)的共同特性(名字、颜色、跑、叫、吃)封装起来,然后遇到一个该类...
  • zealice
  • zealice
  • 2017年08月24日 14:43
  • 99

Java面向对象:封装性

1 Java面向对象思想 1.1面向对象三大特征 (一)封装性:对外部不可见 (二)继承:扩展类的功能 (三)多态性:方法的重载,对象的多态性 2封装性 1. 类中并不是所...
  • qq_31126879
  • qq_31126879
  • 2016年07月22日 09:52
  • 610

怎样写出漂亮整洁的代码?聊聊clean code的编码、重构技巧

本文从clean code的几个大前提出发,提出了实践clean code的一些手段,重点放在促成clean code的一些常用编码和重构技巧。本文只代表笔者本人的一点点感悟。好的代码,最需要的,还是...
  • xgjianstart
  • xgjianstart
  • 2017年03月07日 20:42
  • 292
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:代码重构——之获得封装性DELPHI编码实例 (引用)
举报原因:
原因补充:

(最多只允许输入30个字)