Singleton vs Static

Posted by jmls on 26-Mar-2010 13:29

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

All Replies

Posted by Thomas Mercer-Hursh on 26-Mar-2010 13:57

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.

Posted by Admin on 26-Mar-2010 15:50

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).

Posted by Tim Kuehn on 26-Mar-2010 15:56

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.

Posted by Admin on 26-Mar-2010 16:05

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

Posted by Thomas Mercer-Hursh on 26-Mar-2010 16:14

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.

Posted by Admin on 26-Mar-2010 16:25

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.

Posted by Thomas Mercer-Hursh on 26-Mar-2010 16:43

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".

Posted by bsgruenba on 26-Mar-2010 17:12

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.

Posted by jmls on 27-Mar-2010 14:07

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

Posted by bsgruenba on 27-Mar-2010 14:16

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.

Posted by cwills on 28-Mar-2010 07:11

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.

Posted by Admin on 28-Mar-2010 07:46

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.

Posted by Thomas Mercer-Hursh on 28-Mar-2010 11:30

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.

Posted by cwills on 28-Mar-2010 19:34

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.

Posted by Thomas Mercer-Hursh on 28-Mar-2010 19:46

Seems like expected behavior to me.  Are you surprised?

Posted by Admin on 28-Mar-2010 23:00

Seems like expected behavior to me.  Are you surprised?

For me this was expected. It's consistent with persistent procedures. As long as a PP is loaded all databases are required. The Disconnect statement is not able to unload the persistent procedures - and they would certainly be in serious trouble when the DB would go away. Same with instances of classes (like your singleton).

The good thing about PPs and instances (and singletons) is: You can unload them prior to disconnecting the DB.

You can't do that with a static class one used anymore - the only chance to disconnect the DB then is to QUIT the client.

Posted by cwills on 28-Mar-2010 23:27

Interestingly enough, the database still remains connected after I explicitly delete the singleton instance and then disconnect (see below).

DEFINE VARIABLE helper AS CustomerHelperSingleton NO-UNDO.

helper = CustomerHelperSingleton:Instance.


helper:UpdateCustomer().


MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS

    VIEW-AS ALERT-BOX.

   

DELETE OBJECT helper NO-ERROR.


PAUSE 10 NO-MESSAGE.


DISCONNECT sports2000.


MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS

    VIEW-AS ALERT-BOX.

Posted by Thomas Mercer-Hursh on 29-Mar-2010 10:57

But, given that you can't actually delete a static, are you surprised?

You might want to experiment with the solution in P159909 from the KB.  I haven't tried it, but it claims to be a singleton which you can instantiate and delete.  Same is true of my old solution here http://www.oehive.org/PseudoSingleton .

What I am not hearing, though, is why you would want to have DB access in a static class.

Posted by Admin on 29-Mar-2010 11:05

What I am not hearing, though, is why you would want to have DB access in a static class.

Some developers are actually free and don't need to justify to people on forums they participate.

Posted by Thomas Mercer-Hursh on 29-Mar-2010 11:25

Huh?  All I am looking for here is a use case.  With a use case, we can examine the question of whether or not this behavior is or is not a problem.  As near as I can tell, it is expected behavior for a static so it is neither a bug nor surprising.  So, the obvious question is, what is the problem?  I.e., what use case makes this behavior surprising or problematic?

Posted by bsgruenba on 29-Mar-2010 16:04

Hey Mike,

I have to be honest and say I'm finding it hard to find a use case where I think that accessing a buffer in a static method is a good idea. Now that doesn't mean that it's not - just that I can't think of a case where I would do it.

Can you explain your answer a little?

Bruce

Posted by cwills on 29-Mar-2010 17:44


But, given that you can't actually delete a static, are you surprised?

You might want to experiment with the solution in P159909 from the KB.  I haven't tried it, but it claims to be a singleton which you can instantiate and delete.

My singleton class above is the same as the example shown in P159909...

Not to worry, I'll do some more testing and figure out whats happening.

Posted by Thomas Mercer-Hursh on 29-Mar-2010 19:00

FWIW, my old version of a singleton doesn't use statics and thus is easily deleted.  I still wouldn't put a DB action in it, though.

