570 likes | 724 Views
Unit 3 Singleton. Summary prepared by Kirk Scott. Design Patterns in Java Chapter 8 Singleton. Summary prepared by Kirk Scott. Pileated Woodpecker From Wikipedia, the free encyclopedia
E N D
Unit 3Singleton Summary prepared by Kirk Scott
Design Patterns in JavaChapter 8Singleton Summary prepared by Kirk Scott
Pileated Woodpecker • From Wikipedia, the free encyclopedia • The Pileated Woodpecker (Dryocopuspileatus) is a very large North Americanwoodpecker, roughly crow-sized, inhabiting deciduous forests in eastern North America, the Great Lakes, the boreal forests of Canada, and parts of the Pacific coast. It is also the largest woodpecker in the United States, excepting the possibly extinct Ivory-billed Woodpecker.
The name Dryocopus come from the Greekdrus, an oak tree and kopos, a cutter.
General Background on Responsibility and the Singleton Design Pattern • Objects in general should be responsible for themselves • That means that they should be internally consistent • It also means that they are self-sufficient • They do not depend (on the whole) on other classes/objects • They do not implement their functionality by referring to other classes/objects
In modeling a real problem domain, it can happen that for a given class there will be only one instance • This situation is known as a singleton • The idea is that the code for the class itself should control how many instances of it there are • It should not be the responsibility of all external code to determine how many instances there are
If there is a singleton class, responsibility for maintaining it as a singleton can be centralized in the class itself • In the context of the whole application, responsibility for this part of the design has been centralized • Viewed from another perspective, client code is relieved of the responsibility • Client code does then become dependent on the logic implemented in the singleton class
Book Definition of the Pattern • Book definition: • The intent of the Singleton pattern is to ensure that a class has only one instance and to provide a global point of access to it.
The authors state, “The mechanics of Singleton are more memorable than its intent.” • You might say that the pattern incidentally involves the apportionment of responsibility, but its purpose really has to do with construction rather than responsibility itself.
Implementing the Pattern • Challenge 8.1 • How can you prevent other developers from constructing new instances of your class?
Solution 8.1 • “To prevent other developers from instantiating your class, create a single constructor with private access. • Note that if you create other, nonprivate constructors or no constructors at all, other objects will be able to instantiate your class”
Comment mode on: • The idea of a private constructor has not come up before • If you have a private constructor, the question becomes, how and where do you call it from?
The Factory class definition on the following overhead illustrates the solution that the authors are proposing • The Factory class contains a default constructor which is declared private • It also contains a static method which returns a reference to an instance of the class • The class definition is followed by comments on how it works.
public class Factory • { • private static Factory factory = new Factory(); • private Factory() • { • } • public static getFactory() • { • return factory; • } • }
The class contains a static instance of itself as an instance variable • The variable is constructed (once) in its declaration by a call to the private constructor • The class also contains a static getFactory() method which returns a reference to the singleton • The call to the get method in a client piece of code would be like any call to a static method: • Factory singletonReference = Factory.getFactory();
An Alternative Approach to Implementing the Pattern • The book’s solution works • Not having considered the possibility of private constructors or classes containing instance variables which are instances of themselves, this is not the first solution approach that would have occurred to me • I would have been thinking along different lines
A Non-Solution • The following overhead shows a starting approach to solving the problem • Notice that it is incomplete • You are stopped when you realize that a constructor is a constructor • What does the constructor “return” if nothing is to be constructed?
/* An approach to creating a singleton that doesn’t work. */ • public class Factory • { • private static intinstanceCount = 0; • public Factory() • { • if(instanceCount == 0) • // do the construction • else • // don’t do the construction • } • }
A constructor is different from a method • You don’t have the choice of conditionally either returning a reference to an object or not • The constructor should return a reference to an object • The object is actually created ultimately by the fact that constructor calls are bucked up to the Object class • All the local constructor code does is initialize instance variables.
Could a constructor return a null reference? • Maybe, but there’s no reason to even find out. • If it could, that would not be desirable.
A Halfway Solution • On the other hand, a constructor, like a method, can be declared to throw an exception • Then the code can be written either to run successfully and return a reference to a newly created object • Or to throw an exception if the conditions for the Singleton design pattern aren’t met
public class Factory • { • private static intinstanceCount = 0; • public Factory() throws Exception • { • if(instanceCount == 0) • instanceCount++; • else • throw new Exception(“Uh-oh”); • } • }
Although the previous code isn’t the way the book does it, the code compiles • The thing client code would have to keep in mind is that a call to the constructor would have to be done in a try/catch block • This solution does restrict a class to having at most one instance
What’s Wrong with the Halfway Solution • On the other hand, it’s not a complete solution with all of the desirable characteristics mentioned by the book • One client can call the constructor one time and obtain a reference to the singleton • However, the solution does not provide a mechanism whereby multiple clients at different times could obtain a reference to that singleton
There is another, more serious aspect to this shortcoming • This solution puts the burden on the client code of keeping track of whether an instance has been created before • If an instance has been created, then all potential client code has to “know” or keep track of that fact
In other words, in terms of responsibility and centralization, this solution decentralizes responsibility. • Responsibility for making the one instance of the singleton is centralized in the singleton class. • But responsibility for knowing and keeping track is thrown onto the client code.
If an instance has been created, there would be no need to call the constructor again • Under this solution, if other client code didn’t “know”, it would mistakenly call the constructor again • Instead of getting a reference to the singleton, it would get an exception • This is not very helpful
The conclusion is this: • It is desirable to centralize all aspects of a singleton in the singleton class • Trying to deal with them on a piecemeal basis in every piece of potential client code is not good • There could be many places where this would have to be done • This is both inefficient and prone to error, either by writing in correct code or by missing a place where the code should be included
Lazy Initialization • Next, the book introduces what it calls lazy initialization • The idea is that an instance of the Singleton class is only constructed if some piece of client code requests a reference to it • This requires removing the construction from the line declaring the instance variable • Construction is then inserted into the getFactory() method • This is shown on the next overhead
public class Factory • { • private static Factory factory; • private Factory() • { • } • public static Factory getFactory() • { • if(factory == null) • factory = new Factory(); • return factory; • } • }
Notice how the logic of this approach does echo the halfway approach somewhat • Construction is conditional • Depending on your point of view, it may also be nicer because you are calling the constructor from within a method rather than doing it where the instance is declared
Challenge 8.2 • Why might you decide to lazy-initialize a singleton instance rather than to initialize it in its field declaration?
Solution 8.2 • “Two reasons for lazy-initializing singletons are as follows. • 1. You might not have enough information to instantiate a singleton at static initialization time. • For example, a Factory singleton might have to wait for the real factory’s machines to establish communication channels.
2. You might choose to lazy-initialize a singleton that requires resources, such as a database connection, especially if there is a chance that the containing application will not need the singleton in a particular session.”
Whether you lazy initialize or not, construction of the singleton takes place in the singleton’s class • And access to the singleton is provided through a public, static method
Singletons and Threads • CS 320 is not a pre-requisite for CS 304 • This subsection in the book depends on understanding concepts from CS 320, so it is not part of CS 304 • If you have had CS 320, the idea can be summarized pretty easily • If you are trying to maintain a singleton, then you have to synchronize calls to the method which constructs the singleton • Without synchronization, it would be possible for two different threads to each create an instance of a so-called singleton
Recognizing Singleton • In most applications you can generally have multiple instances of a given class • It may be the case that for a particular application, under certain conditions, only one instance of a given class is needed • Even in this case, the client code may be simple enough that there is simply one place where that instance is created, and there is no need to include the singleton machinery in the class
In reality, singletons are rare • The trick is learn how to identify that situation • For what it’s worth, I had a student once who said he had applied that pattern in an application • He was writing code in a networked environment where logging of events was handled by a singleton logger class • It was desirable for multiple, different pieces of networked client code to have a common log, not separate logs for each client (process)
Some More Observations on the Pattern • The authors say that Singleton is probably the best known pattern • This may be because it is easy to understand • It may also be because, for C style programmers, it makes it easy to get around the requirements of object-oriented programming • A singleton object can serve as a global type of variable
In general, the reason for the existence of a singleton is some restriction in the problem domain • A singleton shouldn’t be introduced unless a good reason can be found for having one • Client code, if at all possible, should have no interest in special conditions on the creation of instances of other classes (like singletons)
Note that if a class is to have subclasses or different versions, singleton won’t work at all • The restrictions on construction in the superclass will mess up construction in the subclasses
Another Example • It will be clear that the following example is not taken from some real business application. • It is given simply as a way of reiterating the idea of a singleton and how to accomplish it in code. • The code on the following overhead shows a class implemented using the singleton pattern using the coding technique, including lazy initialization, suggested by the book.
public class MonotheisticGod • { • private static MonotheisticGodoneGod; • private MonotheisticGod() • { • } • public static MonotheisticGodgetMonotheisticGod() • { • if(oneGod == null) • oneGod = new MonotheisticGod(); • return oneGod; • } • }
The code on the following overhead shows a call to obtain a reference to the foregoing singleton.
public class TestGod • { • public static void main(String[] args) • { • MonotheisticGodmyOneGod = MonotheisticGod.getMonotheisticGod(); • System.out.println(myOneGod); • } • }
UML for the Pattern • The singleton pattern is quite simple structurally • There is no one accepted UML diagram that illustrates it • You might include it or recognize it in a UML diagram by a detailed listing of the instance variables and methods of the class