Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ganesh_JavaSE7_Programming_1z0-804_study_guide.pdf
Скачиваний:
94
Добавлен:
02.02.2015
Размер:
5.88 Mб
Скачать

Chapter 5 ObjeCt-Oriented design prinCiples

types of Design patterns

broadly, goF design patterns can be classified into the following three categories:

Creational patterns offer the flexibility to decide who is responsible for object creation, how the object will be created, which object will be created, and when the creation will take place. in essence, creational patterns provide an abstraction for object instantiation.

examples: singleton, Factory, abstract Factory, and prototype.

Structural patterns are focused on how related classes (and objects) are composed together to form a larger structure.

examples: Composite, decorator, proxy, and Façade.

Behavioral patterns define the communication among the objects and control the flow within the participating objects.

examples: Mediator, Chain of responsibility, Observer, state, and strategy.

The Singleton Design Pattern

There are many situations in which you want to make sure that only one instance is present for a particular class. For example, assume that you defined a class that modifies registry, or you implemented a class that manages printer spooling, or you implemented a thread-pool manager class. In all these situations, you might want to avoid hard-to-find bugs by instantiating no more than one object of such classes. In these situations, you could employ the singleton design pattern.

The singleton design pattern is meant to ensure that only one instance of the class is created. The pattern implementation provides a single point of access to the class. This pattern is a creational design pattern, which means it controls instantiation of the object. In Java SDK, the pattern is used in many places, such as java.lang.Runtime.

Figure 5-1 shows the class diagram of the singleton pattern. It comprises a single class, the class that you want to make as a singleton. It has a private constructor and a static method to get the singleton object.

Singleton

-mySingleton : Singleton

- Singleton()

+ getSingleton() : Singleton

Figure 5-1. UML class diagram of singleton design pattern

the singleton design pattern offers two things: one and only one instance of the class, and a global single point of access to that object.

128

Chapter 5 Object-Oriented Design Principles

Assume that the FunPaint application requires a logger that you want to implement as a singleton. Listing 5-7 shows a possible implementation.

Listing 5-7.  Logger.java

//Logger class must be intantiated only once in the application; it is to ensure that the

//whole of the application makes use of that same logger instance

public class Logger {

//declare the constructor private to prevent clients

//from instantiating an object of this class directly

private Logger() {

}

 

 

public static Logger myInstance;

// by default, this field is initialized to null

// the static method to be

used by clients to get the instance of the Logger class

public static Logger getInstance() {

 

if(myInstance == null) {

 

// this is

the first time this method is called, and that's why myInstance

is null

 

 

 

myInstance

= new Logger();

}

 

 

 

// return the same

object reference any time and every time getInstance is called

return myInstance;

 

 

}

public void log(String s) {

// a trivial implementation of log where we pass the string to be logged to console System.err.println(s);

}

}

Look at the singleton implementation of the Logger class. The constructor of the class is declared as private, so you cannot simply create a new instance of the Logger class using the new operator. The only way to get an instance of this class is to call the static member method of the class via the getInstance() method. This method checks whether a Logger object already exists or not. If not, it creates a Logger instance and assigns it to the static member variable. In this way, whenever you call the getInstance() method, it will always return the same object of the Logger class.

Listing 5-7 used lazy initialization. You could employ early initialization if your singleton constructor is not computationally expensive.

Ensuring That Your Singleton Is Indeed a Singleton

You might be wondering what we are talking about, but it is really important (as well as really difficult) to ensure that your singleton pattern implementation allows only instance of the class.

For instance, the implementation provided in Listing 5-7 works only if your application is single-threaded. In the case of multiple threads, trying to get a singleton object may result in creation of multiple objects, which of course defeats the purpose of implementing a singleton.

Listing 5-8 shows a version of the Logger class that implements the singleton design pattern in a multi-threaded environment.

129

Chapter 5 Object-Oriented Design Principles

Listing 5-8.  Logger.java

public class Logger { private Logger() {

// private constructor to prevent direct instantiation

}

public static Logger myInstance;

public static synchronized Logger getInstance() { if(myInstance == null)

myInstance = new Logger(); return myInstance;

}

public void log(String s){ // log implementation

System.err.println(s);

}

}

Note the use of the keyword synchronized in this implementation. This keyword is a Java concurrency mechanism to allow only one thread at a time into the synchronized scope. You will learn more about this keyword in Chapter 13.

So, you made the whole method synchronized in order to make it accessible by only a thread at a time. This makes it a correct solution, but there is a problem: poor performance. You wanted to make this method synchronized only at the first time the method is called, but since you declared the whole method as synchronized, all subsequent calls to this method make it a performance bottleneck.

Okay, fine. What if you synchronize only the new statement? See Listing 5-9.

Listing 5-9.  Logger.java

public class Logger { private Logger() {

// private constructor

}

public static Logger myInstance; public static Logger getInstance() {

if(myInstance == null) { synchronized (Logger.class) {

myInstance = new Logger();

}

}

return myInstance;

}

public void log(String s) { // log implementation System.err.println(s);

}

}

It’s a nice try, but this solution does not work either. The synchronization does not prevent the accidental creation of two singleton objects. Now, implement the famous double-checked locking (see Listing 5-10).

130

Chapter 5 Object-Oriented Design Principles

Listing 5-10.  Logger.java

public class Logger { private Logger() {

// private constructor

}

public static Logger myInstance; public static Logger getInstance() {

if(myInstance == null) { synchronized (Logger.class) {

if(myInstance == null) { myInstance = new Logger();

}

}

}

return myInstance;

}

public void log(String s) { // log implementation System.err.println(s);

}

}

Well, this implementation is also not a foolproof solution for a multi-threaded application. It creates a problem due to erroneous out-of-order writes allowed by the Java memory model. Although the memory model problem was reportedly fixed in Java 5, we do not encourage you to use this solution.

Listing 5-11 shows another implementation of the Logger class that is based on the “initialization on demand holder” idiom. This idiom uses inner classes and does not use any synchronization construct (recall the discussion of inner classes in Chapter 4). It exploits the fact that inner classes are not loaded until they are referenced.

Listing 5-11.  Logger.java

public class Logger { private Logger() {

// private constructor

}

public static Logger myInstance; public static class LoggerHolder {

public static Logger logger = new Logger();

}

public static Logger getInstance() { return LoggerHolder.logger;

}

public void log(String s) { // log implementation System.err.println(s);

}

}

Hmm…at last you’ve found an efficient working solution. However, before we close this discussion of the singleton design pattern, two parting words of caution. First, use the singleton pattern wherever it is appropriate, but do not overuse it. Second, make sure that your singleton implementation ensures the creation of only one instance even if your code is multi-threaded.

131

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]