[DELPHI]单例模式(singleton) 陈省


======================================================
注:本文源代码点此下载
======================================================

所谓单例就是系统中只能存在某个类的一个实例,在现实中只能存在一个实例的对象是很常见的,比如系统配置对象只能有一个,

比如一个客户端同服务器的tcp/ip连接经常只允许有一个连接等等。下面是一个单例模式的uml图:

单例模式的实现

那么如何保证在系统中每时每刻只有一个类的实例存在呢,这可以通过静态变量来实现,在调用getinstance时判断静态变量是否为nil,

如果为nil表示系统中没有类的实例,则构造对象,同时将类实例赋值给静态变量,如果不为nil,则直接返回静态变量对应的类的实例。

java或者c++中,类中的变量可以修饰为static表示该变量不依赖于类的实例而单独存在,在delphi中没有类似的关键字,所以只能是

定义一个单元的私有变量来实现对类实例的引用计数。

同时,另外在java中为了防止用户使用类的构造函数来创建多个类的实例,需要将构造函数的存取属性改为private,但是在delphi中,

编译器对构造函数的保护级别进行了特殊的处理,即便将contructor方法设定为private存取权限,编译器仍然会将contructor的保护级

别修正为public,因此将调整构造函数的保护级别防止多例的产生在delphi中是行不通的。幸好tobject基类中定义了newinstance方法,

这个方式是一个类方法,通过编译器魔法系统在每次构造对象时都会调用这个类方法,那么通过重载这个静态方法,就可以实现对构造函数的控制了。

下面就是一个单例的配置类示意代码:

tsingleconfig=class(tobject)

private

    fconfigpath: string;

procedure setconfigpath(const value: string);

public

class function getinstance():tsingleconfig;

    //系统配置路径

property configpath:string read fconfigpath write setconfigpath;

    //...省略

class function newinstance: tobject; override;  

procedure freeinstance;override;

end;

implementation

var

globalconfig:tsingleconfig=nil;//单元内私有的静态配置对象变量

{ tsingleconfig }

procedure tsingleconfig.freeinstance;

begin

inherited;

globalconfig:=nil;

end;

class function tsingleconfig.getinstance: tsingleconfig;

begin

if not assigned(globalconfig) then

    globalconfig:=tsingleconfig.create();

result:=globalconfig;

end;

class function tsingleconfig.newinstance: tobject;

begin

if not assigned(globalconfig) then

    globalconfig:=tsingleconfig(inherited newinstance);

result:=globalconfig;

end;

procedure tsingleconfig.setconfigpath(const value: string);

begin

fconfigpath := value;

end;

在静态的类方法getinstance中,通过对静态变量globalconfig对应的对象进行判断来首先判断globalconfig变量是否为nil

,如果为nil,则表明系统中还没有初始化对象的实例,这时调用私有的构造函数来初始化对象,并将其赋值给globalconfig

变量,如果不为nil,则返回已经创建的globalconfig对象。通过globalconfig的静态变量,就可以保证对象的实例始终只有

一个(注意的是:globalconfig对象需要声明在implementation部分,而不要声明在单元interface部分,这样变量对单元外

的用户是不可见的,这样可以保护变量不会被用户误修改)。

此外,用户可能会在使用完对象后,将其释放,在delphi中,一个对象被释放后,它的实例对应的变量并不会自动设定为nil

,如果之后用户再次调用getinstance获得全局对象时,虽然对象已经被销毁了,但是assigned(globalconfig)仍然返回为真,

那么getinstance就返回一个错误的指针,导致av错误。为了避免这种情况,可以重载freeinstance方法,该方法在对象被释

放时总是会被调用的,在freeinstance方法中释放对象后将globalconfig重新设定为nil就可以了。

vcl中的单例

在vcl中也有很多的单例,比如剪贴板类tclipboard类,在clipbrd.pas单元中提供了类似于上面的实例控制技术,不过它是

通过函数clipboard来返回剪贴板的唯一实例的,

function clipboard: tclipboard;

begin

if fclipboard = nil then

    fclipboard := tclipboard.create;

result := fclipboard;

end;

同样的,它也在类的析构函数中将静态变量设定为nil

destructor tclipboard.destroy;

begin

if (fclipboard = self) then

    fclipboard := nil;

inherited destroy;

end;

不过同前面的重载newinstance的方法相比,vcl中方法缺陷就是不能防止用户多次创建用户的实例,下面代码分别

调用tclipboard类和tsingleconfig类的create方法两次,然后比较两次调用后获得的实例的内存地址,判断类是

被创建了几次:

procedure tform1.btn1click(sender: tobject);

var

p1:pointer;

p2:pointer;

begin

p1:=tclipboard.create();

p2:=clipboard;

showmessage(inttostr(integer(p1)));

showmessage(inttostr(integer(p2)));

end;

procedure tform1.btn2click(sender: tobject);

var

p1:pointer;

p2:pointer;

begin

p1:=tsingleconfig.create();

p2:=tsingleconfig.create();

showmessage(inttostr(integer(p1)));

showmessage(inttostr(integer(p2)));

end;

从运行结果可以知道tclipboard类的构造函数两次调用后返回的地址不同,而tsingleconfig返回的地址则相同。

可以看出重载newinstance的方法更加严谨,不及出错。


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值