Singleton design pattern

121 views
Skip to first unread message

XemonerdX

unread,
May 7, 2008, 7:14:23 AM5/7/08
to intersystems.public.cache
Hello,

I am fairly new to Cache ObjectScript and I've been trying out various
things. Right now I am trying to create a class that acts as a
Singleton. That is, there can be only one class instance, any attempts
to create another instance will merely return the already created
instance. Is this possible and if so, how? I've tried various things
but my main problem sofar is that I cannot 'hide' the %New classmethod
(make it 'private' as used in some other languages) and thus prevent
direct instantiation.

Thanxxx,

Edwin

sukesh_hoogan

unread,
May 7, 2008, 2:47:14 PM5/7/08
to intersystems...@info2.kinich.com
Edwin

One of the solutions

1) Have a look at

%OnNew(initvalue As %CacheString) method
of the %Library.Persistent class

initvalue is an optional argument,

2) You will have to override the method in your class to prevent more
than one/whatevernumber instance(s) being created.

In the studio from the menu

Class->Override and select the method to override.

The code in this method should be like

set vstat=1
if ..%ExistsId(N) {
s vstat=0
}
quit vstat


---------

3) Replace N with maximum number of instances allowed.

If somebody tries to create a new instance greater than N,
The %New method will return a null object reference (OREF)

HTH

Regards
Sukesh Hoogan
Bombay, India
[Enterprise Resource Planning & Business Intelligence]

http://sukeshhoogan.blogspot.com

Eric

unread,
May 7, 2008, 7:27:55 PM5/7/08
to intersystems.public.cache

Sukesh,

With a singleton, calling new must return the "one" instance, so where
do you store your instance? Especially with a registered object.
Static properties are not supported and workarounds (there are many fi
we want to be inventive) are all unsatisfaying.

If you know of an elegant solution, I would like to know:-)

Eric

sukesh_hoogan

unread,
May 8, 2008, 12:37:56 AM5/8/08
to intersystems...@info2.kinich.com
Eric

I am not sure whether you and Edwin (he says he is fairly new to Caché)
are the same person or are from the same organisation.

In any case, the answer to your question.

The one instance would indeed be stored in a persistent class, which
extends the RegisteredObject class. And you have to override %OnNew
method in the persistent class.

Regards
Sukesh Hoogan
Bombay, India
[Enterprise Resource Planning & Business Intelligence]

http://sukeshhoogan.blogspot.com

XemonerdX

unread,
May 8, 2008, 3:23:02 AM5/8/08
to intersystems.public.cache
Sukesh,

Thank you for your answer. I didn't know that when %OnNew returns an
error the instance created with %New will be destroyed. That seems to
be rather helpful.

Thank you,

Edwin

PS. Eric and I aren't colleagues AFAIK :)

Eric

unread,
May 8, 2008, 6:31:39 AM5/8/08
to intersystems.public.cache

Edwin,

Sorry if I jumped in your thread but I did find the question
interesting. Seems it is still required to persist the class to
acheive this. So out of reach for registered classes.

Thanks Sukesh
> > > Edwin- Hide quoted text -
>
> - Show quoted text -

Gepi

unread,
May 8, 2008, 7:25:18 AM5/8/08
to intersystems...@info2.kinich.com
Here is my solution:
Create this class:
 
