请问delphi中的静态成员怎么定义?(50分)

  • 主题发起人 主题发起人 zhjwjan
  • 开始时间 开始时间
Z

zhjwjan

Unregistered / Unconfirmed
GUEST, unregistred user!
我想实现设计模式里的singleton模式,但它需要一个私有静态成员,静态成员函数就是在函数
前加个class指示符号,那静态成员呢?如果定义一个全局变量来用,这可是谁都可以访问
的,这样就无法实现了。
 
在implementation中声明吧,Delphi没有静态成员这一说法。
 
应把这个成员声明在implementation中,这样就不会被其它使用它的单元访问了.
但,在该成员的单元中的所有类都能访问;在initialized中初始化它.
 
还有一个问题,我把constructor放在private里,但是其他单元里一样可以访问,这是为什么?
 
估计是你没有把原来的方法override, 你把constrcutor 放到 protected里override试试吧
 
Creating a real singleton class in Delphi 5


Abstract:The article describes how to create a class that follows the singleton
pattern. The class described will take care of the singleton requirements and
effects itself, effectively leaving the programmer to use the class as any
others.
Creating a real singleton class in Delphi
by Lasse V錱s鎡her Karlsen, Systems Developer for Cintra Software Engineering

Errata 1. Implementation of TSingleton.NewInstance had the first test switched,
the one that checked if the Instance variable was assigned or not. -- Fixed
11-08-2000 - Thanks to the people that emailed me about this --
2. Overzealous fix for the first errata, the NewInstance method should now
work as stated. -- Fixed 11.08.2000 --

A singleton is a class that supports the creation of just one object. It's
like your computer -- there's just one keyboard. So if you were writing Delphi
code that simulated your computer, you would want just one object instance to
deal with keyboard read, write, and control activities.
Articles about singleton classes are rare but there are a few on the Internet.
I've seen some in magazines too. But none of these articles include sample code
to create a real singleton class.
By "real" I mean that the class itself enforces the one-instance requirement,
instead of leaving that task to the programmer. All of the articles I have
seen so far have required the programmer to use the class in a special way to
enforce the singleton pattern.
In this article you will see how to create a proper singleton class that
includes logic to enforce the one-instance rule.
Note: The conventional approach, in which the one-instance rule is maintained
explicitly by the programmer, is not without merit. Real singleton classes like
the one I present here effectively hide the details and awareness of the
singleton pattern from the programmer. The programmer is relieved of the task
of enforcing the pattern -- that's good -- but the programmer may also be
unaware of the special nature of the class, which is bad. If youdo
n't know
that the class is a singleton class then
all sorts of errors can crop up. You
have been warned!
Writing the code
Our goal is to write a class that can be used like this:
procedure Test;
var
s1, s2 : TSingleton;
begin
s1 := TSingleton.Create;
s2 := TSingleton.Create;
//do
something with s1 and s2 here
s2.Free;
s1.Free;
end;
(I've left out the try...finally blocks and other safeguards for simplicity's
sake.)
The goal is to make the TSingleton class behave in such a way that both s1 and
s2 refer to the same object. Here's what we have todo
:
Instantiate the object the first time Create is called (when s1 is created
above)
Ensure that when another Create is executed (s2 above), the existing object
is reused instead of another one created
Avoid destroying the object when it's not the last reference that is destroyed
(when s2 is freed)
Destroy the instance when the last reference is destroyed (when s1 is freed
above)
Is there a way to override the creation and destruction of a new object in
Delphi? There sure is. In the TObject class (the mother of all objects {pun
intended}), there are two methods we can use:
class function NewInstance: TObject;
virtual;
procedure FreeInstance;
virtual;
NewInstance is responsible for allocating memory to hold a new instance of the
class, and FreeInstance is responsible for freeing that memory when the class
is through with it.
These methods control what happens when the object is created and when the
object is destroyed. If we overwrite this code, we can alter the default
behavior to work the say a singleton class requires. Nothing to it.
Tracking instances is a little trickier. We must:
Keep track of each existing instance of our class
Keep track of how many references there are to this instance
Create a new object only when no instance exists
Destroy the object when the last reference is removed
To keep track of an existing instance, we will use a global variable. Actually,
the variable will ultimately be declared inside the Implementation part of a
unit so it won't be a true global variable. But the scope must be sufficient
to track all the Create and Free calls. We'll call the variable Instance so
we know what it refers to.
As for keeping track of how many references exist, we need another variable.
We can it inside the class or make it a sort-of global like Instance. I'll opt
for the latter way butdo
what you feel is best. I'll name this variable
Ref_Count.
We now have two variables:
var
Instance : TSingleton = nil;
Ref_Count : Integer = 0;
I initialize the variables so that initially theydo
n't contain any garbage.
I know that the compilerdo
es this automatically, so this is just a readability
issue.
We'll need to declare the TSingleton class above the variable block, and if
you take a look at the example files that you cando
wnload at the end of this
article you'll see that I've put the declaration in the interface part of the
unit so that it's visible outside of it.
Here's the declaration of the TSingleton class:
type
TSingleton = class
public
class function NewInstance: TObject;
override;
procedure FreeInstance;
override;
class function RefCount: Integer;
end;
I added the RefCount function so that we can see that it actually works, and
it's often handy to be able to read how many references to the object exist.
It's not required, however, so youdo
n't have to add it to your singleton
classes if youdo
n't need it.
Ok, now for the implementation of the three methods:
procedure TSingleton.FreeInstance;
begin
Dec( Ref_Count );
if ( Ref_Count = 0 ) then
begin
Instance := nil;
// Destroy private variables here
inherited FreeInstance;
end;
end;

