Singelton and event

Posted by goo on 05-Feb-2017 07:46

Is it possible to do this class as a singelton? If so, how can I call the method onTick from the timer?

//Geir Otto

Posted by Laura Stern on 06-Feb-2017 08:14

I tried running the latest code you sent, with the NEW in the constructor.  It works fine for me - THE FIRST TIME in the sense that the instance property does return a value.  But remember - the static constructor will only run once within a session.  If for any reason the class gets garbage collected, it will never run again and the instance value will be gone.  (Try it from the Procedure Editor for example, and run it twice.  It will run the first time and not the 2nd.)

So I would say the doc is not exactly wrong, but it should probably be changed.  You should have the NEW in the Get body of the instance property as your last post shows.  Also, as I said before - remember that you must reset the Timer1:TickInterval from the TickInterval Set property or it will never change from its initial value.

And I don't think I'm qualified to explain why you would use the singleton model vs just using static properties for everything.  

All Replies

Posted by goo on 05-Feb-2017 07:47

Posted by goo on 05-Feb-2017 15:32

Sorry, it seems like my code was not added. Here it comes: Why would not this code run? What am I doing wrong when it comes to singelton?

This is the code that runs it:

def var oTimer as class JBoxTimer no-undo.

oTimer = JBoxTimer:Instance.

oTimer:TickInterval = 40.

The class fails to load..... why?

***********************************************************************

this is the class...

**********************************************************************

USING Progress.Lang.*.

BLOCK-LEVEL ON ERROR UNDO, THROW.

CLASS JBoxTimer:

 define public static property instance as class JBoxTimer no-undo get. private set.

 define public property TickInterval as int initial 60 no-undo get. set.

CONSTRUCTOR private JBoxTimer ():

 end constructor.

 constructor static JBoxTimer():

   DEFINE VARIABLE components AS CLASS System.ComponentModel.Container NO-UNDO.

   DEF VAR timer1             AS class System.Windows.Forms.Timer NO-UNDO.

   components        = NEW System.ComponentModel.Container().

   timer1            = NEW System.Windows.Forms.Timer(components).

   timer1:Interval   = int(JBoxTimer:Instance:TickInterval) * 1000.

   timer1:Enabled    = TRUE.

 Timer1:Tick:Subscribe(JBoxTimer:Instance:OnTick).

END CONSTRUCTOR.

/*------------------------------------------------------------------------------

Purpose:

Notes:

------------------------------------------------------------------------------*/

METHOD private VOID OnTick(input sender as System.Object, input e as System.EventArgs  ):

/*    DEFINE INPUT PARAMETER sender AS System.Object.*/

/*    DEFINE INPUT PARAMETER  e AS System.EventArgs.*/

   MESSAGE 'OnTIck......'

   VIEW-AS ALERT-BOX.

RETURN.

END METHOD.

 METHOD private VOID Dummy(input iDummy as char):

   RETURN.

 END METHOD.

END CLASS.

Posted by Laura Stern on 05-Feb-2017 19:10

As far as I can see you are never initializing the instance property.  It's not enough to define it.  You have to set it to an instance of the class.  I.e, Normally you would add a code body for the Get and New an instance of the class:  

Define public static property instance as JBoxTimer

Get:

  Instance = new JBocTimer().

End.

I believe you can call the private constructor from a static property of the class.

However, in this case, I don't know why you even need an instance.  Actually, when you access the Instance property , even though it returns ?,  the static constructor will run.  Then even if you created an instance, you are then setting the TickInterval and not doing anything with it.  The code that used it has run already.

Maybe instead you want to set a static TickInterval property and move all that static constructor code into the property's Get body?   You don't need a singleton.  It would all just be static.

Posted by goo on 06-Feb-2017 01:06

Laura, from the OpenEdge document:

documentation.progress.com/.../index.html

I have done it that way earlier, but I was told that you could do it in a differtent way, so I followed the documentation. I am not familiar with this way of coding, and to be honost, I am not sure what the diff is betveen static and singleton.

What I hoped to do, was to make ONE class that I could start and that would handle all my needs for a Timer. Till I fully understand this, I will drop the build of class and just use the code within each of the parts that needs a timer.

Posted by goo on 06-Feb-2017 01:13

Ups, Laura, I see now that I have forgotten the most importen part of the code :-// I forgot the NEW part in start of the static constructor.... but still not working as hoped...

 constructor static JBoxTimer():

   JBoxTimer:instance = new JBoxTimer().

   DEFINE VARIABLE components AS CLASS System.ComponentModel.Container NO-UNDO.

   DEF VAR timer1             AS class System.Windows.Forms.Timer NO-UNDO.

   components        = NEW System.ComponentModel.Container().

   timer1            = NEW System.Windows.Forms.Timer(components).

   timer1:Interval   = int(JBoxTimer:Instance:TickInterval) * 1000.

   timer1:Enabled    = TRUE.

 Timer1:Tick:Subscribe(JBoxTimer:Instance:OnTick).

END CONSTRUCTOR.

Posted by goo on 06-Feb-2017 01:21

By returning to this:

 define public static property instance as class JBoxTimer no-undo

 get():

   if instance = ? then instance = new JBoxTimer().

   return instance.    

 end get.

It seems to work.... Is the documentation wrong?

Posted by Laura Stern on 06-Feb-2017 08:14

