Introduction to Software Patterns

(based on the Design Patterns book)
Human to computer: I've got this particular programming problem. My initial attempts to solve it have resulted in monolithic classes which don't support several useful cases and are hard to modify. Please show me successful programs where this problem has occurred, the solution taken, and the consequences of each.

Software patterns are a first step in realizing this dream. By documenting the essential technique and rationale behind recurring expert designs, they help people reuse successful practices.

The purpose of this course is to make you a better software designer. Design, for our purposes, is determining what kinds of objects should go into the system and what compile-time and run-time relationships they should have. This includes the granularity of objects, inheritance structure, compositional structure, and hooks for extensibility.

What is a good design?

Besides achieving its functional objectives, a good design should decompose the system into loosely-coupled components with clearly demarcated variabilities. Despite the hype, object-oriented software is not arbitrarily adaptable; adaptability is precisely determined by the system's design. Changing software in unplanned ways will almost surely break it. Furthermore, undocumented variabilities tend to disappear after a careless enhancement or bug fix.

Tricks of the trade

Design is a creative process. Formal methods can empower the creative mind, but they do not fuel creativity. Therefore, unlike most literature on design, we won't be talking about formal methods, like the Yourdon method or Booch notation. We will be talking about the tricks of the trade: what expert designers know that allows them to dream up world-class designs.

What is it that they know? Successful design patterns. Experts reuse time-worn solutions. Until recently, no record of these solutions was ever made, partly because the techniques aren't new (they're recurring patterns) and partly because they are hard to describe. Design patterns are amorphous and intangible. They are not simply pieces of code or box-and-arrow diagrams. Hence, they are not captured by macros, class libraries, or formal notations.

Example of a pattern

Microsoft Foundation Classes (MFC) is a class library for building applications under Windows. Its basic metaphor is visually editing a "document," which could be a letter, spreadsheet, database, drawing, graph, etc. Any subclass of the Document class will do. The visual representation is provided by an object whose type is a subclass of View.

MFC uses separate objects for documents and views, rather than lumping them together. The job of the view object is to ensure that its appearance reflects the state of the document object. MFC does this via a subscribe/notify protocol between the document and view. At run-time, each view object subscribes to a particular document object, and each document object notifies its subscribers whenever it changes. In response, each subscribed view gets an opportunity to update itself. This lets you dynamically attach and detach views of a document. It also allows multiple, consistent views of the same document.

For example, you might have a document which is an array of numbers. It can be simultaneously displayed as a spreadsheet, a bar chart, and pie chart. Modifying the bar chart, say by dragging the height of a bar, will modify the array, which will then notify the other views and let them update themselves to match.

This design decouples documents from views, and is indeed a pattern among modern user interface toolkits. However, this design is useful whenever you want to decouple an object from the many possible objects dependent on it. For this reason, it is also a recurring pattern in programming languages, graphics packages, and distributed databases.

Pattern form

How can we concisely describe and yet generalize this design? The idea, started by architect Christopher Alexander, is to fill out a form like this:
The general problem to be solved. When to apply the pattern.

Potential consequences that a good solution must take into account. Forces are often non-functional, such as reducing coupling or providing a variability.

Objects that make up the design, their roles, and their relationships. What each object needs to know about the others.

The costs and benefits of applying the pattern. How well it resolves the Forces. Used for evaluating competing solutions.

Suggestions and pitfalls in implementing the pattern.

Known Uses
Examples of the pattern found in real systems.

MFC's Document/View architecture, in pattern form, is called the Observer pattern.

Benefits of patterns

By definition, patterns capture experience, so in a sense they aren't new. However, I hope you can see why patterns are a useful subject of study. Three benefits of putting recurring designs into pattern form are:
Learning about design
Surveying the possible designs enlarges your toolbox and fuels creativity. (This is why the course will also include detailed case studies of real systems.) Studying a pattern and exploring the implementation alternatives allows improving the pattern and makes it easier to come up with new patterns.

You're now able to take a fairly complex arrangement of interfaces and run-time interactions between Documents and Views and simplify it to "Observer pattern." This means you'll be able to conceive of bigger and better designs, because of the higher level of abstraction. Furthermore, you'll be automatically reusing techniques that many others have already tested for you.

Documenting policy
You can use patterns as documentation, to ensure that components stay decoupled and variabilities remain flexible. When someone presents you with code described as being based on the Observer pattern, you can immediately answer questions about policy, e.g.

Another example of policy is specifying whether one object contains another vs. simply being acquainted with another. Both containment and acquaintance can be implemented in the same way, using pointers or references. But they entail significantly different policies with respect to the destruction, archival, or transmission of the object holding the pointer.

Computer to human: You have an object with possibly many dependent objects that could be reused separately. As you can see, MFC, ET++, Smalltalk, CORBA, ActiveX, and Inventor have all used variants of the Observer pattern in this situation...

Further reading

Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, Reading, MA, 1994.

"Software Patterns" in Communications of the ACM, October, 1996.

Mickey Williams and David Hamilton. Programming Windows NT 4. Sams Publishing, 1996.

Thomas Minka
Last modified: Mon Nov 3 20:04:38 EST 1997