class function TSingleton.NewInstance: TObject;
begin
if ( not Assigned( Instance ) ) then
begin
Instance := inherited NewInstance;
// Initialize private variables here, like this:
// TSingleton(Result).Variable := Value;
end;
Result := Instance
Inc( Ref_Count );
end;

class function TSingleton.RefCount: Integer;
begin
Result := Ref_Count;
end;
And that's it!
When you call TSingleton's constructor, a call is placed to the NewInstance
method declared in TObject. This method allocates memory to hold the new
object and returns it to the constructor. The constructor uses that memory
and eventually returns a pointer to the memory to the code that called the
constructor. This pointer is usually stored in a variable while the object
is in use.
I have overridden the NewInstance method so it will allocate the memory only
if no instance of the class exists. If there is an existing instance, the
function simply returns that instance to the constructor so it will be reused.
If we call the constructor three times, an object is created only the first
time. The other two calls simply reuse the first object. The reference count
variable let us know that we have three references to the single instance.
When the program calls the destructor, a call to FreeInstance is placed to
free the memory allocated in the constructor. This method, too, is overridden
so that the object is destroyed only when the last reference is removed.
If you intend to use a singleton in a multithreaded program, treat the object
as you would any variable you share between threads. Because that's just what
youdo
: share it between the threads. So you must take special care when
changing data.
Simplicity itself
As you can see, creating a singleton classdo
esn't require much effort, just
the right knowledge and a few lines of code. My code works fine in Delphi 5.
The technique will probably work fine with older versions of Delphi, but I
haven't tested it so Ido
n't make any guarantees.
Before I give you the file to play with, let me give you a few words of
warning.
Don't descend from a singleton class. The reason for that is that there is
only one instance and reference count variable. If you derive two classes from
TSingleton, only one object will be created. The other class will reuse that
object, which will be an instance of a different class.do
n't godo
wn that
road!
You can't make singleton components for the simple reason of ownership. A
component is owned by a form and one component can't be owned by several other
components.
Remember that the constructor and destructor get called for each new reference
as they are created and destroyed.do
n't initialize private variables in the
constructor anddo
n't free them in the destructor. Instead, build that code
into the NewInstance and the FreeInstance methods, as shown in the comments.
The order of adjusting Ref_Count in the two methods in conjunction with the
rest of the code in those two methods is critical. It has todo
with proper
creation and destruction when something goes wrong. If your initialization
code raises an exception, the order ofdo
ing things like shown above will
make sure that the class is destroyed properly. Alter this code at your peril!

The file that you cando
wnload is just a copy of the unit that declares the
singleton class described in this article.
Link to file: CodeCentral entry 15083.
I'm sure you can find a few places where a singleton class comes in handy and
now you have the tools to create your own! If you want to get in touch with me,
my email is lasse@cintra.no.
 
Pattern: Singleton
Definition
"Ensure a class has only one instance, and provide a global point of access
to it."

This is one of the easiest patterns to implement.
Applications in Delphi
There are several examples of this sort of class in the Delphi VCL, such as
TApplication, TScreen or TClipboard. The pattern is useful whenever you want
a single global object in your application. Other uses might include a global
exception handler, application security, or a single point of interface to
another application.
Implementation Example
To implement a class of this type, override the constructor and destructor
of the class to refer to a global (interface) variable of the class.

