Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Mobile

Patterns and Software Development


FEB94: Patterns and Software Development

Kent is founder of First-Class Software, providing consulting, tools, and components for Smalltalk developers. He can be reached on CompuServe at 70761,1216.


Patterns are a way of developing and packaging reusable software components. The idea of patterns is gaining attention in certain programming circles--especially those based on object-oriented languages and paradigms. At last fall's OOPSLA '93 conference, the foreground topics focused on mainstream development methodologies (such as the second-generation versions of Booch, Rumbaugh, Shlaer-Mellor, and the like), but smoldering in the background was much discussion around patterns. This subject will likely catch fire in the coming year.

Driving the discussion of patterns is the ongoing need to create truly reusable software--the long-awaited benefit of OO languages and methodologies that has yet to materialize.

In this article, I'll look at patterns as a method of guiding reuse. Although some of this discussion may be abstract, it draws upon my ten years of experience as a programmer and current vendor of object tools (Profile/V and the Object Explorer).

Patterns should not be confused with methodologies. A methodology tells you how to write down the decisions you have made. A pattern tells you which decisions to make, when and how to make them, and why they are the right decisions. Methodologies are free of content: Once you imagine a specific solution to a problem, a methodology gives you the wherewithal for writing it down and arriving at a correct implementation. By contrast, patterns are all content.

Abstractors and Elaborators

I divide the world of software development into two parts: the abstractor, creating reusable pieces; and the elaborator, massaging those pieces to fit the needs of a user. Microsoft has lately been promulgating a roughly similar vision, in which software development is divided into two categories: component builders (for example, programmers who write a DLLs or class libraries in C or C++), and solution builders (those who use high-level tools such as Parts, Visual Basic, PowerBuilder, or an application framework in conjunction with low-level DLL components to construct application-level solutions for end users). The abstractor/elaborator categorization is more general, so I'll stick with it.

The economics of reusable software are dominated by the cost of communicating between abstractor and elaborator. For example, if an abstractor takes 1000 hours to create a piece of reusable software, and 100 elaborators each take 100 hours to understand how to use it, then the elaborators have collectively spent ten times as many hours as the abstractor. Obviously, these numbers are hypothetical, but six months to create a reusable component and two-and-a-half weeks to learn how to use to use it effectively are well within the realm of possibility.

Making the abstractor more efficient (by providing, say, a faster compiler or whizzy debugger) won't reduce the total effort spent on writing software; if you view the abstractor and the elaborators as belonging to the same economic domain (say, a large corporation or organization), the equation's total is little changed. The only way to significantly affect the sum is to either reduce the number of elaborators (a bad thing, because it implies that software is not being reused, and thus more work is done from scratch), or reduce the time they spend figuring out the software.

This is nothing new. The old story of maintenance taking up 70 percent of the resources is really another way of saying the same thing. The new wrinkle is that, when you introduce software reuse into the equation, it isn't just one hapless programmer trying to figure out an obscure piece of code--it's hundreds.

Constructing a software component so that it is reusable is a step forward, but nowadays it's not enough. The abstractor needs to do more. Why should the abstractor care? In one model of reuse, there is a development team within a company building software components for other teams to use; in this model, making the elaborators more efficient reduces the development resources required. The company can then use the freed-up resources to shorten time-to-market, increase features, reduce development cost, or improve quality.

In another model of software reuse (the market model), reusable components are available for developers on the open market (for example, the Visual Basic add-on market). Here, if you are a VBX vendor (abstractor) and your customers (elaborators) are able to produce finished applications sooner, you will have a substantial edge over your competition.

If the time it takes elaborators to figure out reusable software is an important issue and solving the problem has significant payback, how can we reduce the time necessary to understand how to reuse software? What is it that, in the hands of elaborators, would make them more successful, sooner? Another way of asking the question is, what do abstractors know that they aren't communicating?

What's missing is a way for abstractors to communicate their intent. The abstractor, in building a piece of reusable software, is solving a whole set of future problems. Indeed, most reusable software results from the experience of being an elaborator several times, then having a flash of insight that solves a number of elaborator problems once and for all. The abstractor needs to communicate which problems a reusable component is intended to solve, how to think about the problem, how this thought process is embodied in the software, in what order to pursue subissues of the problem, and so on. Communicating with elaborators is more important than, say, using a better programming environment.

