Design Patterns and OOP Principles


Introduction

Design patterns are tried-and-true design solutions to recurring problems in software construction.  They  provide ways to structure software components into systems that are flexible, extensible, and have a high degree of reusability, an elusive goal in software engineering.  OO design patterns comes very close to achieving this goal by making effective use of polymorphism, striking a balance between inheritance and composition to build objects.  In this course, we focus only on OO design patterns.  So far, we have seen the following patterns.

We now go back to these patterns and discuss them in more details from the perspective of the following object-oriented programming principles (OOPP).


I. The Union Pattern

Suppose I am faced with the problem of computing the areas of geometrical shapes such as rectangles and circles.  OOPP #0 suggests that I build objects that are capable of computing these areas.  The variants for this problems are the infinitely many shapes: rectangles, circles, etc.  OOPP #1 drives me to define concrete classes such as Rectangle and Circle, and make them subclasses of an abstract class, called AShape, which has the abstract capability of computing its area.  This is an example of the simplest yet most fundamental OO design pattern called the Union Pattern.  It is the result of applying OOPP #0 and OOPP #1.

wpeE.gif (5556 bytes)

The Union Pattern is the result of partitioning the sets of objects in the problem domain into disjoint subsets and consists of

A client of the Union Pattern uses instances of the concrete subclasses (Variant1, Variant2), but should only see them as AClass objects.   The client class code should only concern itself with the public methods of AClass and should not need to check for the class type of the concrete instances it is working with.  Conditional statements to distinguish the various cases are gone resulting in reduced code complexity, making the code easier to maintain.

II. The Strategy Pattern

Going back to the Pizza problem, we see that the Pizza has a shape and delegates the computation of its area to its shape.  It does not care what the exact type of its shape is.  It only knows that its shape is capable of computing the appropriate area.    This is an example of what is called the Strategy Pattern.  The Pizza uses its shape as a "strategy" to compute its area.

wpeF.gif (6773 bytes)

In general, the strategy pattern consists of a union pattern of strategies, and a client class, called the context, that contains a reference to the abstract strategy in the union. The context delegates all works to this stategy reference.  In our Pizza example, the context is the Pizza class, and the abstract AShape plays the role of the (abstract) strategy.   In questions #3 and 4 of homework #2, ABoolExp and its concrete subclasses can be thought of as strategies for BoolExpRef to evaluate itself.  Whenever a client requests a BoolExpRef to evaluate, it forwards the call to its ABoolExp reference, the strategy.  All the work for evaluation is carried out by the current instance of ABoolExp.

 

III. The Composite Pattern

More that often, we combine (or compose) objects to form new objects.  Recursive composition, in particular, is a common object design.  FunList and FunBiTree are such examples.  The recusive object structural design gives rise to recursive algorithms on the object.  This design pattern is called the Composite Pattern.

wpe10.gif (7317 bytes)

In the above, classes Basic1 and Basic2 correspond to the base cases in the recursion, and Composite corresponds to the non-base cases.  The method operation () for Composite is mostly recursive.  Class ABooleanExpression in homework # 2 is another example of the composite pattern.  Class BooExpRef and class ABoolExp form a combination of the strategy and composite pattern.

IV. The Singleton Pattern

Class Empty represents the "empty list".  Conceptually, there is only one empty list in this world.  The concept is akin to that of the empty set: there is only one empty set.  How can we ensure that only one instance of Empty can be created throughout the life of a program?  There is away to design a class to ensure such uniqueness property.  It is called the Singleton Design Pattern.  The following UML diagram describe the pattern.

 

wpe1.gif (4966 bytes)

Note: The field _instance and the method UniqueInstance () are of class scope (i.e. static).

The method UniqueInstance () is called a "factory" method as it is used to manufacture an instance, though unique, of the SingletonClass.   The class SingletonClass is appropriately called a "factory".   In this very special case, SingletonClass manufactures its own (unique) instance.  We will revisit the "factory pattern" in future lectures.

 

IV. The Template Method Pattern

Let's review the method toString () of the class FunList in lab tutorial #2.

public abstract class FunList {

  final public String toString() {
    return "(" + toStringHelp() + " )";
  }

  abstract String toStringHelp();

  // Other methods not listed.
}


The toString () method, as shown, is NOT abstract.   Class FunList defines toString () in terms of  toStringHelp (), an abstract method.  It is up to all future subclasses of FunList to concretely define what toStringHelp ()is supposed to do.  The method toString ()represents what we call an "invariant" behavior for FunList.    The "variant" in this case is the toStringHelp () method.  It is the responsibility of all the variants (i.e. subclasses) of FunList to do the actual work in toStringHelp ().  The method  toString () is an example of the "Template Method Pattern".  A "template method" is a method that  makes calls to at least one abstract method in its own class.  It serves to define a fixed algorithm that all future subclasses must follow. 

In Java, I prefer to specify template methods with the key word final.   Roughly speaking, the key word final means "whatever is defined as final cannot be changed".  A final class is a class that cannot be extended.  A final method is a method that cannot be overridden by any of the subclasses.  A final field is a field that, once initialized, cannot be modified.

The following is an UML diagram describing the template method pattern.

template.png (6408 bytes)

V. The Interpretor Pattern

See lab tutorial #3.

dxnguyen@cs.rice.edu
Copyright 1999, Dung X. Nguyen - All rights reserved.