类方法/"类变量"/构造函数 请温柔一刀等大侠指教!(100分)

  • 主题发起人 主题发起人 avant
  • 开始时间 开始时间
A

avant

Unregistered / Unconfirmed
GUEST, unregistred user!
SINGLETON(单件)模式保证一个类仅有一个实例,并提供一个访问它的全局访问点.实现方法是:
<font color="red">让类自身负责保护它的唯一实例 </font>
做了一个试验,如下:
unit Singleton;

interface

type
TSingleton = class (TObject)
private
Prop: string;
protected
constructor Create;
public
class function GetProp: string;
class procedure SetProp(Value: string);
end;

implementation

var ASingleton: TSingleton;

constructor TSingleton.Create;
begin
if ASingleton = nil then
begin
ASingleton := inherited Create;
ASingleton.Prop := 'INIT';
end;
end;

class function TSingleton.GetProp: string;
begin
if ASingleton = nil then
TSingleton.Create;
Result := ASingleton.Prop;
end;

class procedure TSingleton.SetProp(Value: string);
begin
if ASingleton = nil then
TSingleton.Create;
ASingleton.Prop := Value;
end;

end.

经过测试,确实保证了只创建一个类的实例,我的疑惑是,我怎么释放ASingleton?什么时间释放ASingleton?
 
这是什么代码?看了半天没看懂!

ASingleton 是 TSingleton 的一个实例,而在这个类的构造函数里居然要访问这个类的实例?
这是一个什么概念?好象不符合OOP吧?还是我才疏学浅?
还请高手指点!
 
dear教父:
>>好象不符合OOP吧?
[惶恐的...]我好像...好像看到Java/C++里有很多这样的用法,那里叫做静态(static)常量/变量,
静态函数...
单件[Singleton]模式是《设计模式——可复用面向对象软件的基础》中23个模式(其他如工厂方
法、代理、适配器等等都是应用非常广泛的,在VCL中也能看到)中的一个,转述作者动机如下:
对一些类来说,只有一个实例是很重要的,虽然系统中可以有许多打印机,但却只应有一个打印
假脱机(printer spooler),支应有一个文件系统,一个数字滤波器只能有一个A/D转换器。
我有一个类似的情况可能用它好一些...当然用全局变量也可以,我还是希望学一学,用一用,我所见
到的实现都是java的,没有释放这个概念,但是Delphi里应该释放吧——如果这个类在复杂一些,实用一
些?
望赐教.
 
看了半天才明白你说的单件模式是什么意思.
要释放可以这么做:
Destructor Destroy
override;

Destructor TSingleton.Destroy
begin
ASingleton.Free;
Inherited;
end;
 
hopfield:
这个我知道的,不是这个意思:)

嗯,可能只能如此。类自身可以负责创建它的唯一实例(调用者不需显式的去创建),却无法
负责释放它的唯一实例,而只能由调用者来显式的释放。

记得温柔一刀、左轻侯等大虾讨论过类方法、“类变量”,盼着能继续听听高论。
 
to avant:
我对你的例子有疑问,假设另外有一个TSingleton类型的变量 BSingleton,
如果我对它进行初始化 BSingleton := TSingleton.Create,则 BSingleton 实际上与
ASingleton 并不指向同一个 Singleton 的实例(请注意到 Object Pascal 中对象变量
实际上是指向对象实例的指针),BSingleton 的数据将得不到初始化,BSingleton 的
私有域 Prop 实际上还是空的字符串(其实也是指针)。只不过你调用 BSingleton 的时候
返回的是 ASingleton 的 Prop 罢了。因此,对于对 TSingleton 的每个属性的访问,你
都不得不将它转向为对 ASingleton 对应属性的访问。
而我想 Singleton 模式的原意应该是 BSingleton 初始化时令它也指向 ASingleton
所指的实例,这才是真正的单实例。那样也就不必对对每个属性的访问都作转向了。你的
做法只是从效果上与这种方式相同罢了。
要做到令 BSingleton 初始化时也指向 ASingleton 所指的实例,我现在只想到了重载
TObject 的 NewInstance 和 FreeInstance 方法的办法,另外在TSingleton 中设一个
域作为对 Singleton 的引用计数,与 COM 的机制一样,Create 时计数加 1,Destroy 时
计数减1,引用计数为 0 时就释放 Singleton。 我也想听听有什么好办法。
 