Posted by Admin on 29-Mar-2010 23:42

It appears to me that the schema lock is not based on the static members of the class. Looks like they are based on the whole class. Like RCODE-INFO does only have one set of TABLE and DB references and not one for the static and one for the non static members.

Posted by Admin on 29-Mar-2010 23:49

Hey Bruce,

good to have the Gorilla back!

I have to be honest and say I'm finding it hard to find a use case where I think that accessing a buffer in a static method is a good idea. Now that doesn't mean that it's not - just that I can't think of a case where I would do it.

Can you explain your answer a little?

Not every discussion is about use cases or patterns. Sometimes the ABL behaves in a way that is surprising - but after thinking about it at least understandable (based on other experiences with the language). So it should be possible to discuss language features without being insistently being asked about the use case. I'm sure that everybody on this thread has the same view that in a layered architecture there is probably no use case (or no problem because DB access is on the AppServer and the AppServer usually does not disconnect a DB). But the majority of ABL applications is not yet layerd...

I've attended four progress user group meetings over the last three weeks and I've heard more than once that the fact that people have to justify why they are asking things in forums is keeping them away from forums and maybe also keeping them away from using OO in the ABL at all.

Posted by bsgruenba on 29-Mar-2010 23:57

That's a fair argument, and I'm not going to disagree with you.

But there's a caveat to that. Someone like you, who has immense respect in the industry, can make a statement that leaves his peers concerned. Concerned, not because they think that you are wrong, but because they believe that there's a strong possibility that you have considered something that they have not.

That is my reason for asking. In this case, the fact that you do not have a specific issue with buffers in static methods gives me great pause for concern, because you are someone whose opinion I greatly value and if you believe there is a reason why it's not a problem, or a use case that justifies it, I really want to know what that is because it probably means that I need to re-evaluate my own position.

There's someone else I know that I worked with for a very long time at Progress with whom I have had a lot of disagreements. Often, he would come back to me and say that he did not agree with me, but he was not sure why. I knew that if that was the case, I best shut up and wait, because he probably had a really good reason that he could not articulate at the time.

I have learned to listen to people like that, and you are one of them. So please don't take it as criticism. It's more a case of that you are not someone I will ignore if you have a concern.

Posted by cwills on 30-Mar-2010 01:25

Thanks for pointing that out Mike.

My testing suggests that any class that has a DB-REFERENCE as well as any STATIC member will hold a schema-lock, even after the class instance has been deleted...

So something like this:

CLASS CustomerHelperInstance:


  DEFINE PUBLIC STATIC VARIABLE staticVariable AS CHARACTER NO-UNDO.

 

  METHOD PUBLIC VOID ShowCustomer():

   

    FIND FIRST Customer WHERE Customer.CustNum = 1 NO-LOCK.

    MESSAGE Customer.Name VIEW-AS ALERT-BOX.

   

  END METHOD.


END CLASS.

Will cause a schema-lock once you create an instance of it.... even if you do not reference the static member like below.

MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS

     VIEW-AS ALERT-BOX.


DEFINE VARIABLE helper AS CustomerHelperInstance NO-UNDO.

helper = NEW CustomerHelperInstance().

 

DELETE OBJECT helper NO-ERROR.


PAUSE 5 NO-MESSAGE.


DISCONNECT sports2000.


MESSAGE "connected sports2000" CONNECTED ("sports2000") SKIP NUM-DBS

     VIEW-AS ALERT-BOX.

Posted by Tim Kuehn on 30-Mar-2010 07:15

mikefe wrote:

Not every discussion is about use cases or patterns. Sometimes the ABL behaves in a way that is surprising - but after thinking about it at least understandable (based on other experiences with the language). So it should be possible to discuss language features without being insistently being asked about the use case. I'm sure that everybody on this thread has the same view that in a layered architecture there is probably no use case (or no problem because DB access is on the AppServer and the AppServer usually does not disconnect a DB). But the majority of ABL applications is not yet layerd...

I've attended four progress user group meetings over the last three weeks and I've heard more than once that the fact that people have to justify why they are asking things in forums is keeping them away from forums and maybe also keeping them away from using OO in the ABL at all.