Abort the constructor if the variable is assigned, otherwise create the
instance and assign the variable.
In the destructor, clear the variable if it refers to the instance being
destroyed.

Note: To make the creation and destruction of the single instance automatic,
include its creation in the initialization section of the unit. To destroy
the instance, include its destruction in an ExitProc (Delphi 1) or in the
finalization section of the unit (Delphi 2).

The following Delphi 1 example illustrates two singleton classes, one derived
from TComponent and another derived from TObject.


unit Singletn;

interface

uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs;

type
TCSingleton = class(TComponent)
public
constructor Create(AOwner: TComponent);
override;
destructor Destroy;
override;
end;


TOSingleton = class(TObject)
public
constructor Create;
destructor Destroy;
override;
end;


var
Global_CSingleton: TCSingleton;
Global_OSingleton: TOSingleton;

procedure Register;

implementation

procedure Register;
begin

RegisterComponents('Design Patterns', [TCSingleton]);
end;


{ TCSingleton }

constructor TCSingleton.Create(AOwner: TComponent);
begin

if Global_CSingleton <> nil then

{NB could show a message or raise a different exception here}
Abort
else
begin

inherited Create(AOwner);
Global_CSingleton := Self;
end;

end;


destructor TCSingleton.Destroy;
begin

if Global_CSingleton = Self then

Global_CSingleton := nil;
inherited Destroy;
end;


{ TOSingleton }

constructor TOSingleton.Create;
begin

if Global_OSingleton <> nil then

{NB could show a message or raise a different exception here}
Abort
else

Global_OSingleton := Self;
end;


destructor TOSingleton.Destroy;
begin

if Global_OSingleton = Self then

Global_OSingleton := nil;
inherited Destroy;
end;


procedure FreeGlobalObjects;
far;
begin

if Global_CSingleton <> nil then

Global_CSingleton.Free;
if Global_OSingleton <> nil then

Global_OSingleton.Free;
end;


begin

AddExitProc(FreeGlobalObjects);
end.

 
Singleton模式
起源
Delphi的SINGLETON模式是在SINGLETON的基础上进行了扩展。更多SINGLETON模式的资料
请参阅 《设计模式84页》
目的
保证一个类仅有一个实例,并提供一个访问它的全局访问点,一个相对简单的应用型设计
模式
动机
此模式最重要的是保证大量类正确的访问单个实例。尽管一台个系统可能有多台打印机,
但系统只允许有且只有一个打印缓存。同样比如:一个系统只有一个文件系统、一个窗体
管理系统。对于Delphi的VCL来说:大家天天接触的Tapplication,Tscreen,Tclipboard
都是。此模式更好的是使你可以在任何时候为你的应用程序提供一个全局对象。其它的用
途:可以、提供一些全局的异常句柄,安全控制,为跨进程提供单一的访问点。
怎样保持一个类只有一个实例并且建立好的访问性能?一个全局变量保证了实例的可访问
性,但还没有保证多个实例的同时存大的可能。
一个好的解决方案:建立类自身来负责保持自身一个实例的机制。类的第一个实例能保存
不会再有类的实例被创建(在创建类的新实例时请求被中段)。并提供一个访问类的方法。
这就是我们的singleton模式,典型的应用是建立服务型的的类。

应用
假设有一个用于显示时间进度低级服务类Tprogressor。类包括两个典型的方法:
StartProgress, EndProgress, Abort 和一些常用的属性如:Progress, Aborted其它。

下面代码是Tprogressor类的接口部份
type
TProgressor = class (TObject)
private
FProgress: Integer;
protected
procedure SetProgress(Value: Integer);
public
&amp;raquo;
procedure StartProgress;
&amp;raquo;
property Progress: Integer read FProgress write SetProgress;
end;


下面的代码是应用了singleton模式后的类的的接口部份。
type
TProgressor = class (TObject)
private
FProgress: Integer;
protected
constructor CreateInstance;
class function AccessInstance(Request: Integer): TProgressor;
procedure SetProgress(Value: Integer);
public
constructor Create;
destructor Destroy;
override;
class function Instance: TProgressor;
class procedure ReleaseInstance;
&amp;raquo;
procedure StartProgress;
&amp;raquo;
property Progress: Integer read FProgress write SetProgress;
end;


