Hi,

Good deal, glad that helped!

One thing I can't help but point out though:

> I realise I could have achieved the same thing by use
> of a series of global scoped variables, but trying to be more OO in the
> approach (even with JavaScript) meant that I needed the singleton system
> to encapsulate the code in each object.

That was part of my point: In JavaScript, objects are just objects.
You can have an object that's a constructor function for another
object (functions are objects), and you can have the constructor
function/object also have a `getInstance` method that always returns
the one other object it created. Or...you can just have a single
object in the first place, which is dramatically simpler, and still --
by definition! -- a singleton. :-) You don't lose anything by going
the second route. In each case, you have a global reference that
defines the thing. In each case, it has methods you can call; it can
be passed around (functions are first class objects), etc., etc.

That said, sometimes -- particularly if you're using JavaScript in a
mostly class-based environment -- it pays to go through the
convolutions of making it look the way the target audience
(developers, etc.) expects.

Anyway, best of luck!
--
T.J. Crowder
Independent Software Consultant
tj / crowder software / com
www.crowdersoftware.com

On Feb 7, 8:11 pm, Daff <[email protected]> wrote:
> HI,
>
> TJ, thanks for the great response, exactly the sort of
> discussion/comment I was looking for.
>
> Knew there should have been a better way of doing it.
>
> Having read/reviewed some more on the singleton, agree that perhaps
> ensuring new is not used is probably a better way (and OO compliant(?!))
> of instantiating the singleton, instead utilising the getInstance method
> usually implemented.
>
> Primary reason for using singletons in this case is because I am
> implementing a builder pattern using various builder classes, which will
> be used in a number of objects, and ensuring they are the singletons,
> means that I can shared them across each class that uses a single
> builder instance.  I realise I could have achieved the same thing by use
> of a series of global scoped variables, but trying to be more OO in the
> approach (even with JavaScript) meant that I needed the singleton system
> to encapsulate the code in each object.
>
> I really appreciate  the detailed response and the time taken to answer.
> I hope that it is of benefit to others as well as just me.
>
> Regards.
>
> daff
> SOFTWARE ENGINEER
>
> andrew 'daff' niles | spider tracks ltd |  117a the square
> po box 5203 | palmerston north 4441 | new zealand
> P: +64 6 353 3395 | M: +64 21 515 548
> E: [email protected] <mailto:[email protected]>
>  W:www.spidertracks.com<http://www.spidertracks.com>
>
>
>
> T.J. Crowder wrote:
> > Hi,
>
> > Apologies in advance, this turned out to be a really long post.
>
> >> Found a reasonable attempt by Jim Higgson at
> >>http://jimhigson.blogspot.com/2009/01/prototype-singleton-classes.html
> >> but didn't like the fact that if you called new again it threw an
> >> exception.
>
> > I think the point of Jim's article there is that *all* calls to `new`
> > will throw an exception except for the one call that's performed
> > *internally* as part of the definition of the class. So users of the
> > class will never be able to use `new`, not even once. That's as close
> > to making `new` private as you're going to get, a `new` that always
> > fails and tells you the right thing to do. :-) (There are some issues
> > with the actual code in that article, but the principle is sound.)
>
> >> To make it work like that I have used the following code. Comments on
> >> anything that you don't like, or that might cause an issue would be
> >> appreciated.
>
> > What you've created there isn't really a singleton so much as multiple
> > instance facades sharing the same underlying state. It works, I'm not
> > criticising, but in a true singleton, there is *one* instance.
>
> > Stepping back: There's very little (if any) call for singleton
> > *classes* in JavaScript. (This is probably why you're not seeing more
> > discussion of them as relates to Prototype.) Singleton *classes* are
> > largely a way to work around the fact that in most class-based OOP
> > languages, the only way to create objects with behaviors (methods) is
> > to define a class and then create an instance of it. This isn't true
> > in JavaScript; objects are just objects, and objects can have
> > behaviors all of their own:
>
> >     var bar = {
> >         foo:  42,
> >         inc: function() {
> >             ++this.foo;
> >         }
> >     };
> >     // Usage:
> >     alert(bar.foo); // alerts 42
> >     bar.inc();
> >     alert(bar.foo); // alerts 43
>
> > But you *can* create singleton "classes" with JavaScript if you like.
> > In fact, JavaScript has a very handy feature to make it easier: You
> > can override the usual behavior of `new`. `new` calls a constructor
> > function with `this` set as a newly-created blank object. If the
> > constructor function doesn't return a value, `new` returns the
> > reference to the object it created. But if the constructor function
> > *does* return a value and that value is an object, `new` returns that
> > instead! So making `new` "return" a singleton in straight JavaScript
> > is dead easy: Just have your constructor function return the single
> > instance reference.
>
> > Unfortunately, Prototype's Class stuff doesn't support doing that. The
> > actual constructor function is generated by Prototype and has no
> > return value. Any value you return from your `initialize` function is
> > discarded.
>
> > So your choices if you want singleton classes are
>
> > 1. Disable `new`, throwing an exception like Jim does telling the user
> > what to do
>
> > 2. Don't use Prototype's Class stuff to define your singleton classes
> > and use `new`
>
> > or
>
> > 3. Create multiple instance facades on top of a single shared state
> > (as you did)
>
> > If you'll pardon a digression: Using scoping functions (a key aspect
> > of the module pattern) makes all three of those easier. I always use
> > scoping functions to define things. For instance (no pun!), here's how
> > I define a boring old class with Prototype's Class.create:
>
> > var PlainOldClass = Class.create((function() {
>
> >     // Our instance initializer
> >     function initialize(name) {
> >         this.setName(name);
> >     }
>
> >     // An instance function
> >     function speak() {
> >         alert("Hi, I'm " + this.name);
> >     }
>
> >     // Export our public functions
> >     return {
> >         initialize: initialize,
> >         speak:      speak
> >     };
>
> > })());
>
> > Note how I've defined and called a function to create the object to
> > pass into Class.create, rather than creating it directly using literal
> > notation. This gives us a private class-wide scope (truly private
> > class variables), which has several advantages -- not least that our
> > instance methods can be *named* methods (not anonymous ones), which
> > means debuggers and other tools can help us more effectively. This is
> > pretty much the only way right now you can have named instance methods
> > in current browser implementations of JavaScript. The usual way (to
> > date):
>
> > var Foo = Class.create({
> >     bar: function() {
> >         // ...
> >     }
> > });
>
> > or non-Prototype:
>
> > function Foo() {
> >     // ...
> > }
> > Foo.prototype.bar = function() {
> >     // ...
> > };
>
> > ...just creates an anonymous function and binds it to a property.
> > Tools can't help us much because the function has no name; in call
> > stacks and such you see a lot of "(anonymous)" entries.
>
> > Sadly, you can't do what Jim did in his article, although you *should*
> > be able to:
>
> > var Foo = Class.create({
> >     initialize: function initialize() {
> >         // ...
> >     }
> > });
>
> > That creates a named function called `initialize`, and then assigns it
> > to a property called `initialize`. You should be able to do that, it's
> > valid according to the spec, but bugs in both IE and Safari (and
> > possibly others) mean it doesn't work correctly. There's a great
> > article by kangax exploring this in various browsers:
> >http://yura.thinkweb2.com/named-function-expressions/
>
> > So why the digression into scoping functions? Because it makes all
> > three of your choices easier.
>
> > 1. Disable `new`:
>
> > var Singleton = (function() {
> >     // Our singleton instance
> >     var instance;
>
> >     // The class
> >     var Singleton = Class.create((function() {
>
> >         // Our singleton initializer.
> >         function initialize() {
>
> >             // Does our instance exist?
> >             if (!instance) {
> >                 // No, this is it -- initialize it
> >                 instance = this;
> >                 prepInstance.call(this);
> >             }
> >             else {
> >                 // Yes, fail
> >                 throw "Invalid use of `new`, use
> > `Singleton.getInstance` to get the singleton";
> >             }
> >         }
>
> >         // Our instance prep function.
> >         // Private, not exported to the prototype.
> >         function prepInstance() {
> >             this.foo = 42;
> >         }
>
> >         // Set 'foo'.
> >         // Public; this gets exported to the prototype.
> >         function setFoo(foo) {
> >             this.foo = foo;
> >         }
>
> >         // Get 'foo'.
> >         // Public; this gets exported to the prototype.
> >         function getFoo() {
> >             return this.foo;
> >         }
>
> >         // Export our public functions
> >         return {
> >             initialize:  initialize,
> >             setFoo:      setFoo,
> >             getFoo:      getFoo
> >         };
> >     })());
>
> >     // Our "getInstance" function, which we put on the class
> >     Singleton.getInstance = getInstance;
> >     function getInstance() {
> >         return instance;
> >     }
>
> >     // Create the single instance
> >     new Singleton();
>
> >     // Return the class object for the global symbol
> >     return Singleton;
> > })();
> > // WRONG usage:
> > var s = new Singleton(); // throws useful explanatory exception
> > // Correct usage:
> > var s1 = Singleton.getInstance();
> > var s2 = Singleton.getInstance();
> > alert("s1 === s2? " + (s1 === s2)); // alerts true
>
> > That actually uses two scoping functions, one for the class stuff and
> > one for the singleton stuff. The `instance` reference is truly
> > private, not a property of the constructor function or anything where
> > it can be tampered with. Also note that the `prepInstance` function is
> > also truly private; there's no reason for it to be a public function,
> > of the class or the instance.
>
> > 2. Don't use Prototype's Class stuff for the singleton, and use `new`
>
> > var Singleton = (function() {
> >     // Our singleton instance
> >     var instance;
>
> >     // Our singleton constructor, which we export by returning
> >     // from the anonymous scoping function
> >     function Singleton() {
>
> >         // Does our instance exist?
> >         if (!instance) {
> >             // No, create and initialize it
> >             instance = this;
> >             prepInstance.call(this);
> >         }
>
> >         // Return the instance; this is the magic bit JavaScript
> > enables
> >         return instance;
> >     }
>
> >     // Our instance prep function.
> >     // Private, not exported to the prototype.
> >     function prepInstance() {
> >         this.foo = 42;
> >     }
>
> >     // Set 'foo'.
> >     // Public; this gets exported to the prototype.
> >     function setFoo(foo) {...
>
> read more »

-- 
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en.

Reply via email to