Something to keep in mind is that not everyone here has the same extensive background in OO programming that the current group of people discussing this topic seem to have. In fact, I'd go so far as surmising that most ABL developers are lacking in that area. Consequently not only are they learning how to "think OO", they won't know the "perils" of using certain OO constructs in what would seem to be a perfectly logical way.

Take the present topic - it's been asserted that putting buffer references in a static class has 'no use case'. Outside of the schema-lock question, I can only wonder - why not?

If I want an object that'll survive the duration of a session and not get garbage collected, then it would seem I'm kinda stuck with a static object. If an ABL turned OOABL developer sees that putting a buffer in a static is a good idea, they're going to need a better reason not to do things that way than someone else saying "I wouldn't do it" or "I don't see a use case for that." An alternative solution to the problem the developer is addressing would be even better.

Posted by jmls on 30-Mar-2010 08:06

Speaking as one of those developers, I've been round the block so many times on this oo thing I've getting dizzy

However, I've (finally) come up with my own rules for what I use and when. They work for me, they may make the purists blanch, but hey - they aren't me!

Simple OO Rules

(Soor for short !).

1) "Enums" are defined as a class with only static properties, and a optional static method to validate a value

/* enumPhone.cls */

DEF STATIC PROPERTY Home AS CHAR NO-UNDO INIT "Home Number".

DEF STATIC PROPERTY Work  AS CHAR NO-UNDO INIT "Business".

/* someclass.cls */

MESSAGE EnumPhone:Home VIEW-AS ALERT-BOX.

2) Singleton classes (you only want one type of each class instantiated) are defined as a normal class, with a static property called Instance

/* singleton example */

CLASS Sample:

     DEF STATIC PUBLIC PROPERTY Instance AS CLASS Sample NO-UNDO

          GET():

               IF NOT VALID-OBJECT(Instance) THEN Instance = NEW Sample().

               RETURN Instance.

          END GET. PRIVATE SET.

     METHOD PUBLIC VOID ShowMe():

          MESSAGE "Here" VIEW-AS ALERT-BOX.

    END METHOD.

END CLASS.

/* demo class */

Sample:Instance:ShowMe().

/* demo class 2 */

DEF VAR Sample1 AS CLASS Sample.

Sample1 = Sample:Instance.

Sample1:ShowMe().

.....

Sample1:ShowMe().

.....

Sample1:ShowMe().

.....

Sample1:ShowMe().

.....

Sample1:ShowMe().

3) Only use singleton classes for library-style functions, or for session-specific variables

4) I'm sure I'll remember some more when my brain starts functioning

Posted by Thomas Mercer-Hursh on 30-Mar-2010 11:26

I'm not sure that I get this idea of a discussion of language features detached from usage.  Academics might do that, but it seems to me that most programmers are practical in focus.  If it were true that no one would ever think about using a DB access in a static, why has the topic even come up?  And, given that the behavior is consistent and expected compared to other aspects of the language, why does it get more than just a shrug and "let's move along"?

And I have to say, that while I am certainly in favor of encouraging participation, I don't know that I think that failing to ask "why do you want to do this?" is a good way to encourage participation.   There are a lot of cases where the nature of the question makes it clear that the asker is thinking about the problem in the wrong way.  The long running classic being all those questions about "how can I absolutely ensure that the user won't be able to leave this field and go elsewhere until they have entered valid data?".  The best response to that question is not to provide the asker with code which will do what they want, but to educate them not to want it.

If we really all do agree that one shouldn't mess with the DB in a static, then there is nothing interesting to discuss here.  A) The problem will never arise; and B) We understand why the condition exists.  If that is all, then what's to discuss?  But, if there is a use case where we would want to do this, then we have a problem to solve and thus something to discuss.

Posted by Thomas Mercer-Hursh on 30-Mar-2010 11:36

Tim, the core of the skepticism about the existence of a use case is based on the nature of a static or a singleton.  Singleton is sometimes discussed as a possible anti-pattern since it is a pattern which is very easy to abuse ... not unlike global shared variables.  Both are something that can be very useful in very particular circumstances, but where the default expectation should be that you don't really want to go there if you can help it.  Compare to a superprocedure.  Some supers might well be things that you load up right at the start of a session and expect to leave there all the time.  But, you can get rid of it if you need to; you are in control of its lifecycle.  Make something static and you lose that control.

