C SC 225 Lecture 7: Decorator and Singleton Patterns
[ previous
| schedule
| next ]
The Decorator pattern
- Use this pattern to define a new class that adds functionality (decoration) to an existing class while
implementing the same interface
- The new class can then be used anywhere the original can, and client gets the added value
- The thing being decorated does not know it is being decorated
- This pattern allows the decoration to occur at runtime - very flexible
- This pattern is an example of "favor composition over inheritance"
- This is a Structural Pattern
- Classic example in Swing is JScrollPane.
- It extends Component and has a Component field.
- Adds scrolling capability to its field (displays it with scrollbars when specified)
- Can be used anywhere its field can -- it can be added to container such as JPanel or content pane
- Its paint method will invoke its field's paint method and scrolling (the added value) is applied.
- The field does not know it is being decorated!
- Classic example in java.io is I/O streams
- For instance, Reader implements Readable but only reads characters
- BufferedReader also implements Readable and has a Reader field
- BufferedReader decorates Reader by adding buffering capabilities to whatever stream is being read
- More useful example: DataInputStream adds ability to read
all Java primitive types to InputStream, which reads only characters
- These are just two examples among many in java.io
- The scroll pane decorator is more of an "add-on"
- The I/O stream decorator is more of a "filter"
- Both Component and InputStream are abstract classes, but pattern works with common interface too
The Singleton pattern
- "The one to have when you're having only one" (paraphrased from Schaefer beer slogan)
- Use this if you want to provide exactly one, or at most one, object from a class.
- Why might you want to limit your clients to only one instance?
- This is a Creational Pattern
- The logistics are interesting, as you can see from this Q & A
- Q: Only one object? How do you limit clients to calling your constructor only once?
A: You can't!
- Q: Then how do you assure only one instance is created?
A: Create it yourself!
- Q: If you don't allow clients to create it using a constructor, how do you create it?
A: A private constructor!
- Q: But doesn't Java provide a default no-argument constructor? How can you keep clients from calling it?
A: If you define any constructor, even a private one, Java will not provide the default constructor.
- Q: If the constructor is private and you create the instance yourself, how do clients get the instance?
A: Provide them an accessor, a public getInstance() method
- Q: Is this a chicken and egg situation? How can clients call getInstance() if they don't have one to call it through?
A: Right. The getInstance() method also has to be static, so clients can call it through the class.
- Q: When do you create the instance?
A: There are two schools of thought. Under lazy instantiation, it is not created until the first call to getInstance().
Under eager instantiation, it is created during the JVM's initialization phase, before program execution begins at main()
- Q: What's the advantage of lazy instantiation?
A: You tell me.
- Q: Then why would you ever use eager instantiation?
A: Because it is guaranteed to be thread-safe; lazy instantiation has to be carefully programmed to guarantee this.
- Q: What does "thread-safe" mean?
A: We'll cover threads later in the course. There's a quick intro below but for now you can think of
it as a guarantee that memory contents will not be corrupted when multiple instances of a program
are using them at the same time.
- Quick introduction to Threads (more on these, later...)
- A thread of execution is one program executing instructions under the control of a CPU's program counter.
- It is possible for multiple threads to run
concurrently -- only one is executing at any given instant but the JVM periodically
switches control from one to another.
- But the threads all share the same memory so memory contents can be corrupted
if control is switched during a memory operation.
- A program that assures memory will not be corrupted
by threads is said to be "thread-safe".
- All Java execution is performed by threads. The main thread starts running at main().
- Additional threads are created for event-handling and housekeeping when you run a GUI-based program.
- You can also explicitly define, create and run your own threads.
- Example of a singleton class, with eager instantiation
public class OneOfAKind {
private static OneOfAKind uniqueInstance = new OneOfAKind ();
private OneOfAKind () {
// initialization here
}
public static OneOfAKind getInstance() {
return uniqueInstance;
}
// define fields and methods
}
- Example of a singleton class, with thread-safe lazy instantiation. By declaring getInstance()
to be synchronized, only one thread can use it at a time.
public class OneOfAKind {
private static OneOfAKind uniqueInstance = null;
private OneOfAKind () {
// initialization here
}
public static synchronized OneOfAKind getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new OneOfAKind();
}
return uniqueInstance;
}
// define fields and methods
}
- Maximizing runtime efficiency, even with lazy instantiation.
- Synchronized methods result in execution time overhead.
- The synchronized method may be called many times, but synchronization is only needed
the first time. Why?)
- The Head First book shows a technique that results in synchronization occurring
only once.
- This technique combines use of a volatile variable and a synchronized block,
but may not work unless you are using Java 1.5 or later.
- Declaring a variable volatile assures that reads and writes will occur "atomically" (e.g.
thread-safe). But there are limits to this atomicity. For instance, applying the ++ increment
operator to a volatile variable is not thread-safe, because incrementing requires both a read (load) and a write (store)
operation on the variable. This prevents me from using a solution that avoids synchronized
altogether by collapsing the IF statement in getInstance() to a conditional expression as follows.
// NOT thread-safe even when uniqueInstance is declared volatile
public static OneOfAKind getInstance() {
return (uniqueInstance == null) ? uniqueInstance = new OneOfAKind() : uniqueInstance;
}
- I'll end with a reminder not to use the code just above this line.
[ C
SC 225 | Peter
Sanderson | Math Sciences server
| Math Sciences home page
| Otterbein ]
Last updated:
Peter Sanderson (PSanderson@otterbein.edu)