Designing Software  «Prev  Next»
Lesson 5Combining design patterns
Objective Explore how multiple design patterns can be used in a single class or program.

Combining Design Patterns

We have seen throughout this course that a program may and often does use more than one pattern. Two variations of this are listed below.

Classes and Objects may have Multiple Roles

Although individual classes and objects have roles as participants in a pattern, they often have other roles as well. The Vehicle class in the traffic simulation participates in the Flyweight pattern as the abstract Flyweight class. It has certain responsibilities as that participant. It also has other responsibilities (like maintaining a length and a maximum speed) that are part of the specific simulation and not part of the Flyweight pattern.

Patterns using other Patterns

Patterns may also be used together, one pattern forming part of another. For example, a Mediator may use the Observer pattern to communicate with its colleagues. In fact, it may even use two Observers. The first Observer notifies the Mediator of changes to any of its colleagues. The second Observer notifies all the colleagues of changes to the Mediator.

Lazy Initialization

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. This is typically accomplished by maintaining a flag indicating whether the process has taken place. Each time the desired object is summoned, the flag is tested. If it is ready, it is returned. If not, it is initialized on the spot. See lazy evaluation for a general treatment of this idea. In heavily imperative languages this pattern carries hidden dangers, as does any programming habit that relies on shared state.

The "lazy factory"

In a software design pattern view, lazy initialization is often used together with a factory method pattern. This combines three ideas:
  1. using a factory method to get instances of a class (factory method pattern)
  2. storing the instances in a map, so you get the same instance the next time you ask for an instance with same parameter (Multiton pattern, similar to the singleton pattern)
  3. using lazy initialization to instantiate the object the first time it is requested (lazy initialization pattern).

Java

Here is an example in Java.
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public enum FRUIT_TYPE {
 NONE,
 APPLE,
 BANANA,
}
class Program {
/** @param args */
 public static void main(String[] args) {
  Fruit.getFruitByTypeName(FRUIT_TYPE.BANANA);
  Fruit.showAll();
  Fruit.getFruitByTypeName(FRUIT_TYPE.APPLE);
  Fruit.showAll();
  Fruit.getFruitByTypeName(FRUIT_TYPE.BANANA);
  Fruit.showAll();
 }
}

Using a private constructor to force the use of the factory method.
@param type

Lazy Factory method, gets the Fruit instance associated with a certain type. Instantiates new ones as needed.
@param type Any allowed fruit type, e.g. APPLE
@return The Fruit instance associated with that type.

public class Fruit {
 private static Map<FRUIT_TYPE, Fruit> types = 
 new HashMap<FRUIT_TYPE, Fruit>();
 private Fruit(FRUIT_TYPE type) {}
 public static Fruit getFruitByTypeName(FRUIT_TYPE type) {
  Fruit fruit;
  if (!types.containsKey(type)) {// Lazy initialisation
   fruit = new Fruit(type);
   types.put(type, fruit);
  } else { // OK, it's available currently
     fruit = types.get(type);
  }
  return fruit;
}

Lazy Factory method, gets the Fruit instance associated with a certain type. Instantiates new ones as needed. Uses double-checked locking pattern for using in highly concurrent environments.
@param type Any allowed fruit type, e.g. APPLE
@return The Fruit instance associated with that type. 

Line 1 below: Check again, after having acquired the lock to make sure the instance was not created meanwhile by another thread
public static Fruit getFruitByTypeNameHighConcurrentVersion(FRUIT_TYPE type){
 if (!types.containsKey(type)) {
  synchronized (types) { /* line 1*/
   if (!types.containsKey(type)) {// Lazy initialisation
    types.put(type, new Fruit(type));
   }
  }
 }
 return types.get(type);
}

Displays all entered fruits
public static void showAll() {
 if (types.size() > 0){
  System.out.println("Number of instances made = " + types.size());
  for (Entry<FRUIT_TYPE, Fruit> entry : types.entrySet()){
   System.out.println(Constants.firstLetterToUpper(entry.getKey().toString()));
  }
  System.out.println();
  }
 }
}

Designing Design Patterns - Quiz

Now it is time to take a short quiz.
Designing Design Patterns - Quiz