CSC/ECE 517 Fall 2010/ch3 3f sm: Difference between revisions
Line 79: | Line 79: | ||
Ruby is a dynamically typed object oriented language. This means type checking happens at run time. Implementing the singleton pattern in | Ruby is a dynamically typed object oriented language. This means type checking happens at run time. Implementing the singleton pattern in | ||
Ruby has the same aspects as in Java. So we can just write the ruby equivalent of the code above and we have the | Ruby has the same aspects as in Java. So we can just write the ruby equivalent of the code above and we have the Ruby singleton pattern. | ||
Just to show how it looks we have it down here. | Just to show how it looks we have it down here. | ||
Revision as of 02:47, 7 October 2010
Introduction
Singleton pattern has the simplest class diagram among all the design patterns discussed by the gang of four. It has just one class. It has some interesting characteristics. Lets start with the definition itself.
- Singleton
- Ensures a class has only one instance, and provides a global point of access to it.
The definiton can be explained as "we can have one and only one instance of the class". Singleton pattern is useful in cases where you need to interact with input and output devices, to maintain thread pools, to log events in a single log file etc. Although the class diagram of the singleton class seems simple, it's implementation can be quite a task. To make sure its peculiar behavior of having just one object and serving up the same object when requested makes it so. Classes are naturally designed in a way that we create many instances of them. So we have to move against this and make sure we have only one object. We have to take care of multi-threading scenarios, subclassing etc. We will discuss the implementation of singleton pattern in dynamic and static languages below.
Singleton Pattern in Java
Java is a statically typed object oriented language. Among other things this means that type checking is done in compile time in Java.
Implementation
- Singleton pattern in java is implemented considering the 4 steps mention below.
- Create a default constructor and make it private.
- Create a method for getting the reference to the singleton object.
- Make the access method synchronized to prevent thread issues.
- Prevent cloning of the object by overriding the object clone method.
class SingletonObject { private static SingletonObject myObject; private SingletonObject() { // Optional Code } public static SingletonObject getSingletonObject() { if (myObject == null) { myObject = new SingletonObject(); } return myObject; } }
In this code we have followed the first two steps. We are making the default constructor private. This means that the constructor can be accessed only by other methods inside the class. This prevents a developer from creating an object of the class by using the 'SingletonObject myObject = new SingletonObject()'. To get the object of the singleton class the developer must call the SingletonObject.instance() method. In the instance method a new object is being created if a singleton doesn’t exist, otherwise the existing reference to SingletonObject is returned. This way the reference to same object is returned every time the instance method is called. In the above example we are using lazy instantiation, that means the object is created only when the for the first time the initialize method is called. We could have also used early instantiation in that case the object would be instantiated when the class is loaded.
class SingletonObject { private static SingletonObject myObject; private SingletonObject() { // Optional Code } public static synchronized SingletonObjec myObject() { if (myObject == null) { myObject = new SingletonClass(); } return myObject; } public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }
Synchronization and Cloning
The above code seems to be good enough for a singleton pattern, but when we take a closer look we realize that when multiple threads access this instance method
there is a possibility that more than one object can get created. So we need to make this method synchronized. This makes sure that only one thread can
access this function at a time and thus ensuring that only object is ever created of the SingletonObject class. Synchronizing the singleton method has
some consequences. This may result in performance loss if the singleton instance method is being accessed a lot of times in our over all application.
This may be the case in some singletons which manage I/O operations, write logs to the same file etc. A closerlook at the code can tell us that
synchronizing access to the instance() method is not necessary after the object is created, that is after the first call to the initialize() method.
We can still create a copy of the singleton by cloning it using the Object’s clone method. It can be done like this 'SingletonObject clonedObject = (SingletonObject) obj.clone();'. To avoid this we have to override the clone method as shown above. We can just throw an exception when this method is called.
Singleton pattern in Ruby
Ruby is a dynamically typed object oriented language. This means type checking happens at run time. Implementing the singleton pattern in Ruby has the same aspects as in Java. So we can just write the ruby equivalent of the code above and we have the Ruby singleton pattern. Just to show how it looks we have it down here.
class SingletonObject def initialize #option code end @@instance = SingletonObject.new def self.instance return @@instance end private_class_method :new end
This is how singleton is implemented in ruby. In this case we are using static instantiation as you can see. We might have to use mutex and lock the block of code which runs initialize. Its still a lot of things to care about, so ruby provides a simpler way to do it. Ruby standard library has a singleton module which we can use. It defines the initialize method for us. It does lazy instantiation of the singleton object too.
require 'singleton' class SingletonObject include Singleton end
The include Singleton statement in the above code includes all the methods of the Singleton module. The instance method we saw in the previous example is implemented in the Singleton module. This module uses the lazy instantiation method.
Conclusion
Singleton pattern in both languages as seen above has to take care of a lot of implementation details. It is counter intuitive to the concept of objects and classes. For example subclassing is another important thing to remember when we use the singleton pattern. We cannot subclass the singleton class as it may lead to duplication, unnecessary consequences in the lower classes etc. Implementation of singleton pattern mostly decides on what tradeoff we are ready to make. If we dont call the initialize method too many times then we can make do with having it being synchronized. If lazy instantiation is necessary then we have to tradeoff the convinience that comes with using static variables. So now we have learned to implement the singleton pattern in static an dynamic variables.
References