利用属性封装复杂的选项

1、考虑这样一个场景。

我们的程序中有一个“选项”窗口,这个窗口包含很多选项。其中有一个选项是单选类型的,用户可以从N个选项值中选择一个。

我们需要在用户单击“确定”按钮后把用户选择的值保存到文件中,程序下次启动时再读取到内存中。

 

2、不好的解决方案

通常情况下,我们会在按钮单击事件中写类似下面的代码:

 

procedure TfrmOption.btnOKClick(Sender: TObject);
begin
  if rb1.Checked then
    // save value 1
  else if rb2.Checked then
    // save value 2
  else if rb3.Checked then
    // save value 3
end;

 

或者这样:

 

procedure TfrmOption.btnOK2Click(Sender: TObject);
var
  value: Integer;
begin
  if rb1.Checked then
    value := 1
  else if rb2.Checked then
    value := 2
  else if rb3.Checked then
    value := 3;
  // save value
end;

  

 

这样的代码有什么问题呢?

首先,如果这样的选项一多,那 btnOKClick 中的代码将会非常长,影响了可读性。

其次,btnOKClick 的职责只是负责保存选项,而在上面的代码中还多出了判断选项值的职责。这在以后的维护中将会带来逻辑上的混乱。

 

3、利用属性封装的方案

 从逻辑上来讲,“选项”这个功能可以分为三个部分。

 一是选项值的保存与读取,这部分可以单独写一个类,具体怎么做超出了本文讲解范围,所以暂不做详细解释。

 二是选项值的显示与修改,只保存和读取的选项值是没有意义的,它们必须展示给用户,让用户修改和查看才有意义。

 第三部分也就是接下来我们要讲的部分。因为保存下来的值一般是经过摘要的,利于存储的。而展示给用户的是经过扩展的,修饰的。

 这一转换过程已经体现在上面两段代码中:将单选按钮(或者其他控件)的状态转换为数字进行保存。或者反过来,根据数字的值设置不同单选按钮的选中状态。

 前面说过了,btnOKClick 的职责是保存选项值,那剩下的职责给谁?给“选项”窗口。

 我们可以这样想。“选项”窗口除了负责展示和接受用户对选项的修改,还担负了将用户的输入转换成方便存储的值,同时还担负了反向过程的职责。

 而这个职责可以用属性来实现,利用属性将这个职责变为“选项”窗口的一部分,同时这也屏蔽了一些业务逻辑,有利于以后这个选项的扩展升级。

 下面是具体的代码:

 

unit Unit1;

 

interface

 

uses

  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,

  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,

  Vcl.StdCtrls;

 

type

  TfrmOption = class(TForm)

    btnOK: TButton;

    rb1: TRadioButton;

    rb2: TRadioButton;

    rb3: TRadioButton;

    btnOK2: TButton;

    procedure btnOKClick(Sender: TObject);

  private

    function GetProp1: Integer;

    procedure SetProp1(const Value: Integer);

    { Private declarations }

  public

    { Public declarations }

    procedure SaveOption(Name: string; Value: Integer);

    property Prop1: Integer read GetProp1 write SetProp1;

  end;

 

var

  frmOption: TfrmOption;

 

implementation

 

{$R *.dfm}

 

procedure TfrmOption.btnOKClick(Sender: TObject);

begin

  SaveOption('Prop1', Prop1);

end;

 

function TfrmOption.GetProp1: Integer;

begin

  if rb1.Checked then

    Result := 1

  else if rb2.Checked then

    Result := 2

  else

    Result := 3;

end;

 

procedure TfrmOption.SaveOption(Name: string; Value: Integer);

begin

 

end;

 

procedure TfrmOption.SetProp1(const Value: Integer);

begin

  case Value of

    1: rb1.Checked := True;

    2: rb2.Checked := True;

    else rb3.Checked := True;

  end;

end;

 

end.

  

 

 

后记:为了方便讲解,没有把文中提到的那个选项设计为枚举。更进一步的,可以用枚举结合TRadioGroup控件增强可维护性和可读性。

你可能感兴趣的:(利用属性封装复杂的选项)