Include %systemInclude
IncludeGenerator %systemInclude
Class Utile.SingletonClass Extends %RegisteredObject 
{

    ClassMethod GetNew(initvalue As %String) [ CodeMode = generator, Final, ProcedureBlock = 1, ServerOnly = 0 ]
    {
        s %code=0
        $$$GENERATE(" set objRef=$g(%Singleton("""_%class_"""),$$$NULLOREF)")
        $$$GENERATE(" if objRef=$$$NULLOREF")
        $$$GENERATE(" {s objRef=..%New(.initvalue)")
        $$$GENERATE(" s %Singleton("""_%class_""")= objRef")
        $$$GENERATE(" }")
        $$$GENERATE(" q objRef")
        Quit $$$OK
    }
}
 
Now, when you need a singleton class extend the Utile.Singleton:
 
 Class Utile.TestSingleton Extends SingletonClass
{

    Property TestProp As %String;

    ClassMethod TestSingleTon()
    {
        w "No singleton:",!
        set obj1=##class(TestSingleton).%New()
        set obj2=##class(TestSingleton).%New()
        w
        w !
        kill
        w "Singleton:",!
        set obj3=##class(TestSingleton).GetNew()
        set obj3.TestProp=2
        set obj4=##class(TestSingleton).GetNew()
        w
        w !,"TEST "_obj4.TestProp,!
    }
}
 
To generate a singleton instance, you have to use GetNew() method.

Eric

unread,
May 8, 2008, 12:42:05 PM5/8/08
to intersystems.public.cache
Gepi,

I agree this works, but it is part of those unsatisfying workarounds.
Encapsulation is broken by using a %variable, so it is structured
programming but not OO.

Maybe the best solution would be to persist it (as Sukesh suggested)
but modify the storage so that it end up in CacheTemp. I just tested
it and it works!

Eric

Herman Slagman

unread,
May 8, 2008, 3:00:59 PM5/8/08
to intersystems...@info2.kinich.com

"Eric" <Eric....@gmail.com> schreef in bericht
news:d62dce3a-dc5e-4f00...@25g2000hsx.googlegroups.com...


> Maybe the best solution would be to persist it (as Sukesh suggested)
> but modify the storage so that it end up in CacheTemp. I just tested
> it and it works!

Eric,

See below an implementation for %RegisteredObject.

HTH

Herman


Class PlacidSky.Singleton Extends %RegisteredObject
{
Method %OnNew(initvalue As %CacheString) As %Status [ Private,
ProcedureBlock = 1, ServerOnly = 1 ] {
Set Obj="",Found=0
For {
Set Obj=$zobjnext(Obj)
If Obj="" Quit
If Obj=##this Continue
If Obj.%ClassName(1)=..%ClassName(1) {
Set Found=1
Quit
}
}
If Found {
Quit $$$ERROR($$$GeneralError,..%ClassName(1)_" already
instantiated")
}
Quit $$$OK
}
ClassMethod Get() as PlacidSky.Singleton {
Set Obj="",Found=0
For {
Set Obj=$zobjnext(Obj)
If Obj="" Quit
If Obj.%ClassName(1)=..%ClassName(1) {
Set Found=1
Quit
}
}
If Found {
Quit Obj
}
Quit ..%New()
}
}


In Terminal:

LSP200>s o1=##class(PlacidSky.Singleton).%New()

LSP200>w

o1=<OBJECT REFERENCE>[1...@PlacidSky.Singleton]
LSP200>s o2=##class(PlacidSky.Singleton).%New()

LSP200>w

%objlasterror="0 _ ? *PlacidSky.Singleton already
instantiated(%OnNew+11^PlacidSky.Singleton.3:LSP200"
o1=<OBJECT REFERENCE>[1...@PlacidSky.Singleton]
o2=""

LSP200>s o3=##class(PlacidSky.Singleton).Get()

LSP200>w

%objlasterror="0 _ ? *PlacidSky.Singleton already
instantiated(%OnNew+11^PlacidSky.Singleton.3:LSP200"
o1=<OBJECT REFERENCE>[1...@PlacidSky.Singleton]
o2=""
o3=<OBJECT REFERENCE>[1...@PlacidSky.Singleton]


Eric

unread,
May 8, 2008, 4:56:48 PM5/8/08
to intersystems.public.cache
Herman,

I am not familiar with $zobjnext and it is not documented but looks
handy for some things. Am I understanding correctly that you are
iterating through all objects in the current process?

Thanks

Eric

On May 8, 3:00 pm, "Herman Slagman" <herman_slagman@placid-dash-sky-
dot-org> wrote:
> "Eric" <Eric.Ane...@gmail.com> schreef in berichtnews:d62dce3a-dc5e-4f00...@25g2000hsx.googlegroups.com...

Herman Slagman

unread,
May 8, 2008, 6:53:05 PM5/8/08
to intersystems...@info2.kinich.com

"Eric" <Eric....@gmail.com> schreef in bericht
news:f70c6ea4-4c70-4562...@34g2000hsh.googlegroups.com...

Eric,

> not documented

Well, documentation isn't one of ISC's strongest points ;-)
Normally I wouldn't use undocumented stuff, but after working with Cache for
more then ten years, you get a feeling for what's really undocumented and
what they forgot to document.
Ofcourse if they change or remove the implementation of $zobjnext (highly
unlikely), your code would break.
But over the years I've seen documented features break in new releases too.
:-S

The documented ones are: $zobjproperty, $zobjmethod and $zobjclassmethod.

There's also a $zobjclass(object), which is about ten times faster then
object.%ClassName(1).

I've also seen $zobjval, $zobjmod, $zobjmods and $zobjexport, don't know
what they do.

> Am I understanding correctly that you are
> iterating through all objects in the current process?

Yep, all instantiated objects in memory.

Herman


sukesh_hoogan

unread,
May 9, 2008, 1:50:05 AM5/9/08
to intersystems...@info2.kinich.com
Herman & Eric


A) The documentation has notes on only three

$zobjclassmethod
$zobjmethod
$zobjproperty

http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=AZOBJ

The documentation is silent on

$zobjnew(ClassName As %String)
$zobjval
$zobjexport
$zobjmods

$zobjnew(classname) is equivalent to %New()

I will post about $zobjval later in the day.

Regards
Sukesh Hoogan
Bombay, India
[Enterprise Resource Planning & Business Intelligence]

http://sukeshhoogan.blogspot.com

Gepi

unread,
May 9, 2008, 3:45:44 AM5/9/08
to intersystems...@info2.kinich.com
The mapping idea is good, here is a new implementation but without needing
to map the class to a temporary global:

Here is the SingletonClass (can't be inherited)
Class Utile.SingletonClass Extends %Persistent [ NoExtent]
{

Property InternalId As %String;

Index MyId On InternalId [ IdKey, PrimaryKey, Unique ];

Property TestProp As %String [ Transient ];

ClassMethod Test()
{
set obj1=##class(Utile.SingletonClass).%OpenId(1)
set obj1.TestProp=3
set obj2=##class(Utile.SingletonClass).%OpenId(1)

set obj3=##class(Utile.SingletonClass).%OpenId(2)
set obj3.TestProp=2
set obj4=##class(Utile.SingletonClass).%OpenId(2)

w
w !,"TEST id1: "_obj2.TestProp_" id2: "_obj4.TestProp,!
}

Method %LoadData(id As %String) As %Status [ Private, ProcedureBlock =
1, ServerOnly = 1 ]
{
QUIT $$$OK
}

<Storage name="MyStorage">
<SequenceNumber>6</SequenceNumber>
<Type>%CustomStorage</Type>
</Storage>
}

You will have to:
1. maintain the InternalId prop, the MyId index and the MyStorage
declaration;
2. All the properties has to be declared with attribute [Transient];

I have test this on CACHE 2007.1 and is working.
It has one limitation. The instance can't be accessed form different
processes it works only on one process (connection).

to see the result call the Test method in Terminal.


"Eric" <Eric....@gmail.com> wrote in message
news:d62dce3a-dc5e-4f00...@25g2000hsx.googlegroups.com...

George James

unread,
May 9, 2008, 4:00:15 AM5/9/08
to intersystems...@info2.kinich.com
Herman
There's quite a few undocumented functions listed here:
http://cachewiki.org/index.php/Undocumented_Syntax

Feel free to update/annotate this list as appropriate.

Regards
George

George James Software
www.georgejames.com

Eric

unread,
May 9, 2008, 1:24:24 PM5/9/08
to intersystems.public.cache
Thanks George, that's a good reference!

On May 9, 4:00 am, George James <Geor...@georgejames.com> wrote:
> Herman
> There's quite a few undocumented functions listed here:http://cachewiki.org/index.php/Undocumented_Syntax
>
> Feel free to update/annotate this list as appropriate.
>
> Regards
> George
>
> George James Softwarewww.georgejames.com
>
>
>
> -----Original Message-----
> From: Herman Slagman [mailto:herman_slagman@placid-dash-sky-dot-org]
>
> Posted At: 08 May 2008 23:53
> Posted To: Caché Newsgroup
> Conversation: Singleton design pattern
> Subject: Re: Singleton design pattern
>
> "Eric" <Eric.Ane...@gmail.com> schreef in berichtnews:f70c6ea4-4c70-4562...@34g2000hsh.googlegroups.com...
>
> Eric,
>
> > not documented
>
> Well, documentation isn't one of ISC's strongest points ;-) Normally I
> wouldn't use undocumented stuff, but after working with Cache for more
> then ten years, you get a feeling for what's really undocumented and
> what they forgot to document.
> Ofcourse if they change or remove the implementation of $zobjnext
> (highly unlikely), your code would break.
> But over the years I've seen documented features break in new releases
> too.
> :-S
>
> The documented ones are: $zobjproperty, $zobjmethod and
> $zobjclassmethod.
>
> There's also a $zobjclass(object), which is about ten times faster then
> object.%ClassName(1).
>
> I've also seen $zobjval, $zobjmod, $zobjmods and $zobjexport, don't know
> what they do.
>
> > Am I understanding correctly that you are iterating through all
> > objects in the current process?
>
> Yep, all instantiated objects in memory.
>
> Herman- Hide quoted text -
Reply all
Reply to author
Forward
0 new messages