Posted by Thomas Mercer-Hursh on 30-Mar-2010 11:38

Soor for short !

Are you sure that isn't Simple Object Usage Rules?

Posted by Thomas Mercer-Hursh on 30-Mar-2010 11:43

Julian, I think the discussion has now looped around far enough to have illustrated an important point in response to your original question, to wit, it is possible to implement a singleton without statics and, if one does so, the singleton can be disposed of in a way that an object of all statics can not.

Posted by Jurjen Dijkstra on 31-Mar-2010 08:41

Strange, I am reading this thread and it is asserted that we should agree that buffers dont belong in statics because it is bad practice. But it seems like nobody wants to tell why it is a bad idea and why it is bad practice.

Instead, the question is turned around to "convince me with a use case where you need buffers in a static".

Please, enlighten me why buffers should not be accessed in a static method.

Also, I agree with Mike that sometimes you get the feeling that "you're doing it wrong" has become the default answer to many questions.

Posted by jquerijero on 31-Mar-2010 10:45

I think the discusion deviated too much from the original problem that was presented causing too much confusion.


The issue was "locking a record and disconnecting DB at run-time" which in this case since we are going to hold on (at least that is what I assumed) to the lock record the STATIC class is not suitable if you want to be able to disconnect the database during run-time.

However, if you don't need the lock to persist during the lifetime of the application, say in case, we are reading User Preferences or Application Settings a STATIC class is very useful.

Posted by Thomas Mercer-Hursh on 31-Mar-2010 11:31

Try googling on "singleton anti pattern" and you will find lots of discussion about why one should be cautious about using them, but, basically, it is the same reason to be cautious about using global anything .... one spends a lot of effort controlling scope and, especially in good OOABL, encapsulating functionality.  Global variables and statics have no scope -- they are everywhere and forever.  People often resort to them for the wrong reasons.  Including a database connection in that permanence is simply raising the bar for the complexity and significance of this thing that has escaped from careful control.  It is not that there is something specific about a buffer, but rather the whole idea .... symbolized by the fact that one then has a DB connection that one can't get to go away.

And, while unfortunate, I think it is true that "you're doing it wrong" is the best answer to quite a few questions.  Don't you find that often when you get stuck on a problem that the answer comes from reformulating the question?  Often, just getting the question right makes the problem disappear.  I think this is particularly true when moving into a new area.  I remember the early days of ABL GUI and all the questions on PEG which stemmed from the author not having comes to grips with the whole notion of event-driven programming.  Their problem came from trying to force things back into a procedural model in an event-driven context.  I.e., they would have been OK if they had just stuck with a procedural model, but in trying to transition to event-driven thinking, they hadn't quite made the transition yet and that meant they kept experiencing problems which didn't exist, or, at least, wouldn't exist if they were thinking in the new paradigm.  Clearly, we are going through another one of those periods with a transition to OOABL.  People used to procedural code are having trouble with OO code because they aren't yet quite thinking in an OO way, even though they are using OO forms.  When that is true, the right answer, the best answer, is to reformulate the question.

Posted by Thomas Mercer-Hursh on 31-Mar-2010 11:33

if you don't need the lock to persist during the lifetime of the  application, say in case, we are reading User Preferences or Application  Settings a STATIC class is very useful.

And I would suggest there are better solutions.  There is no reason to hang on to the database during the entire session for information that you are reading once.

Posted by Tim Kuehn on 31-Mar-2010 11:54

tamhas wrote:

And I would suggest there are better solutions.  There is no reason to hang on to the database during the entire session for information that you are reading once.

There is if your concurrent licensing model requires holding a share-lock on a record for the duration of the user session.

Posted by Thomas Mercer-Hursh on 31-Mar-2010 12:13

Sounds like defining your way into a box.

Posted by Tim Kuehn on 31-Mar-2010 12:32

Was that supposed to be constructive?

Posted by Thomas Mercer-Hursh on 31-Mar-2010 12:54

