Software Patterns

Introduced around 1995 by "gang of four": Design Patterns by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.

Organizing your code (the right classes and the right code in those classes) is a lifelong learning experience and it is very difficult for anything but the most simple programs. For example, where should the code for operation "get email for user" go? It could reasonably be placed either "functionally" in an EmailManager or "logically" in a User object. I often compromise by placing things by functionality such as in the EmailManager and then creating a delegating function such as User.getEmail() that just calls the EmailManager. I can therefore satisfy grouping by both functionality and by logic.

Experience with partitioning and organizing a program is particularly important when working with others on a large program. It is specifically the code organization that supports code reuse, making your code smell better and last longer. Using established patterns not only provides solutions you may not have thought of, but they are usually reusable out of the box.

Patterns are essentially just formalisms and the naming of nice idioms or patterns of coding. Can also formalize design tactics or strategies. A way to give experience to programmers (junior and senior). A nice way to know you're talking about the same thing. Provides high bandwidth conversation. Most common patterns I've heard are visitor, iterator, delegate, factory, proxy. The "gang of four" likens patterns to plot patterns in books such as "tragically-flawed hero".

Some people go nuts with these things and say "heh, you're not using pattern blort." Most of the time you have a pattern in your head from prior experience and it just happens to fit in a particular situation. Many people read "gang of four" book repeatedly hoping to gain the wisdom of ages. I suspect that simply getting more coding experience and learning patterns in a specific situation from a more experienced developer will yield the best results. I recommend against designing a program by sticking in patterns. Patterns are not meant to be straightjackets.

To this day, I am learning new programming patterns. I just don't try to look them up in a book so I can find its name; the exact variant may not actually have a name. Design the program as you think about it, but recognize patterns when you see them.

Remember that empirically, we don't know the answer yet to building large software so any claims that patterns or XP or UML or whatever are the answer are suspect. Also note that I programmed for 15 years before the patterns book came out without the need for calling a visitor a visitor. William Shakespeare had it right:

"What's in a name? That which we call a rose / By any other name would smell as sweet."

This lecture just lists the various popular patterns.

A small beginning

Here is the first pattern I learned by watching another person code. Instead of

if ( cond ) {
  x = 3;
}
else {
  x = 4;
}

use

x = 4;
if ( cond ) {
  x = 3;
}

Depending on the CPU architecture / language / compiler, this might be faster or slower. Regardless, I thought it was "tighter"

Visitor

http://128.42.6.89/JavaResources/DesignPatterns/VisitorPattern.htm:

"The purpose of the Visitor Pattern is to encapsulate an operation that you want to perform on the elements of a data structure. In this way, you can change the operation being performed on a structure without the need of changing the classes of the elements that you are operating on. Using a Visitor pattern allows you to decouple the classes for the data structure and the algorithms used upon them."

http://en.wikipedia.org/wiki/Visitor_pattern

If I'm walking a tree, I would provide an object to the tree walker that knew to call a method per tree node like an event call back.

This is also useful for grammars. I pass in a Behavior object to the grammar and then actions in the grammar call Behavior.defineVariable(...) etc...

Visitors are not so great at providing context information; that is, visitors usually apply an action to a single element, node, or atom of the structure without concern for surrounding elements. For example, imagine you have a tree data structure representing programming language expressions like this:

  *
 / \
0   +
   / \
  3   4

A visitor pattern that does the addition should not bother as the surrounding context indicates the result must be 0 because of the multiplication by 0 around it.

Iterator

A form of visitor that walks a data structure, retrieving the elements one by one. In Java, you will see a lot of code like this:

List users = new LinkedList();
for (Iterator it = users.iterator(); it.hasNext();) {
    User u = (User) it.next();
    ...
}

Factory

OO languages have great flexibility at the method call level (polymorphism). You can refer to several types at once and send a "message." At the creation side, however, you have to say new T() where T is a specific type. Often a factory is useful so you ask for a new something but the factory decides precisely what type. For example, it could start using a debugging object:

class UserFactory {
  public static User create() {
    if ( debug ) {
      return new DEBUG_User();
    }
    else {
      return new User();
    }
  }
  ...
}

Then at the reference site you say:

User u = UserFactory.create(); // either User or DEBUG_User returned

This lets the reference site stay the same, but does not let you easily install a new factory so completely new User object creation functionality can be switched in. For that, see the next section.

Switch

The factory shown in previous pattern uses a switch (a "knob") to return different objects. The knob is the "debug" variable and the IF-statement.

Adding a level of indirection is a very common computer science trick. Putting in a switch between a reference site and a service provides lots of flexibility to swap out / in new functionality w/o changing your reference site.

Delegation

Delegation is like inheritance done manually (because Java does not formalize the notion). Delegation is the idea that an object answers a message but actually defers or delegates that functionality to a contained or referenced object. This is very common. For example,

class Person {
  PersonManager pm = ...;
  public int getMembership() {
    return pm.getMembership(this.getID());
  }
  ...
}

The servlet PageDispatcher we discussed is an example of a delegator also because it delegates all functionality for a URL to a Page object. The servlet is only the glue that delegates execution from the web server to your application code.

Why do we care about delegation? Sometimes the relationship you want between pieces of data is one of containment not inheritance; combining into a single aggregate with a nice, well-defined interface is useful; you just delegate the functionality to other objects.

Delegates allow you to group code in multiple ways. For example, in the above a Person should really be able to answer questions about it's membership, but do you really want all that complicated membership stuff in the Person object? Probably not--it is better to have the Person object delegate the functionality to a proper service object, which groups all membership related functionality.

Proxy

The factory pattern above creates normal User objects or debugging objects, DEBUG_User according to a debugging flag. You call the DEBUG_User object a proxy (in this case a debugging proxy).

More commonly you will see remote proxies where, for example, a database manager is actually a proxy that communicates with a remote database.

You can add a caching proxy that provides a buffer between you and a slow resource like a disk drive. The BufferedStream is an example of a caching proxy. It looks like a stream, but adds a cache.

A proxy is like a "tunnel". For example, HTTP proxies are used by large companies to filter web access. Users within the intranet set up their webservers to use a proxy. When they access a URL, it actually first goes to the proxy which checks to see if the URL is valid. If so, the proxy gets the data and then passes it back to the browser.

Let's look at a specific example of a proxy. Imagine you have a program that fires "events" when things of interest occur. You can set up an interface that is a "listener" such as this:

interface DebugEventListener {
    public void outOfMemory();
    public void launchOfNewThread(Thread t);
    public void login(User u);
}

Then your program can create a listener:

class MyMainApplication {
   class Tracer implements DebugEventListener {
       public void outOfMemory() {...}
       public void launchOfNewThread(Thread t) {...}
       public void login(User u) {...}
   }

   public static DebugEventListener listener = new Tracer();

   void startup() {...}

   void loginUser(User u) {
       if (...) listener.login(u);
   }
}

What if you want to remotely track events? No problem. Change

   public static DebugEventListener listener = new Trace();

to create a new kind of listener that marshalls events across a socket and the foo() method will not have to change at all!

   public static DebugEventListener listener = new RemoteSocketTrace();

The listener is now a remote socket proxy.

Adaptor

An adaptor makes one object look like another. It's like a proxy that alters the interface. The adaptor usually sits between a library that makes predefined method calls (such as callbacks from a GUI library) and user code that must answer those calls. Rather than structure your user code the way the library needs, structure the code the way you want and make an adaptor that routes the library method calls to your user code.

Adaptors are particularly useful when you need to make two pieces of software work together that are already written and cannot be changed.

Cache

Here is a simple cache design that only loads a User object once from the database. Is a write-through cache in the sense that addUser writes to both the cache and the database.

