Skip to content

Commit cbe19ff

Browse files
committed
Merge pull request #66 from sukima/singleton-refactor
Refactors the code in the singlton design pattern
2 parents 44a0023 + 12d5623 commit cbe19ff

File tree

1 file changed

+56
-40
lines changed

1 file changed

+56
-40
lines changed

chapters/design_patterns/singleton.md

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,59 +7,75 @@ chapter: Design Patterns
77

88
Many times you only want one, and only one, instance of a class. For example, you may only need one class that creates server resources and you want to ensure that the one object can control those resources. Beware, however, because the singleton pattern can be easily abused to mimic unwanted global variables.
99

10+
1011
## Solution
1112

1213
The publicly available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned.
1314

14-
The actual definition of the singleton class follows.
15+
This is works because CoffeeScript allows you to define executable statements inside a class definition. However, because most CoffeeScript compiles into a [IIFE][] wrapper you do not have to place the private class inside the class definition if this style suits you. The later might be useful when developing modular code such as found in [CommonJS][] (Node.js) or [Require.js][] (See the discussion for an example).
1516

16-
Note that I am using the idiomatic module export feature to emphasize the publicly accessible portion of the module. Remember coffeescript wraps all files in a function block to protect the global namespace.
17+
[IIFE]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/
18+
[CommonJS]: http://www.commonjs.org/
19+
[Require.js]: http://requirejs.org/
1720

1821
{% highlight coffeescript %}
19-
root = exports ? this # http://stackoverflow.com/questions/4214731/coffeescript-global-variables
20-
21-
# The publicly accessible Singleton fetcher
22-
class root.Singleton
23-
_instance = undefined # Must be declared here to force the closure on the class
24-
@get: (args) -> # Must be a static method
25-
_instance ?= new _Singleton args
26-
27-
# The actual Singleton class
28-
class _Singleton
29-
constructor: (@args) ->
30-
31-
echo: ->
32-
@args
33-
34-
a = root.Singleton.get 'Hello A'
35-
a.echo()
36-
# => 'Hello A'
37-
38-
b = root.Singleton.get 'Hello B'
39-
a.echo()
40-
# => 'Hello A'
22+
class Singleton
23+
# You can add statements inside the class definition
24+
# which helps establish private scope (due to closures)
25+
# instance is defined as null to force correct scope
26+
instance = null
27+
# Create a private class that we can initialize however
28+
# defined inside this scope to force the use of the
29+
# singleton class.
30+
class PrivateClass
31+
constructor: (@message) ->
32+
echo: -> @message
33+
# This is a static method used to either retrieve the
34+
# instance or create a new one.
35+
@get: (message) ->
36+
instance ?= new PrivateClass(message)
37+
38+
a = Singleton.get "Hello A"
39+
a.echo() # => "Hello A"
40+
41+
b = Singleton.get "Hello B"
42+
b.echo() # => "Hello A"
43+
44+
Singleton.instance # => undefined
45+
a.instance # => undefined
46+
Singleton.PrivateClass # => undefined
47+
{% endhighlight %}
4148

42-
b.echo()
43-
# => 'Hello A'
4449

45-
root.Singleton._instance
46-
# => undefined
50+
## Discussion
4751

48-
root.Singleton._instance = 'foo'
52+
See in the above example how all instances are outputting from the same instance of the Singleton class. You can also see that the PrivateClass and instance variable are not accessible outside the Singleton class. In essance the Singleton class provides a static method get which returns only one instance of PrivateClass and only one. It also hides the PrivateClass from the world so that you can not create your own.
4953

50-
root.Singleton._instance
51-
# => 'foo'
54+
The idea of hiding or making private the inner workings is preference. Especially since by default CoffeeScript wraps the compiled code inside it's own IIFE (closure) allowing you to define classes without worry that it might be accessible from outside the file. In this example, note that I am using the idiomatic module export feature to emphasize the publicly accessible portion of the module. (See this discussion for further explanation on [exporting to the global namespace][1]).
5255

53-
c = root.Singleton.get 'Hello C'
54-
c.foo()
55-
# => 'Hello A'
56+
[1]: http://stackoverflow.com/questions/4214731/coffeescript-global-variables
5657

57-
a.foo()
58-
# => 'Hello A'
58+
{% highlight coffeescript %}
59+
root = exports ? this
60+
61+
# Create a private class that we can initialize however
62+
# defined inside the wrapper scope.
63+
class ProtectedClass
64+
constructor: (@message) ->
65+
echo: -> @message
66+
67+
class Singleton
68+
# You can add statements inside the class definition
69+
# which helps establish private scope (due to closures)
70+
# instance is defined as null to force correct scope
71+
instance = null
72+
# This is a static method used to either retrieve the
73+
# instance or create a new one.
74+
@get: (message) ->
75+
instance ?= new ProtectedClass(message)
76+
77+
# Export Singleton as a module
78+
root.Singleton = Singleton
5979
{% endhighlight %}
6080

61-
## Discussion
62-
63-
See in the above example how all instances are outputting from the same instance of the Singleton class.
64-
6581
Note how incredibly simple coffeescript makes this design pattern. For reference and discussion on nice javascript implementations, check out [Essential JavaScript Design Patterns For Beginners](http://addyosmani.com/resources/essentialjsdesignpatterns/book/).

0 commit comments

Comments
 (0)