If you need to communicate what you were thinking about when you wrote your reusable software, what form would such communication take? Of course there are the usual mechanisms--a tutorial, reference manual, comments in the code, the coding conventions used by the source (if it is available to the elaborator), and, of course, word of mouth--bits of advice passed from guru to novice.

Researchers and developers have been exploring another approach, which falls under the rubric of patterns. I'll discuss the abstract definition later; first, I'll provide a concrete example of how patterns can be used to communicate the programmer's intent.

A Multicurrency Library

Let's take as an example a class library for handling multicurrency transactions. There are two principal classes: a Money entity, which has a value and a currency, and a CurrencyExchange, which can convert a Money in one currency to a Money in another. How can you use these objects? What is the intent behind the design? Here are three patterns that describe it. While by no means complete, a set of 15 or 20 such patterns would provide any elaborator a good start on reusing the library.

The Money Object Pattern

Problem: How to represent a monetary value in a system which needs to deal with many different currencies.

Constraints: One important concern in a system dealing with financial calculations is efficiency--making sure the calculations run in a timely manner and use as little memory as possible. The simplest representation of monetary values, and one which maps well onto the hardware, is representing them as fixed or floating-point numbers.

While you'd like your system to be as efficient as possible, you'd also like it to be flexible. For instance, you'd like to be able to decide as late as possible in which precision computations should occur. The rapidity of change of most financial systems dictates that flexibility is more important than efficiency for most applications---you can always buy faster hardware. When you need real number crunching, you can translate from and to a representation more flexible than simple numbers.

Another consideration, related to flexibility, is that a system handling multiple currencies should be as simple to use as possible. Only code concerned with creating or printing currency values should be aware that many currencies are possible. The rest of the code should look as much as possible like you are just using numbers.

Solution: When you need to represent a monetary value, create an instance of Money whose value is the value you need to represent and whose currency is the standard, three-character abbreviation (USD for United States dollars, for instance).

The Money Arithmetic Pattern

Problem: How can you do arithmetic with Money?

Constraints: Money arithmetic should be as simple as possible. Taking this constraint to the extreme would lead you to allow Money and numbers to freely interoperate, perhaps with a default currency to allow conversion of numbers to Money.

A far more important principle than mere programming convenience is making sure financial algorithms are correct. Restricting the combinations of values that can operate together arithmetically can catch many programming errors which might otherwise produce answers that seem reasonable, but are incorrect.

Solution: Send a Money the message + or -- with another Money as the parameter, or * or / with a number as the parameter. A Money will be the result of any of these messages. Adding a Money and a number, or multiplying two Moneys will result in an error.

The Money Print Pattern

Problem: How can you print a Money?

Constraints: The simplest possible design has a single global exchange rate. Asking a Money to print itself would cause it to convert to the common currency and print.

This simplest solution ignores the complexity of most financial systems, which must deal with multiple exchange rates--some historical, some current (perhaps kept up-to-date with currency exchanges), some projected. By specifying an exchange rate (in the form of a CurrencyExchange), your printing code will be slightly more complicated, but much more flexible as a result.

Solution: Print Money by sending CurrencyExchange the message "print" with Money as an argument. Money will be printed in the CurrencyExchange's preferred currency. There is a second message, printCurrency, which takes two arguments. The first is the Money to be printed, and the second is the currency (again, a three-character string containing a standard abbreviation) in which to print it.

Patterns

As you can see, a pattern has three parts:

  • Problem. The first part of every pattern is the problem it solves. This is stated as a question in a sentence or two. The problem sets the stage for the pattern, letting readers quickly decide whether the patterns applies to their situation.
  • Context. Patterns explicitly describe the context in which they are valid. The context is the set of conflicting constraints acting on any solution to the problem. You saw in Money an example of efficiency vs. flexibility. Other patterns might blalance development time and run time, or space and speed.
  • The constraints aren't just described, however. The pattern also specifies how the constraints are resolved. Money states that flexibility and correctness are more important than raw efficiency. Other patterns might find a balance between two or more constraints, instead of saying that one dominates. The aforementioned patterns really just sketch the context section. A fully developed pattern might have two or three pages of analysis to back up its solution.
  • Solution. Given the analysis of the constraints in the context section, the solution tells you what to do with your system to resolve the constraints. Supporting the solution is an illustration of it at work--either a diagram or code fragments.

