Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 6
In order to identify the appropriate design pattern to use, you must first clearly identify the problem that you're trying to solve; that's where the problem element of the design pattern description is helpful. Choosing one design pattern over another also usually involves trade-offs that can impact an application's or system's flexibility and future maintenance. That's why it's important to understand the consequences of using a given design pattern before you begin implementing it.
Consider the task of designing a complex user interface using buttons, textfields, and other non-container components. The Composite design pattern regards containers as components, which lets us nest containers and their components (containers and non-containers) within other containers, and do so recursively. If we chose not to use the Composite pattern we would have to create many specialized non-container components (a single component combining a password textfield and a login button, for example), which is harder to achieve.
Having thought this through, we understand the problem we're trying to solve and the solution offered by the Composite pattern. But what are the consequences of using this pattern?
Using Composite means that your class hierarchies will mix container and non-container components. Simpler clients will treat container and non-container components uniformly. And it will be easier to introduce new kinds of components into the UI. Composite can also lead to overly generalized designs, making it harder to restrict the kinds of components that can be added to a container. Since you won't be able to rely on the compiler to enforce type constraints, you will have to use runtime type checks.
Runtime type checks involve if statements and the instanceof operator, which leads to brittle code. If you forget to update a runtime type check as your application requirements evolve,
you could subsequently introduce bugs.
It is also possible to choose an appropriate design pattern and use it incorrectly. The Double-Checked locking pattern is a classic example. Double-checked locking reduces lock acquisition overhead by first testing a locking criterion
without actually acquiring the lock, and then only acquiring the lock if the check indicates that locking is required. While
it looked good on paper, Double-checked locking in JDK 1.4 had some hidden complexities. When JDK 5 extended the semantics
of the volatile keyword, developers were finally able to reap the benefits of the Double-checked locking pattern.
David Geary's Java design patterns series is an excellent first stop for learning about some of the Gang of Four patterns mentioned in this article:
Allen Holub wrote about several important concurrency patterns and other design techniques for his Java toolbox series, "Programming Java in the real world":
Brian Goetz's two JavaWorld articles about double-checked locking are essential reading for anyone inclined to put too much faith in an out-of-the-box solution:
Design patterns are discussed in JavaWorld's Java 101, Java tips and Java Q&A series:
Some patterns have become more important with time:
Others have stood the test of time:
Additional resources for learning about design patterns: