娃娃鸭Delphi面向对象编程思想刘艺笔记

本文介绍了Delphi编程中的几个实用技巧,包括使用Decorator模式实现对象状态的自动恢复、TObject类的功能扩展方法、以及TPersistent类的深入解析。通过具体代码示例展示了如何利用这些技巧提高代码质量和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

1、  自动生命周期的整理

unit mtReaper;

 

interface

type

  ImtReaper = interface

    ['{F3E97960-3F35-11D7-B847-001060806215}']

  end;

 

  TmtReaper = class(TInterfacedObject, ImtReaper)

  private

    FObject: TObject;

  public

    constructor Create(AObject: TObject);

    destructor Destroy; override;

  end;

 

implementation

 

uses SysUtils;

 

constructor TmtReaper.create(AObject: TObject);

begin

  FObject := AObject;

end;

 

destructor TmtReaper.Destroy;

begin

  FreeAndNil(FObject);

  inherited;

end;

 

end.

 

=====================================================================

unit frmMain;

 

interface

 

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls;

 

type

  TNoisyDeath = class(TObject)

  public

    destructor Destroy; override;

  end;

 

  TForm1 = class(TForm)

    Button1: TButton;

    Button2: TButton;

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

  private

    procedure WaitAWhile;

  public

    { Public declarations }

  end;

 

 

 

var

  Form1: TForm1;

 

implementation

uses mtReaper;

{$R *.dfm}

 

destructor TNoisyDeath.Destroy;

begin

  showMessage('对象销毁了!');

  inherited;

end;

 

procedure TForm1.WaitAWhile;

var

  i: Integer;

begin

  for i := 0 to 5000 do

  begin

    caption := Inttostr(i);

  end;

end;

 

procedure TForm1.Button1Click(Sender: TObject);

var

  NoisyDeath: TNoisyDeath;

begin

  NoisyDeath := TNoisyDeath.create;

  try

    waitAWhile;

  finally

    NoisyDeath.Free;

  end;

 

end;

 

procedure TForm1.Button2Click(Sender: TObject);

var

  NoisyDeath: TNoisyDeath;

  girm: ImtReaper;

begin

  NoisyDeath := TNoisyDeath.create;

  girm := TmtReaper.Create(NoisyDeath);

  waitAWhile;

end;

 

end.

 

Decorator模式

·在不影响其他对象的情况下,以动态、透明度的方式给单个对象添加职责

·处理那些可以撤消的职责

·当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;另一种情况可能是,因为类定义被隐藏,或类定义不能用于生成子类。

自动恢复字体

unit uSnapshot;

 

interface

 

uses SysUtils, mtReaper,classes;

 

type

  ISnapshot=interface

    ['{FA256FA8-211F-462D-890B-FC0EB6096AD8}']

    procedure Restore;

  end;

 

  TSnapshot=class(TInterfacedObject,ISnapshot)

  private

    FOriginal:TPersistent;

    FTarget:TPersistent;

    FReaper:ImtReaper;

  public

    constructor Create(Target:TPersistent);

    destructor Destroy;override;

    procedure Restore;

  end;

 

implementation

 

constructor TSnapshot.create(Target:TPersistent);

begin

  FOriginal:=TPersistent(Target.classType.create);

  FReaper:=TmtReaper.create(FOriginal);

  FTarget:=Target;

  FOriginal.Assign(Target);

end;

 

destructor TSnapshot.Destroy;

begin

  restore;

  Inherited;

end;

 

procedure TSnapshot.Restore;

begin

  if FTarget<>nil  then

    FTarget.Assign(FOriginal);

end;

end.

unit Unit1;

 

interface

 

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls, ComCtrls;

 

type

  TForm1 = class(TForm)

    FontDialog1: TFontDialog;

    btnSet: TButton;

    Memo1: TMemo;

    procedure btnSetClick(Sender: TObject);

    procedure FormCreate(Sender: TObject);

    procedure WaitAWhile;

  private

  public

    { Public declarations }

  end;

 

var

  Form1: TForm1;

 

implementation

 

uses uSnapshot;

 

{$R *.dfm}

 

procedure TForm1.btnSetClick(Sender: TObject);

var

  FontSnapshot: ISnapshot;

begin

  FontSnapshot := TSnapshot.Create(Memo1.Font);

  {这里可以填写可能会改变TFont状态的任何代码}

  if FontDialog1.Execute then

    Memo1.Font := FontDialog1.Font;

  Memo1.Update;

  WaitAWhile;

end;

 

procedure TForm1.FormCreate(Sender: TObject);

begin

  Memo1.Lines.Add(

    '一个模拟对象状态改变后自动恢复的例子。');

end;

 

procedure TForm1.WaitAWhile;

var i: Integer;

begin

  for i := 0 to 5000 do

  begin

    caption := '状态恢复倒计时:' + Inttostr(5000 - i);

  end;

end;

 

end.

 

这个例子确实有点份量,说明一些问题。

 

2、  TObject