Point being that I doubt there is any principle, no matter how worthy, that one can't define a condition where it is necessary to violate it.  Then, one has to decide between the definition and the violation.    In particular, one shouldn't confuse the implementation with the functional requirement.  Holding a share-lock is an implementation.  Is it the only way to meet the functional requirement?  Is it the best way?  Would you ordinarily think that holding a share lock for an extended period was a desirable feature of your application?

But,  even if one decides to stick with the share lock *and* comes up with a compelling reason to put it in a static class, accepting the limitations which that implies, that doesn't mean that the principle no longer exists or is no longer valid.  One shouldn't use "I really, really have to do this here and it wil be OK" to then do it over and over again at the drop of a hat.  Instead, keep the principle in place and comment the exception with remarks indicating "I really shouldn't be doing this and I will stop doing it as soon as I think of a better way" so that some other programmer doesn't come along and say "Oh, that's handy; I'll use this over here".

This is why the quest for use cases ... so that we can examine whether or not they actually *are* the best solution or a necessary solution.  By saying "I need to hold a share lock to implement my licensing scheme", you have defined a requirement which can't be met any other way ... as long as one assumes that the share lock is the only possible implementation.    I don't see that as calling the principle into question.

Posted by Tim Kuehn on 31-Mar-2010 14:07

You're still presuming a fair bit more knowledge and experience with OO coding than one would generally find in the ABL developer community. Chances are that if an ABL developer playing with OO puts a buffer reference in a static object, it'll be because it made sense at the time and there's no reason to put in some kind of disclaimers that this is an exception to "proper" development techniques.

Now - if you care to offer concrete counter-use-cases where the same objective can be accomplished some other way, you'd get a lot further compared to denigrating certain ideas "just because".     

Posted by Thomas Mercer-Hursh on 31-Mar-2010 14:22

I don't have any use cases that have been offered that I can counter, except yours and the way you have stated it, you have defined the requirement into the solution, so I can't counter that.  All I can do is to ask whether a share-lock is really the best way to fulfill the requirement, but one would have to know a lot more about the requirements to begin to think of other solutions.  In general terms, I dislike the idea of holding long lasting locks for any purpose and I would be thinking about some kind of token or whatever that only involved a brief connection, but I don't know your requirements.

I am a little puzzled because it sounds like you think this is some very detailed recommendation that one would have to learn a lot to understand.  It's not.  All it is about is that OO is all about encapsulating things, limiting their scope, controlling the limits of their impact.  Things like a static that is going to hang around until the session is over is not keeping in that spirit.  Wrapping a DB connection into the thing that hangs around forever is even less in keeping with that spirit.  That's all.

It is no different really than the old contrast between explicit parameter passing and shared variables.  People used shared variables because they were dead easy and it was very handy in situations like having a nested set of run statements and wanting to fiddle with something at the very bottom of the nest.  But, all that loosey goosey poking about is a maintenance nightmare.  It is much cleaner to explicitly pass parameters since then you know where values come from and go to.

Statics are the OO equivalent of a global shared variable.  Arguably useful, but creating that same kind of loosey goosey lack of clear connections.  There is nothing more to it than that.

Posted by jquerijero on 31-Mar-2010 14:39

In that case, you will need to tell your STATIC class that you are about to disconnect the DB in which case you call a method of the STATIC class to release the lock. You call another method to reacquire the lock after reconnecting to the DB. I would suggest wrapping the DB disconnect/connect process into another static class.

Posted by Tim Kuehn on 31-Mar-2010 14:50

The problem is that simply having an active buffer reference in any active code instance will prevent a db DISCONNECT() from running to completion.

If the db reference was dynamic, and the dynamic buffer was deleted before the DISCONNECT, then it might work.

Posted by Admin on 31-Mar-2010 14:56

In that case, you will need to tell your STATIC class that you are about to disconnect the DB in which case you call a method of the STATIC class to release the lock. You call another method to reacquire the lock after reconnecting to the DB. I would suggest wrapping the DB disconnect/connect process into another static class.

That won't work! As Cameron and myself have shown with the sample code is that it's not about record locks you can control with your code. It's about a schema lock because the static instance remains in memory.

