Singleton vs Static - Forum - OpenEdge Development - Progress Community
 Forum

Singleton vs Static

  • Refresh an old man's memory:

    If I have a class comprised entirely of static methods and properties, is that any different (in theory or practice) to a Singleton class ?

    Julian

  • Singleton is a pattern; static is a language feature.  See http://en.wikipedia.org/wiki/Singleton_pattern

    There are possible picky fine points, but that distinction is the primary one, i..e. statics can be used in a number of ways which have nothing to do with the singleton pattern and the singleton pattern can be implemented in ways that don't use statics, but statics are one way to implement the singleton pattern in sufficiently modern versus of ABL.

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • If I have a class comprised entirely of static methods and properties, is that any different (in theory or practice) to a Singleton class ?

    As a side note on static classes: When they cause a schema lock (because they access the database) you won't be able to disconnect the database at runtime anymore - because the static class cannot be unloaded from memory at all.

    This may be an issue for every kind of ABL session that is directly connected to a database and might need to disconnect databases when a user logs out (think of first steps when adding OO features to a legacy application - before you are able to do a full rearchicture).

  • mikefe wrote:

    As a side note on static classes: When they cause a schema lock (because they access the database) you won't be able to disconnect the database at runtime anymore - because the static class cannot be unloaded from memory at all.

    In other words, if a developer needs to have any kind of reference to a db that needs to be disconnected - make sure that the table references are in classes whose objects can be deleted.

  • In other words, if a developer needs to have any kind of reference to a db that needs to be disconnected - make sure that the table references are in classes whose objects can be deleted.

    Exactly. You native English speakers have so nice words

  • Of course, I would question whether it was a reasonable design to have DB references in a static in the first place.  Something suspicious about that from the start, aside from the possible consequences.

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • I was expecting you to question any kind of comment on OO

    But it still should be allowed to warn people of consequences of

    certain designs. Even if some people wouldn't make those design

    decisions at all.

  • Possibly also vice-versa ... i.e., "oh, I see that could be a problem ... perhaps I should be wondering about what kinds of things are appropriate for statics in case there are other issues I haven't discovered yet".

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • Julian,

    I look at Static classes (whether in ABL, Java, C++ or C#) the same way I do Global Variables in the ABL. In fact, there really isn't a lot of difference. Static classes behave like the -p procedure.

    The rule of thumb about static methods is that anything that they reference should be released from memory at the end of the scope of the method in which they were created (except the return value).

    The rule of thumb about static members is that they are no different to global variables.

    I think the standard Singleton pattern (as defined by the Gang of Four) has one missing optional component that shows the distinction between it and a static class. Over and above the GetInstance method, it should have a ReleaseInstance and RefreshInstance method, all of which are static and take care of getting rid of the class and reinstantiating it. Now you have a class that is globally accessible, for which there is only one instance that can be destroyed and reinstantiated, which clarifies the difference between Singleton and Static.

  • forgive my ignorance, but are you saying that you should never use a static property or method apart from the 3 you described ?

  • No. Not at all.

    What I am saying is that in the Singleton pattern these three should also exist when it is sensible to be able to blow away and restart the Singleton.

    I use static members for things that I need to know globally. There are *very* few of these.

    I use static methods for Factory methods were I'm trying to hide the complexity of instantiating a class.

    It is generally a bad practice to use something statically where it should really have a lifetime. This especially true with languages like C# and Java (and now I assume Progress too) where garbage collection is automatic. Anything that has a long lifetime is going to bloat your memory usage. So be careful about having objects that have reference to them that are static - they live for the duration of the session.

  • Also worth mentioning:

    - Singletons can implement interfaces.

    - Statics cannot

    - Singletons can be passed as parameters... into methods and stored in properties etc.

    - Statics cannot

    As far as I can tell, its ok to use buffers inside static methods. Something like below should work without permanently locking the record?

    Any thoughts?

    ROUTINE-LEVEL ON ERROR UNDO , THROW.


    CLASS CustomerHelper:


      METHOD PUBLIC STATIC VOID UpdateCustomer():

        DEFINE BUFFER bCustomer FOR Customer.

       

        FIND FIRST bCustomer WHERE bCustomer.CustomerNo = 12345 EXCLUSIVE-LOCK.

       

        /* Code to update customer */

       

      END METHOD.


    END CLASS.

  • As far as I can tell, it's ok to use buffers inside static methods.

    I'll let you alone to discuss that with the Dr.

    Something like below should work without permanently locking the record?

    I've not been talking about record locks. They should behave like in persistent procedures and it's all based on record scope and blocks etc..

    It's about the schema lock.

    Try this code (based on your sample code):

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS 
        VIEW-AS ALERT-BOX.

    CustomerHelper:UpdateCustomer()  .

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.

    DISCONNECT sports2000 .

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.

    CustomerHelper:UpdateCustomer()  .

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.

    For the runtime it appears that after the DISCONNECT statement the sports2000 DB is gone, but from the user stats in promon it's clear that the client is not disconnected. And as a matter of fact you still have access to the record in the second call into UpdateCustomer. And connecting a different DB with the same logical DB name should either fail or be silently ignored.

    If you don't call into the static methods (of a class that has DB references), the client is gone from promon after the DISCONNECT statement.

  • Beyond Mike's cautions, let me suggest that the quesion should not be "can I get away with ...", but rather "is there a compelling reason to ...".  Statics have a role, but they also have a price.  Approach using one the same way you approach using a global shared variable, with caution and doubt.  I suspect that a great many uses of statics and singletons are cases of not thinking clearly about how to make something happen and thus taking the easy way out.

    Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice  http://www.cintegrity.com

  • Apologies Mike, I should have read your post a little closer.

    Do PSC know about this one?
    This looks like a bug to me? Or do PSC claim this behavior as 'by design' or 'deliberate'  ?
    From my testing, this problem appears to affect singletons as well. Try this:
    CustomerHelperSingleton.cls
    CLASS CustomerHelperSingleton:

      /* Singleton */
      DEFINE PRIVATE STATIC VARIABLE inst AS CustomerHelperSingleton NO-UNDO.
      DEFINE PUBLIC STATIC PROPERTY Instance AS CustomerHelperSingleton
        GET:
          IF inst = ? THEN
            inst = NEW CustomerHelperSingleton().
          RETURN inst.
        END GET.

     
      METHOD PUBLIC VOID UpdateCustomer():
       
        DEFINE BUFFER bCustomer FOR Customer.
       
        FIND FIRST bCustomer WHERE bCustomer.CustNum = 1 EXCLUSIVE-LOCK.
        bCustomer.Name = STRING(ETIME).
       
        MESSAGE bCustomer.Name VIEW-AS ALERT-BOX.
       
      END METHOD.

    END CLASS.
    TestSingleton.p
    DEFINE VARIABLE helper AS CustomerHelperSingleton NO-UNDO.
    helper = CustomerHelperSingleton:Instance.


    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.

    helper:UpdateCustomer().

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.

    DISCONNECT sports2000.

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.

    helper:UpdateCustomer().

    MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS
        VIEW-AS ALERT-BOX.
    I'm not getting any errors and promon is still showing my connection to sports2000..?
    I'm running this sample from within OpenEdge Architect 10.2A002 on Windows 7.