1.       inheritsFrom 判断一个类是否另一类的祖先类

2.       下面三种方案是分别运行的。

procedure TForm1.onclick(Sender: TObject);

var

  str: string;

  tmpclass: TObject;

  classref: TClass;

begin

       ///第一种最简单的解决方案

  str := '';

  ListBox1.Items.Add(str);

  str := format('Class Name = %s', [sender.ClassName]);

  ListBox1.Items.Add(str);

  str := format('Class Parent = %s', [sender.ClassParent.ClassName]);

  ListBox1.Items.Add(str);

  str := format('InstanceSize = %d', [sender.InstanceSize]);

  ListBox1.Items.Add(str);

 

//以下为第二种,复杂的解决方案

  tmpclass := sender;

  while tmpClass.ClassParent <> nil do

  begin

    str := tmpclass.ClassName;

    listbox1.Items.Add(str);

    tmpclass := tmpclass.ClassParent.Create;

  end;

 

 

 {方案三}

  listbox1.Items.Add('============================');

  classref := sender.ClassType;

  while classref <> nil do

  begin

    listbox1.Items.Add(classref.ClassName);

    classref := classref.ClassParent;

  end;

end;

3、  TPersistent

1.TPersistent类在Delphi类中的地位不亚于TObject类,该类定义于Classes单元中,Delphi中定义的类几乎都是由该类派生而来的。TPersistent之所以在Delphi类中居于如此重要的地位是因为它是Delphi可视化编程的基础。该类实现了对象公布(published)属性的存取,即在该类及其派生类中声明为published的属性、方法和事件等可在设计期时显示在Object Inspector窗体或数据模块的DFM 文件中。当Delphi 重新打开原来保存的窗口时,对象的设置值将被DelphiDFM文件中读出。在程序运行期,对象将被初始化为设计期所设置的状态。

2.

{$M+}

 

  TPersistent = class(TObject)

  private

    procedure AssignError(Source: TPersistent);

  protected

    procedure AssignTo(Dest: TPersistent); virtual;

    procedure DefineProperties(Filer: TFiler); virtual;

    function  GetOwner: TPersistent; dynamic;

  public

    destructor Destroy; override;

    procedure Assign(Source: TPersistent); virtual;

    function  GetNamePath: string; dynamic;

  end;

 

{$M-}

 

{$M+}{$TYPEINFO ON}作用是一样的,

{$M-}{$TYPEINFO OFF}作用是一样的。

当一个类在{$M+}{$M-}间声明时,程序编译器将为类生成与RTTI(Runtime Type Information)相关的代码和事件也具有存取特性。如果一个类或祖先类都没有在{$M+}{$M-}中声明,则该类不能在published的属性、事件和方法。

 

3.

TPersistentAssign方法,主要完成两个对象属性的复制,完成对象的published属性、方法和事件的逐个复制。

Assign方法在TPersistent类中声明为虚方法,以便允许每个派生类定义自己的复制对象方法。如果派生类没有重写Assign方法,则TPersistentAssign方法会将复制动作交给源对象来进行:

procedure TPersistent.Assign(Source: TPersistent);

begin

  if Source <> nil

then

Source.AssignTo(Self)

else AssignError(nil);

end;

 

TPersistentAssign方法很少被派生类所覆盖,但AssignTo却常被派生类根据需要覆盖。

如果由AssignTo方法来实现复制,那么必须保证源对象的类已经重写了AssignTo方法,否则将会抛出一个AssignError异常。

procedure TPersistent.AssignTo(Dest: TPersistent);

begin

  Dest.AssignError(Self);

end;

 

procedure TPersistent.AssignError(Source: TPersistent);

var

  SourceName: string;

begin

  if Source <> nil then

    SourceName := Source.ClassName else

    SourceName := 'nil';

  raise EConvertError.CreateResFmt(@SAssignError, [SourceName, ClassName]);

end;

 

Assign方法和AssignTo方法常用于对象的克隆。

 

4.

function  GetNamePath: string; dynamic;

GetNamePath方法是Delphi内部调用的,用于取得对象名以显示在Object Inspector中。

function  GetOwner: TPersistent; dynamic;

=================================================

function TPersistent.GetOwner: TPersistent;

begin

  Result := nil;

end;

=================================================

 

GetOwner用于返回对象的所有者,它往往和GetNamePath一起使用,GetOwnerTPersistent中只返回nil,该方法在TPersistent类中声明而不是在TComponent类中声明,是为了使一些由TPersistent派生的类可以在Object Inspector 中显示出来,如TCollection等类。对于一般应用程序设计来说,不需要直接调用这两个方法。

 

 

5. DefineProperties

procedure DefineProperties(Filer: TFiler); virtual;

TPersistent中声明的DefineProperties方法为非published属性存取提供了接口。对于从TPersistent派生的子类,Delphi编译器在编译时自动为类的published的属性加入与存取相关的代码,而对于其他的非published的属性要存入DFM文件或流中;需要在派生类中覆盖DefineProperties方法来完成这些属性值的存取操作。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值