Posted by jquerijero on 31-Mar-2010 15:15

A class with a static member is ALSO static class. The reference to the hInstance have to be released. That class you guys refer to as singleton is missing a method that releases the static member Instance. The static instance that is created will remain in the memory even if you delete all the instance of objects. In this case you must call a method to release "hInstance" then disconnect.

Posted by Admin on 31-Mar-2010 15:21

A class with a static member is ALSO static class. The reference to the hInstance have to be released. That class you guys refer to as singleton is missing a method that releases the static member Instance. The static instance that is created will remain in the memory even if you delete all the instance of objects. In this case you must call a method to release "hInstance" then disconnect.

Camerons code has shown that this won't work.

Posted by jquerijero on 31-Mar-2010 15:21

This is happening because you have not released CustomerHelperSingleton. Remember that a class with a static member is also static class.

Try adding

DELETE OBJECT CustomerHelperSingleton:Instance.

DELETE OBJECT CustomerHelperSingleton. /* I'm not sure if this is possible, but basically, there is an instance that is implicitly created behind the scene the first time you try to access Instance when you run CustomerHelperSingleton:Instance, you are in fact creating a second instance of CustomerHelperSingleton. It just so happen that the property Instance is shared */

Posted by Admin on 31-Mar-2010 15:41

DELETE OBJECT CustomerHelperSingleton.

Won't compile:

    • Unknown Field or Variable name - CustomerHelperSingleton. (201)

Posted by Marko Myllymäki on 08-Dec-2014 05:38

I found this old thread after reading Tom Bascom's presentation slides about static classes:

http://pugchallenge.eu/images/2014pdfs/tom_bascom_stop_shared_variables.pptx

We have been using the same approach for replacing shared variables with static properties.

However, now I started to wonder if we should use a singleton class instead. In OE 11.4 you can pass an object as a parameter between client and AppServer. Using singleton, we could send this singleton object to AppServer but the same can not be done with a static class. So, if we need to access the same common values both in AppServer and client, using a singleton pattern might be useful. 

Any thoughts?

Posted by Peter Judge on 08-Dec-2014 08:14

As Thomas Mercer-Hursh  says early in the thread
> Singleton is a pattern; static is a language feature. 
 
You can easily have singletons that are not implemented using static members. The only trick is that someone, somewhere needs to hold at least one reference otherwise the garbage collector will do its work.
 
If you want to have 2 instances of the same class running on client and server, you will need to pass the values back and forth. Were you thinking of passing the singleton itself?
 
-- peter
 
[collapse]
From: Marko Myllymäki [mailto:bounce-mmy@community.progress.com]
Sent: Monday, 08 December, 2014 06:39
To: TU.OE.Development@community.progress.com
Subject: RE: [Technical Users - OE Development] Singleton vs Static
 
Reply by Marko Myllymäki

I found this old thread after reading Tom Bascom's presentation slides about static classes:

http://pugchallenge.eu/images/2014pdfs/tom_bascom_stop_shared_variables.pptx

We have been using the same approach for replacing shared variables with static properties.

However, now I started to wonder if we should use a singleton class instead. In OE 11.4 you can pass an object as a parameter between client and AppServer. Using singleton, we could send this singleton object to AppServer but the same can not be done with a static class. So, if we need to access the same common values both in AppServer and client, using a singleton pattern might be useful. 

Any thoughts?

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Marko Myllymäki on 09-Dec-2014 01:39

Hi Peter, thanks for you reply. Yes, I was thinking of passing the singleton object itself. Do you see some obstacles in that?

Posted by Marian Edu on 09-Dec-2014 02:11

Passing a singleton will give you a new instance on the client each time so there will only be one instance of it on the server (well one on each agent)... on the client you'll have a bunch of those unless you only ask for it once but I doubt singleton pattern will enforce only one instance on the client. This is only a creational pattern while when passing objects around those are serialized and de-serialized back, will be interesting to find out what progress is going to do though.

Posted by Simon L. Prinsloo on 09-Dec-2014 04:17

In my experience, the type of context you may typically want to pass  around like this tends to be bound to the client, not the server. So the singleton will always be passed in one direction, towards the server, where it is treated and handled as a singleton for the duration of the call, but needs to be discarded at the end of the call in favour of the singleton passed by the next caller.