Patterns Form Language

Although patterns are interesting in isolation, it is when they work together, forming a coherent language, that their power becomes apparent. A few times in my life I've been fortunate enough to work with someone who just seems to ask the right questions first. Rather than chasing issues that seem interesting but are ultimately secondary, some people can zero in on the one issue at any given moment that will allow the most progress. A language of patterns can function in much the same way.

By choosing the order in which the patterns are considered, the pattern writer has the chance to guide the reader in dealing with issues in the right order. In the patterns above, I have chosen to ignore efficiency for the moment, confident that should the issue arise later, it can be dealt with locally (I can imagine a later pattern which tells how to temporarily suspend the flexibility of Money to gain efficiency). In general, a good pattern language will lead you to address issues with wide scope early, and those with limited impact later.

How can you write your own patterns? The bad news is that applying patterns to programming is a new enough technique that there isn't anything like a body of experience to draw on. However, the Hillside Group has made progress with patterns. (See the accompanying text box entitled, "Pattern Resources.")

The first step in writing a pattern is a process of discovery. You notice yourself making the same decision over and over. You might find yourself saying, "Oh, this is just a such and so," or, "Oh, we don't have to worry about that now." These are the moments that you can capture as patterns.

Once you have noticed a recurring decision, you have to invent the pattern that encodes it. First, you must catalog the constraints that make the solution right. You will often find in exploring the constraints that you don't quite have the solution right--either it isn't the right solution, or you've described it too specifically or too generally. Finally, you have to find a problem statement that will help a reader choose when the pattern is appropriate.

A Pattern Checklist

After you have a pattern, you need to evaluate and refine it. Here is my checklist when I'm looking at a new pattern:

  • Does it read well? Does it have a sense of tension and release? Two thirds of the way through the context section of a good pattern you should be saying, "I never thought of this problem in quite this way. Now that I see all the constraints that have to be satisfied, I can't understand how there is any solution." Then, when you read the solution, you should blink your eyes, drop your shoulders, and give a sigh. Strongly literary patterns will make a bigger impact on the reader, and are likely to be based on deeper insight and clearer thinking than patterns that don't read like a story.
  • Does it tell me what to do? In the early stage of finding a pattern, I often find that I have really only described a solution without having stated the problem. The typical symptom of these solution-oriented patterns is that they don't tell you what to do and when to create the solution. Solution patterns leave the hard work to the reader--figuring out when a solution is appropriate and how to create it. As a pattern writer, you have this information tucked away in your head somewhere. Introspecting enough to pin it down and express it is what will make your patterns (and the code they support) valuable.
  • Does it stand without being easily broken into other patterns? I have heard "client-server" suggested as a pattern. While I can imagine a description of it that would read well, it fails the atomicity test. There is really a language of patterns which create client-server architectures. Somewhere in there are the decisions that divide responsibility for computation and storage between a shared server and multiple clients. Just saying "client-server," though, is too vague; it captures too many decisions to be a pattern.
  • Does it fit with other patterns to solve a larger problem? On the one hand, a pattern needs to stand on its own, without being further decomposable. However, for a pattern to be complete it must work in harmony with others to solve a larger problem. If I can't imagine how a pattern could be part of a larger language, either it isn't a good pattern, or other patterns are out there waiting to be discovered.
Using patterns to enhance reuse is just one of the ways patterns are being applied to programming.

Pattern Resources

The idea of patterns capturing design expertise originated with the architect Christopher Alexander. His books The Timeless Way of Building and A Pattern Language (both from Oxford Press) are required reading for anyone who wants to get serious about patterns. A forthcoming Addison-Wesley book, Design Patterns: Micro-architectures for Object-Oriented Design, by Erich Gamma et al., catalogs some of the most common object patterns.

The Hillside Group is a nonprofit corporation founded to promote communication to and through computers by all potential users, focusing initially on patterns as a strategy. The founding members are myself, Ken Auer, Grady Booch, Jim Coplien, Ralph Johnson, Hal Hildebrand, and Ward Cunningham. Our sponsors are Rational and the Object Management Group. In August 1994 we will sponsor the first annual Pattern Languages of Programs conference. For more information, contact [email protected]. The Hillside Group also has a mailing list, which you can contact at [email protected].

--K.B.


Copyright © 1994, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.