I tried running the latest code you sent, with the NEW in the constructor.  It works fine for me - THE FIRST TIME in the sense that the instance property does return a value.  But remember - the static constructor will only run once within a session.  If for any reason the class gets garbage collected, it will never run again and the instance value will be gone.  (Try it from the Procedure Editor for example, and run it twice.  It will run the first time and not the 2nd.)

So I would say the doc is not exactly wrong, but it should probably be changed.  You should have the NEW in the Get body of the instance property as your last post shows.  Also, as I said before - remember that you must reset the Timer1:TickInterval from the TickInterval Set property or it will never change from its initial value.

And I don't think I'm qualified to explain why you would use the singleton model vs just using static properties for everything.  

Posted by Tim Kuehn on 06-Feb-2017 08:30

[quote user="Laura Stern"] And I don't think I'm qualified to explain why you would use the singleton model vs just using static properties for everything.   [/quote] A couple observations -

  1. Statics by definition never unload from the ABL session once they're loaded while singletons can be deleted and re-instantiated. This can be a big issue when continuous-deployment shops where an object instance needs to be re-instantiated for some reason.
  2. Statics also violate encapsulation as they're effectively scoped to the entire session. This makes developing test jigs and overall testing more problematic than it would be otherwise. 

I used statics a fair bit when I was first starting in OO, now I avoid them as much as possible. I might be able to justify a single static (ala SESSION) and hang dynamic instances off that static's properties - that would be about it. 

Posted by Laura Stern on 06-Feb-2017 08:33

And P.S: In the instance property code, I would not check for ?, I would use if VALID-OBJECT(...)

Posted by goo on 06-Feb-2017 09:27

Define public static property instance as JBoxTimer

Get:

 Instance = new JBocTimer().

End.

VS.

 DEFINE PUBLIC STATIC PROPERTY Instance AS CLASS JBoxTimer NO-UNDO

   GET.

   PRIVATE SET.

with the   Instance = new JBocTimer() in the constructor.

What is the point of using the second property if you can only use it once? And what would you do if you needed it in another part of the application? It seems like the first way of using property is the way I need to use it, since I want to call it several times, but just have it instansiated once....

The only problem I know have with the code, is that it will not fire the onTick event :-) it is not that easy to start using the new stuff :-) but fun!!

Posted by Laura Stern on 06-Feb-2017 09:32

Sorry.  You've lost me completely.  

Obviously, you don't want to create a new instance EVERY time someone asks for one.  You need to check if you have one already - as you showed earlier, but:

if NOT VALID-OBJECT(instance) then

   instance = new JBoxTimer().

You said: " What is the point of using the second property".  What property is that?

Of course the onTick even will not fire with the exact code you showed.  The program is over before the time interval expires.  You need to be in some kind of a I/O blocking - like a WAIT-FOR.  With that, I do see the timer tick.

Posted by goo on 06-Feb-2017 09:43

In my test I added a wait-for, but I will do a second check. Thanks :-)

The second property was the one that did not have the instance = new JBoxTimer() in the GET part.... That was taken from the documentation that I found for singeltons. I was trying it, but as you said, if I lost it, I could not call it more than once...

Posted by Laura Stern on 06-Feb-2017 10:23

Still lost.  You can call the instance property more than once.  All I said was that the static constructor will only run once.  Here's my code:

def var oTimer as class JBoxTimer no-undo.

oTimer = JBoxTimer:Instance.

oTimer:TickInterval = 6.

WAIT-FOR "CLOSE" OF THIS-PROCEDURE.

JBoxTimer.cls:

CLASS JBoxTimer:

   DEFINE PRIVATE VAR timer1 AS class System.Windows.Forms.Timer NO-UNDO.

   DEFINE PUBLIC STATIC PROPERTY Instance AS CLASS JBoxTimer NO-UNDO

   GET:

       IF NOT VALID-OBJECT(Instance) THEN DO:

           Instance = NEW JBoxTimer( ).

           Instance:InitializeTimer( ).

       END.

       RETURN Instance.

   END.

   PRIVATE SET.

   DEFINE PUBLIC PROPERTY TickInterval AS INT INITIAL 3 NO-UNDO

   GET.

   SET (interv AS INT):

       timer1:INTERVAL = interv * 1000.

   END.  

   CONSTRUCTOR PRIVATE JBoxTimer( ):

   /* Make the constructor private so it cannot be called from outside of

      the class  */

   END CONSTRUCTOR.

   METHOD PRIVATE VOID initializeTimer():

       DEFINE VAR components AS CLASS System.ComponentModel.Container NO-UNDO.

       components = NEW System.ComponentModel.Container().

       timer1 = NEW System.Windows.Forms.Timer(components).

       timer1:INTERVAL = TickInterval * 1000.

       timer1:ENABLED = TRUE.

       Timer1:Tick:Subscribe(OnTick).                

   END.

   METHOD private VOID OnTick(input sender as System.Object, input e as System.EventArgs  ):

       MESSAGE 'OnTIck......' VIEW-AS ALERT-BOX.

   END METHOD.

END.

Posted by goo on 06-Feb-2017 14:00

Thanks Laura, now I understand more than when I started :-) It seems that I have been trying to do a mix of two things that does not mix that good. Also I didn't understand the use between static and singelton when looking at the example in the documentation. Anyway, this code you now gave me, did what I wanted, so I am happy :-) thanks a lot...

This thread is closed