A typical use case that I can think of:
Each branch has a cash book and the responsible users can only access the cash book data for their own branch. They cannot select another branch. But the internal auditor needs to access the cash book data of any branch. You could ask him in each program which branch he is working with, or you can always default it to the last one he used. In order to do this, you need to track his "working" branch in some environment variable. Legacy code typically use a global shared variable, modern code will need a singleton. This also enables both the branch user and the auditor to use common programs without forcing the branch user to enter the only possible valid value each time, since the "working branch" variable/property can be set to the user's branch.

If the singleton contains a lot of data that are always needed in the business logic, you typically end up passing all of these as separate parameters to the AppServer (which later causes problems when more data is needed) or as some formatted string (which can be extended if more data is needed) with every call to the AppServer. The latter would simply use the parameters, store it in a singleton or, in traditional code, replace the values in the global shared variables with those in the parameters. If you pass the singleton from the client to the AppServer, you have the option of replacing any possible pre-existing singleton with the received object or copy the whole object's values in a simple call, rather than copying values one by one from a formatted string.

Posted by Peter Judge on 09-Dec-2014 10:23

It's technically possible, of course, but then (I think) you're moving into a place where you're treating the object as a value object (or data transfer object) rather than a singleton. A singleton in my mind is one isntance of an object that does some work. By definition, there is one per AVM (one on the client, one per agent on the server). As Marian says elsewhere, passing the singleton results in more than one singleton.

The pseudo-code below shows how I'd think about it. (there are some details that will vary). The point is to keep the singleton and data-transfer-object patterns distinct in your mind.

/* this is a singleton*/
class UserContextManager:
  /* this is ONE way of implementing a singleton; not my first choice,
     but it's readable */
  def static property Instance as UserContextManager get. private set.
  method public UserContext GetCurrentContext().
  method public void SetCurrentContext(poUC as UserContext).
  /* other methods to do Stuf */
end class.
/*this is the data transfer object */ class UserContext serializable /* 11.4+ or roll-your-own */: def pub property Name as char get. set. def pub property SomeOtherData as class Foo get. set. /* whatever you want to consider context */ end class. class Client.ServiceAdapter: method public void CallAppServer(<args>): oUC = UserContextManager:GetCurrentContext(). run Server/ServiceInterface.p on hAppServer ( input <args>, input-output oUC). UserContextManager:SetCurrentContext(oUC). end. end class. /* ServiceInterface.p */ def input parameter <args> def input-output parameter poUC as UserContext. UserContextManager:SetCurrentContext(poUC). /* run update_ledger.p or whatever needs running */ poUC = UserContextManager:GetCurrentContext().

hth,

-- peter

Posted by Simon L. Prinsloo on 10-Dec-2014 01:58

I do agree that that would be a cleaner implementation than having the actual data contained inside the singleton and passing the singleton itself. With this pattern, the choice of how to implement the singleton would become irrelevant again, since that is not what is passed.

Posted by Marko Myllymäki on 15-Dec-2014 01:32

Peter, thanks a lot for sharing your thoughts and sample code. That approach makes sense.

BTW, there seems to be a couple of typos in the sample code:

e.g. UserContextManager:GetCurrentContext(oUC) should be UserContextManager:SetCurrentContext(oUC).

but the idea if fully understandable.

Posted by Peter Judge on 15-Dec-2014 08:16

Hi Marko,
 
The cut&paste demon strikes again ... fixed now :)
 
[collapse]
From: Marko Myllymäki [mailto:bounce-mmy@community.progress.com]
Sent: Monday, 15 December, 2014 02:33
To: TU.OE.Development@community.progress.com
Subject: RE: [Technical Users - OE Development] Singleton vs Static
 
Reply by Marko Myllymäki

Peter, thanks a lot for sharing your thoughts and sample code. That approach makes sense.

BTW, there seems to be a couple of typos in the sample code:

e.g. UserContextManager:GetCurrentContext(oUC) should be UserContextManager:SetCurrentContext(oUC).

but the idea if fully understandable.

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

This thread is closed