Ever wondered what design patterns are? In this article, I’ll explain why design patterns are important, and will provide some examples, in Java, of when and why they should be used.
What are Design Patterns?
Design patterns are optimized, reusable solutions to the programming problems that we encounter every day. A design pattern is not a class or a library that we can simply plug into our system; it’s much more than that. It is a template that has to be implemented in the correct situation. It’s not language-specific either. A good design pattern should be implementable in most—if not all—languages, depending on the capabilities of the language. Most importantly, any design pattern can be a double-edged sword— if implemented in the wrong place, it can be disastrous and create many problems for you. However, implemented in the right place, at the right time, it can be your savior.
There are three basic kinds of design patterns:
- Structural
- Creational
- Behavioral
Structural patterns generally deal with relationships between entities, making it easier for these entities to work together.
Creational patterns provide instantiation mechanisms, making it easier to create objects in a way that suits the situation.
Behavioral patterns are used in communications between entities and make it easier and more flexible for these entities to communicate.
Why should we use them?
Design patterns are, by principle, well-thought out solutions to programming problems. Many programmers have encountered these problems before, and have used these ‘solutions’ to remedy them. If you encounter these problems, why recreate a solution when you can use an already proven answer?
Example
class StrategyExample {
public static void main(String[] args) {
Context context;
// Two contexts following different strategies
context = new Context(new ConcreteStrategyAdd());
int resultA = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategySubtract());
int resultB = context.executeStrategy(3,4);
}
}
// The classes that implement a concrete strategy should implement this
// The context class uses this to call the concrete strategy
interface Strategy {
int execute(int a, int b);
}
// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyAdd's execute()");
return a + b; // Do an addition with a and b
}
}
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategySubtract's execute()");
return a - b; // Do a subtraction with a and b
}
}
// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {
private Strategy strategy;
// Constructor
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
Pretty simple, right? Now, let’s take a closer look at the strategy pattern.
Strategy Pattern
The strategy pattern is a behavioral design pattern that allows you to decide which course of action a program should take, based on a specific context during runtime. You encapsulate two different algorithms inside two classes, and decide at runtime which strategy you want to go with.
In our example above, the strategy is based on whatever the context variable was at the time the class was instantiated. If you give it the context for ConcreteStrategyAdd, it will use ConcreteStrategyAdd, and vice versa.
Cute, but where can I use this?
Imagine that you’re currently developing a class which can either update or create a new user record. It still needs the same inputs (name, address, mobile number, etc.), but, depending on a given situation, it has to use different functions when updating and creating. Now, you could probably just use an if-else to accomplish this, however, what if you need to use this class in a different place? In that case, you’ll have to rewrite the same if-else statement all over again. Wouldn’t it be easier to just specify your context?
Class User {
public void createOrUpdate(String name, String address, String mobile, String userId)
{
if(userId == null) {
// it means the user doesn't exist yet, create a new record
} else {
// it means the user already exists, just update based on the given userid
}
}
}
Now, the “usual” strategy pattern involves encapsulating your algorithms inside another class, but in this case, another class would be wasteful. Remember that you don’t have to follow the template exactly. Variations work as long as the concept remains the same, and it solves the problem.
Adapter Pattern
The adapter pattern is a structural design pattern that allows you to repurpose a class with a different interface, allowing it to be used by a system which uses different calling methods.
This also lets you alter some of the inputs being received from the client class, making it into something compatible with the adaptee’s functions.
How can I use this?
Another term to reference an adapter class is a wrapper, which basically lets you “wrap” actions into a class and reuse these actions in the correct situations. A classic example might be when you’re creating a domain class for table classes. Instead of calling the different table classes and calling up their functions one by one, you could encapsulate all of these methods into one method using an adapter class. This would not only allow you to reuse whatever action you want, it also keeps you from having to rewrite the code if you need to use the same action in a different place.
Compare these two implementations:
Non-Adapter Approach
User user = new User();
user.createOrUpdate( //inputs );
Profile profile = new Profile();
profile.createOrUpdate( //inputs );
If we needed to do this again in a different place, or even reuse this code in a different project, we would have to type everything all over again.
Better
That’s opposed to doing something like this:
Account account = new Account();
account.createNew( //inputs );
In this situation, we have a wrapper class, which would be our Account class:
class Account()
{
public void createNew( //inputs )
User user = new User();
user.createOrUpdate( //subset of inputs );
Profile profile = new Profile();
profile.createOrUpdate( //subset of inputs );
}
}
This way, you can use your Account again whenever you need it—plus, you’d be able to wrap other classes under your account class as well.
Factory Method Pattern
The factory method pattern is a creational design pattern which does exactly as it sounds: it’s a class that acts as a factory of object instances.
The main goal of this pattern is to encapsulate the creational procedure that may span different classes into one single function. By providing the correct context to the factory method, it will be able to return the correct object.
When can I use this?
The best time to use the factory method pattern is when you have multiple different variations of a single entity. Let’s say you have a button class; this class has different variations, such as ImageButton, InputButton and FlashButton. Depending on the place, you may need to create different buttons—this is where you can use a factory to create the buttons for you!
Let’s begin by creating our three classes:
Interface Button {
public String getHtml();
}
Class ImageButton implements Button {
public String getHtml() {
return "..."; //This should be whatever HTML you want for your image-based button
}
}
Class InputButton implements Button {
public String getHtml() {
return "..."; //This should be whatever HTML you want for your normal button (<input type="button"... />);
}
}
Class FlashButton implements Button {
public String getHtml() {
return "..."; //This should be whatever HTML you want for your flash-based button
}
}
Now, we can create our factory class:
Class ButtonFactory
{
public static Button createButton(String type)
{
try
{
Class buttonClass = Class.forName(type);
java.lang.reflect.Constructor cons = buttonClass.getConstructor(classParm);
return cons.newInstance(objectParm);
} catch(Exception x) {
System.out.println("The button type " + type + " is not recognized.");
}
return null;
}
}
We can use this code like so:
String[] buttons = {"ImageButton", "InputButton", "FlashButton"};
for(int i=0; i<buttons.length; i++) {
ButtonFactory.createButton(buttons[i]).getHtml();
}
The output should be the HTML of all your button types. This way, you would be able to specify which button to create depending on the situation and reuse the condition as well.
Decorator Pattern
The decorator pattern is a structural design pattern which enables us to add new or additional behavior to an object during runtime, depending on the situation.
The goal is to make it so that the extended functions can be applied to one specific instance, and, at the same time, still be able to create an original instance that doesn’t have the new functions. It also allows for combining multiple decorators for one instance, so that you’re not stuck with one decorator for each instance. This pattern is an alternative to subclassing, which refers to creating a class that inherits functionality from a parent class. As opposed to subclassing, which adds the behavior at compile time, “decorating” allows you to add new behavior during runtime, if the situation calls for it.
To implement the decorator pattern, we can follow these steps:
- Subclass the original “Component” class into a “Decorator” class
- In the Decorator class, add a Component pointer as a field
- Pass a Component to the Decorator constructor to initialize the Component pointer
- In the Decorator class, redirect all “Component” methods to the “Component” pointer, and
- In the Decorator class, override any Component method(s) whose behavior needs to be modified
Steps courtesy of http://en.wikipedia.org/wiki/Decorator_pattern
When can I use this?
The best place to use the decorator pattern is when you have an entity which needs to have new behavior only if the situation requires it. Once we’ve established our decorations, we can start programming them.
The following example illustrates the use of decorators using coffee making scenario. In this example, the scenario only includes cost and ingredients.
// The Coffee Interface defines the functionality of Coffee implemented by decorator
public interface Coffee {
public double getCost(); // returns the cost of the coffee
public String getIngredients(); // returns the ingredients of the coffee
}
// implementation of a simple coffee without any extra ingredients
public class SimpleCoffee implements Coffee {
public double getCost() {
return 1;
}
public String getIngredients() {
return "Coffee";
}
}
The following classes contain the decorators for all Coffee classes, including the decorator classes themselves:
// abstract decorator class - note that it implements Coffee interface
abstract public class CoffeeDecorator implements Coffee {
protected final Coffee decoratedCoffee;
protected String ingredientSeparator = ", ";
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
public double getCost() { // implementing methods of the interface
return decoratedCoffee.getCost();
}
public String getIngredients() {
return decoratedCoffee.getIngredients();
}
}
// Decorator Milk that mixes milk with coffee. Note it extends CoffeeDecorator
public class Milk extends CoffeeDecorator {
public Milk(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
public double getCost() { // overriding methods defined in the abstract superclass
return super.getCost() + 0.5;
}
public String getIngredients() {
return super.getIngredients() + ingredientSeparator + "Milk";
}
}
// Decorator Whip that mixes whip with coffee. Note it extends CoffeeDecorator
public class Whip extends CoffeeDecorator {
public Whip(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
public double getCost() {
return super.getCost() + 0.7;
}
public String getIngredients() {
return super.getIngredients() + ingredientSeparator + "Whip";
}
}
// Decorator Sprinkles that mixes sprinkles with coffee. Note it extends CoffeeDecorator
public class Sprinkles extends CoffeeDecorator {
public Sprinkles(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
public double getCost() {
return super.getCost() + 0.2;
}
public String getIngredients() {
return super.getIngredients() + ingredientSeparator + "Sprinkles";
}
}
Here’s a test program that creates a Coffee instance which is fully decorated (i.e., with milk, whip, sprinkles), and calculate cost of coffee and prints its ingredients:
public class DecoratorTest
{
public static void main(String[] args)
{
Coffee c = new SimpleCoffee();
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
c = new Milk(c);
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
c = new Sprinkles(c);
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
c = new Whip(c);
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
// Note that you can also stack more than one decorator of the same type
c = new Sprinkles(c);
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
}
}
The output of this program is given below:
Cost: 1.0; Ingredients: Coffee
Cost: 1.5; Ingredients: Coffee, Milk
Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles
Cost: 2.4; Ingredients: Coffee, Milk, Sprinkles, Whip
Cost: 2.6; Ingredients: Coffee, Milk, Sprinkles, Whip, Sprinkles
Example courtesy Wikipedia decorator pattern example
Singleton Pattern
The singleton design pattern is a creational design pattern which makes sure that you have one single instance of a particular class in the duration of your runtime, and provides a global point of access to the single instance.
This makes it easier to set up a point of “coordination” for other objects that use the singleton instance as well, since the singleton’s variables will always be the same for anything that calls it.
When can I use this?
This solution is thread-safe without requiring special language constructs, but it may lack the laziness of the one below. The INSTANCE is created as soon as the Singleton class is initialized. That might even be long before getInstance() is called. It might be (for example) when some static method of the class is used. If laziness is not needed or the instance needs to be created early in the application’s execution, or your class has no other static members or methods that could prompt early initialization (and thus creation of the instance), this (slightly) simpler solution can be used:
public class MySingleton {
private static final MySingleton INSTANCE = new Singleton();
// Private constructor prevents instantiation from other classes
private MySingleton() {
}
public static MySingleton getInstance() {
return INSTANCE;
}
}
By doing this, we can access our MySingleton instance from different parts of our code, even in different classes. This data will persist throughout all getInstance calls.
Conclusion
There are many more design patterns to study; in this article, I’ve only highlighted some of the more prominent ones that I use when programming. If you’re interested in reading about the other design patterns, Wikipedia’s Design Patterns page has a plethora of information. If that’s not enough, you can always check out Design Patterns: Elements of Reusable Object-Oriented Software, which is considered to be one of the best design pattern books available.
One last thing: when you use these design patterns, always make sure that you’re trying to solve the correct problem. As I mentioned previously, these design patterns are a double-edge sword: if used in the wrong context, they can potentially makes things worse; but if used correctly, they become indispensable.
Post inspired from: http://net.tutsplus.com/articles/general/a-beginners-guide-to-design-patterns/
I’ll highly appreciate your feedback on this article.