使用类方法控制实例的唯一性
CST 2005-7-29
文档目的
在面向对象编程中,出于各种原因,有时我们希望控制一个类的实例仅有一个存在于内存中。例如我在编程中遇到一个实际情况,程序的配置数据保存在XML文档中,需要写一个XML配置文件解析类。由于读入的数据可能很庞大,而且在程序每个模块中都用到该解析类的实例,那么如果每次都创建一个实例并重新读入数据,效率就不令人满意了。
类方法
我们可以通过类方法(Class Method)实现实例创建的控制,其实也有许多其他的方法。这里我先介绍“类方法”的实现。
如下是DELPHI对类方法的定义:
A class method is a method (other than a constructor) that operates on classes instead of objects. The definition of a class method must begin with the reserved word class. For example,
type
TFigure = class
public
class function Supports(Operation: string): Boolean; virtual;
class procedure GetInfo(var Info: TFigureInfo); virtual;
...
end;
The defining declaration of a class method must also begin with class. For example,
class procedure TFigure.GetInfo(var Info: TFigureInfo);
begin
...
end;
In the defining declaration of a class method, the identifier Self represents the class where the method is called (which could be a descendant of the class in which it is defined). If the method is called in the class C, then Self is of the type class of C. Thus you cannot use Self to access fields, properties, and normal (object) methods, but you can use it to call constructors and other class methods.
A class method can be called through a class reference or an object reference. When it is called through an object reference, the class of the object becomes the value of Self.
定义一个类方法就是在将一个方法生命在PUBLIC段中,并加上class前缀。这样的方法可以由类直接调用,而不需要先创建类的实例。比如构造函数Create就是一个类方法,我们在创建对象实例的时候这样写:
MyObject:=TmyClass.Create;
此时的Create方法由类TmyClass调用。
实现方法
我们实现的目的就是不以平时的对象访问模式调用对象
Myobject:=TmyClass.Create(self);
Try
With Myobject do begin
//do something
end;
finally
FreeAndNil(Myobject);
End;
而是使用一个类方法GetInstance来间接获取对象实例,这个唯一的对象实例被申明在类的单元文件内(后文回详细说明),在GetInstance函数中判断该实例是否被创建赋值,如果没有,则调用构造函数创建;如果已创建,则直接返回该实例。
这里,我们需要将构造函数Create申明在private段中,仅能由GetInstance判断调用。TmyClass.GetInstance函数返回值为TmyClass类型的变量,如果Tmyclass继承自Tcomponent的话,create方法需要一个Aowner参数,因此我为GetInstance增加一个Aowner参数。
GetInstance类方法的实现不影响类中其他方法的实现,一般我们在调试设计阶段不进行类方法的考虑,而是公开需要测试的对象方法以便于调试。最后要发布类的时候,我们做如下改动:
1. 在类单元文件中,但是在类定义之外申明唯一对象变量。
我的做法是在
implementation
var mySoloObject:TMyClass;
2. 隐藏构造函数
将constructor函数申明在private段中
3. 增加一个GetInstance类方法,定义在Public段中,判断mySoloObject是否被创建。
具体代码如下:
……
//类定义
TConfigReader = class(TComponent)
private
FXMLDoc: TXMLDocument;
constructor Create(AOwner:TComponent); override;
public
FCtrlInfoList:TObjectList;
function OpenXMLFile(const APath:string):boolean;
function ReadCtrlInfoToList: Integer;
class function GetInstance(AOwner:TComponent):TConfigReader;
end;
//唯一对象变量
implementation
var
ConfigReaderSolo:TConfigReader;
//函数定义
class function TConfigReader.GetInstance(AOwner:TComponent):TConfigReader;
begin
if ConfigReaderSolo=nil then
ConfigReaderSolo:=TConfigReader.Create(AOwner);
Result:=ConfigReaderSolo;
end;
访问方法
做了如上修改以后,我们的类就可以实现唯一实例的判断以及创建控制。
在主调单元中,我们想往常一样uses类所在的unit文件,相较普通的对象访问方式,我们采用类似如下的代码:
TConfigReader.GetInstance(Self).MyMethod1;
TConfigReader.GetInstance(Self).MyMethod2;
MyVar:= TConfigReader.GetInstance(Self).MyFunc1;
MyObject:= TConfigReader.GetInstance(Self).Member1;
……
小结
对数据的隐藏、共享、保护是OO编程的优点,利用不同的数据访问级别隐藏相应的方法、成员,可以更大限度控制数据的安全性,也可以让程序按照我们想要的方式运行。类方法和类成员可以在对象没有创建的时候就被访问,因此可以越过对象本身来判断其创建与否。定义在单元内但类外的对象变量可以全局于单元内的所有类。
在我的YAHOO公文包中有这片文章相关的代码,欢迎下载。也希望大家给我提出改进意见。