class DBMgr {
    Map<Integer,User> users = new HashMap<Integer,User>();
    public User getUser(int ID) {
        User u = users.get(ID);
        if ( u!=null ) return u;
        ... load user ID from db, put in u ...
        users.put(ID, u);
        return u;
    }
    public int addUser(User u) { // returns ID
        users.put(ID, u); // add (replace if there already)
        ... insert user into DB, get autoinc ID back ...
        return ID;
    }
}

Singleton

When you have a collection of related methods, you can stuff them into a "module", making them static.

public class MembershipManager {
    public static int getMembership(int ID) {...}
    ...
}

Useful to encapsulate. However, it makes it hard to swap out that functionality since everything references MembershipManager.getMembership(...). Plus sometimes you need shared data between methods.

The singleton pattern is useful for this. Then you do:

MembershipManager mm = MembershipManager.instance();
int m = mm.getMembership(...);

Naturally you really need an interface if you plan on swapping out functionality.

public class MembershipManager {
    private static MembershipManager _instance = null;

    public static synchronized MembershipManager instance() throws Exception {
        if ( _instance == null ) {
            _instance = new MembershipManager();
        }
        return _instance;
    }

    /** Only this class can make new instance and it does it once. */
    private MembershipManager() {
    }

    /** non-static! */
    public int getMembership(int ID) {...}

    ...
}

Command or operation

Often you need to record a sequence of operations so that you can undo them like a database supporting transactions. GUIs often queue up commands in an event queue that tell listeners what to do.

I recently used this pattern for operations on a buffer of strings. This buffer needed to be an ArrayList for speedy access to the ith element. However, after the buffer was created, I needed to insert strings and delete sequences of strings from say i to element j. Insert into an array is very expensive because you must move data and possibly reallocated the buffer. By using a queue of commands instead of doing the actual operations, I was able to avoid the intensive work. I just spit out the buffer later paying attention to the operations. For example, to insert "foo" after string index 44, I simply recorded the operation and then, when printing, I add "foo" after printing the string at index 44.

Tree Model

I'm not sure what people call this, but sometimes you need to walk arbitrary data structures in a tree-like fashion. Swing tree views use a TreeModel. Somehow you need to map walking instructions like node.getChild(i) onto another data structure that may not actually be a tree. You might have a simple list that you want to walk like a flat tree. Rather than force all other data structures to conform to one of your interfaces or make a parallel tree that points into the nodes of the other data structure, we need a simple object that translates navigation instructions onto the other data structure. This way, the code that walks in tree-fashion has a consistent interface to employ. For example, imagine you want to walk trees like node.getChild(i) but the tree implementation someone gives you has different method name or, worse, has a child-sibling representation forcing you to do getNextSibling() i times to get the ith child.

The solution is to define a Tree interface for walking:

public interface Tree {
    Object getChild(Object parent, int index);
    int getChildCount(Object parent);
    boolean isLeaf(Object node);
}

Then your walker can say:

tree.getChild(node, i);

instead of

node.getChild(i);

which forces a particular interface on the nodes. If your tree nodes look like:

public class TreeNode {
    List children = ...;
    public TreeNode getMyChild(int i) {
        return (TreeNode)children.elementAt(i);
    }
    ...
}

then you can build a Tree like this:

public class MyTree implements Tree {
    TreeNode root;
    public MyTree(TreeNode root) { this.root = root; }
    Object getChild(Object parent, int index) {
        TreeNode p = (TreeNode)parent;
        return p.getMyChild(i);
    }
    ...
}

To walk a non-tree structure like a list might have a Tree like this:

public class ListToTreeConverter implements Tree {
    List root;
    public MyTree(List root) { this.root = root; }
    Object getChild(Object parent, int index) {
	// don't care about parent; always null
        return list.getMyChild(i);
    }
    boolean isLeaf(Object node) {
        return true;
    }
    ...
}