类接口部份的介绍:
· 方法class function Instance用于访问类的单件的实例。此方法一般在第一时
间访问,类的实例被创建。
· 类构造器被重载,如果你尝试着不通过instance方法来构建一个新的实例将会
抛出一个异常。
· 可通过调用ReleaseInstance来清除单件类存在的实例。一般在你要清除部件时
调节器用些方法。在Delphi 1 exit过程中调用,在Delphi 2/3/4/5/6中在单元
finalization中调用。不要访问TProgressor.Instance.Free来清除实例,

现在让我们来看看singleton模式实现。
constructor TProgressor.Create;
begin

inherited Create;
raise Exception.CreateFmt('Access class %s through Instance only',
[ClassName]);
end;


constructor TProgressor.CreateInstance;
begin

inherited Create;
end;


destructor TProgressor.Destroy;
begin

if AccessInstance(0) = Self then
AccessInstance(2);
inherited Destroy;
end;


class function TProgressor.AccessInstance(Request: Integer): TProgressor;
const FInstance: TProgressor = nil;
begin

case Request of
0 : ;
1 : if not Assigned(FInstance) then
FInstance := CreateInstance;
2 : FInstance := nil;
else

raise Exception.CreateFmt('Illegal request %d in AccessInstance',
[Request]);
end;

Result := FInstance;
end;


class function TProgressor.Instance: TProgressor;
begin

Result := AccessInstance(1);
end;


class procedure TProgressor.ReleaseInstance;
begin

AccessInstance(0).Free;
end;


procedure TProgressor.SetProgress(Value: Integer);
begin

&amp;raquo;加入实现代码
end;


procedure TProgressor.StartProgress;
begin

&amp;raquo;加入实现代码
end;


此模式的密秘在AccessInstance方法,AccessInstance方法使用了常量来存放实例。
由于Delphi不支持静态类字段的原因,你必需使用此方法。AccessInstance根据请求的
参数来确认不同的返回方式,返回当前实例(Request = 0),创建一个实例
(Request = 1),重置实例(Request = 2)。Delphi 2/3/4/5/6中,你必须将编译器设置
$J+来支持形式常量(参见Delphi在线帮助)
将CreateInstance设成保护方法(protected),我们确保了其它类不能调用类的构造器。
但由于构造函数本身为虚函数的原因,它可以被后代重载。例中,当第一次调用类实例时
将确认类的具体类型。
唯一需要你手动添加的代码是:在单元的initialization或finalization加入清除代码。

下面是应用实例(Delphi1.0)

unit ProgressorTest;

...
...

implementation

...
...

procedure ShutDown;
far;
begin

&amp;raquo;
TProgressor.ReleaseInstance;
end;


initialization
MMWIN:START INITIALIZATION
&amp;raquo;
AddExitProc(ShutDown);
end.


具体操作Tprogressor的代码:

procedure TSomeClass.DoSomething;
var I: Integer;
begin

&amp;raquo;
TProgressor.Instance.StartProgress;
&amp;raquo;
for I := 0 to 100do

&amp;raquo;
begin

&amp;raquo;
TProgressor.Instance.Progress := I;
&amp;raquo;
..{ 完成其它的工作}
&amp;raquo;
end;

&amp;raquo;
TProgressor.Instance.EndProgress;
end;

 
觉得第一个实现方法比较好,因为他没有把我们常用的create,destroy方法隐藏起来,只是
在create,destroy会自动调用的newinstance,freeinstance方法里实现,这样把这个类交给
其他人用的时候不需要改变接口。
vrgl,多谢你找了这些好文章,我现在是刚刚开始学设计模式,以后多多指点。
 
accept answers
 
class function TSingleton.NewInstance: TObject;
begin
if ( not Assigned( Instance ) ) then
begin
Instance := inherited NewInstance;
// Initialize private variables here, like this:
// TSingleton(Result).Variable := Value;
end;
Result := Instance
Inc( Ref_Count );
end;
上面的 Instance := inherited NewInstance;有问题啊,好像不可以把父类对象赋给子类对象
 
Tsingleton和Tobject的NewInstance方法一样,
而且都返回Tobject类型的结果,
没什么不对的呀,关于多态中的描述见下面:
多态是通过把子类对象赋给父类对象,
让父类对象具有子类对象的方法,例子表达式如下:
TParent=class(TObject)
procedure Draw;virtual;
end;

TChild=class(TParent)
procedure Draw;override;
procedure ShowHierarchy;virtual;
end;

Parent:=Child;
Parent.Draw;//执行子类的Draw;
 
后退
顶部