由于 object pascal 的限制,op 没有实现 c++ 中的类的静态成员(所有实例只有一份
拷贝)。所以,实际上很多实现利用了 TXXXList。比如 Rxlib 源代码。类在创建的时候
注册到 xxlist。
 
邪门,这是什么怪招?:-)
这种应该是不规范的,就象很多书上都强调过的,类的代码不应该引用类的实例,
就象在TForm1中不应该访问Form1一样,虽然编译也可以通过。
理由如bbkxjy所述
在C++中可以在类里面设置一个static变量,作为引用计数,create对象时检查一下
delphi可能只能用全局变量了
一定要象你这样用的话,其实根本不必写两个类方法,直接写成两个全局函数不是
清楚得多么?:-)
 
bbkxjy:
>>对 TSingleton 的每个属性的访问,你都不得不将它转向为对 ASingleton 对应属性的访问
看题:<font color="red">让类自身负责保护它的唯一实例</font>
该类只维护一个实例;事实上,<font color="red">理想的状态是编程中不允许实例化任何对象,
直接使用类方法,在C++/JAVA中通过把构造函数定义为protected,试图实例化时会得到编译错误,但
我在Delphi中把构造函数定义为protected,还可以调用TObject.Create,于是就象代码中那样把构造
函数给覆盖了</font>,
使用该类可如下
...
TSingleton.SetProp('Hello');
...
<font color="green">//可能在另一单元了</font>
Prop := TSingleton.GetProp;
<font color="blue">Singleton模式是对全局变量的一种改进.它避免了那些存储唯一实例的全
局变量污染名空间.</font>

mikedeakins:
>>op 没有实现 c++ 中的类的静态成员
温柔一刀、左轻侯等大虾讨论过的“类变量”(implementation var ASingleton: TSingleton;)
应该相当于静态成员吧?
>>很多实现利用了 TXXXList
用TXXXList实现静态成员吗?能否举个例子?谢谢!

左轻侯:
让我在琢磨琢磨...
>>直接写成两个全局函数不是清楚得多么
我最初也这么想来着,关键是看到了某个Java程序在配置系统参数时,依据单件模式设计了一个
PropertyManager类,在整个程序中也不用声明全局变量,也不显式的实例化,直接就用类方法来读写
系统参数,感觉新鲜,一时心动,写来玩一玩 :-)
我发现《设计模式》上讲的很多东西都非常有道理(要不也不至于鼎鼎大名,好多论坛/学习小
组专门琢磨它呢),比如说开篇讲的面向对象设计的几个基本原则:
<font color="blue">针对接口编程,而不是针对实现编程。</font>——我原来可是对Delphi的抽象类
Tstrings,TStream大骂特骂的呀,现在 :-)
<font color="blue">优先使用对象组合,而不是类继承。</font>——我原来可是千方百计牵强附会的往
继承上靠,以显得OO呢 :-)
 
java和delphi不同,java是没有全局函数的,所有的成员都封装在类里面
所以为了方便,把访问系统的方法写成类方法是很自然的事
delphi中是可以使用全局函数的,所以不需要这么折腾:-)
 
看离线库的时候找到了这个问题的标准答案:LID=375317
相信你看了以后会很爽的:-)
 
to avant:
看过左轻侯说的问题了吗?BakuBaku 的做法是正确的,他重载了NewInstance和
FreeInstance。因为 TObject 的 Create 方法是空的,什么都没做,那么,对象
实例占用的内存,及初始化是在那里完成的呢?这个魔法是由编译器暗地里实现的,
在生成的机器码中,在进入TObject.Create前,编译器自动生成了调用
TObject.NewInstance 的代码,NewInstance 会调用 GetMem 分配内存,跳到
TObject.InitInstance对这块内存进行初始化,然后返回该内存地址作为结果,
即对象变量实际上是指向这块内存的指针。
因此,当执行 BSingleton := TSingleton.Create 时,BSingleton 就指向了另一
个 TSingleton 的实例,而不是ASingleton,这样你的实例实际上并不唯一,
除非你重载了 NewInstance 。这么说不知道够不够清楚。
 
