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.