高、实在是高!
Listen...
 
bbkxjy:
>>当执行 BSingleton := TSingleton.Create时,BSingleton就指向了另一个 TSingleton 的实例,而不是ASingleton
你试了没有 :) BSingleton也指向ASingleton的.
constructor TSingleton.Create;
begin
if ASingleton = nil then
begin
ASingleton := inherited Create;
ASingleton.Prop := 'INIT';
end;
end;
调用BSingleton := TSingleton.Create时,如果已经存在ASingleton,什么也没做,没有inherited Create,是不是也就
不会调用NewInstance?
BakuBaku的做法应该更好.
我在试一试.
谢谢左轻侯
 
To avant:
我在Delphi5中试了一下,New|Project,Form1 上放了一个Button,对应的Unit1.pas
如下(TSingleton 部分是从你的Singleton单元中拷过来的):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

TSingleton = class (TObject)
private
Prop: string;
protected
constructor Create;
public
class function GetProp: string;
class procedure SetProp(Value: string);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
var ASingleton: TSingleton;
constructor TSingleton.Create;
begin
if ASingleton = nil then
begin
ASingleton := inherited Create;
ASingleton.Prop := 'INIT';
end;
end;
class function TSingleton.GetProp: string;
begin
if ASingleton = nil then
TSingleton.Create;
Result := ASingleton.Prop;
end;
class procedure TSingleton.SetProp(Value: string);
begin
if ASingleton = nil then
TSingleton.Create;
ASingleton.Prop := Value;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
BSingleton: TSingleton;
begin
BSingleton := TSingleton.Create;
try
ShowMessage(IntToStr(Longint(Pointer(BSingleton))));
ShowMessage(IntToStr(Longint(Pointer(ASingleton))));
finally
if Pointer(BSingleton) <> Pointer(ASingleton) then
BSingleton.Free;
end;
end;
initialization
finalization
if ASingleTon <> nil then
ASingleTon.Free;
end.
试验结果:
1、第一次Click Button1,显示的两个数字是相同的;
2、以后每次 Click Button1,则显示的第一个数字,即BSingleton实例的地址,均不同;
显示的第二个数字,即ASingleton实例的地址均与第一次Click Button1时 Asingleton
实例的地址一致。
结论:ASingleton 始终指向同一个实例,BSingleton 每次 Create 都指向一个新的实例。
这样试验不知道正确以否?
 
bbkxjy应该是对的:-)
慢了一步:-)
 
哈哈,是我错了,当时一试就知道了,想上来,据渔网坏了,今天才拨号上来,不好意思:)
bbkxjy,左轻侯:谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢
BakuBaku的做法是基于对VCL的深刻理解,不能屏蔽掉Create,就从NewInstance着手,高,实在是高.
我奇怪这些资料都是怎么知道的,一看原来联机帮助里就有说明,我回去得好好学学:)
我本来的问题里有"怎么释放ASingleton?什么时间释放ASingleton?"如果我把TSingleton放到
一个单独的单元里,下面这段代码会执行吗?
finalization
if ASingleTon <> nil then
ASingleTon.Free;
望指教:)
 
哟,原来BakuBaku的做法bbkxjy早就给我指明了,惭愧,当时浆糊的很:)
考虑在加点分,好久问题没解决的这么爽了:)
 
还可以继续:)
 
finalization部分的代码在程序终止时执行,帮助中说只要initialization
部分的代码在程序初始化时开始执行了,对应finalization部分的代码在结束时
就保证会被执行。Application,Screen这种全局对象实例就是在initialization部分创建,
在对应的finalization部分释放的。
 
后退
顶部