When is a "Pattern" not a "Pattern"?

When is a "Pattern" not a "Pattern"?

By Alan Griffiths

Overload, 12(61):, June 2004


We all go through life listening to and telling stories: it is an important part of human social behaviour. Parents tell children bedtime stories, we read novels, watch TV and films, and many successful computer games are based on allowing the player to participate within a story. Incidentally, one of the things that that seems to have been lost in many of these formats is the idea of participation - although, to the annoyance of their mother, my children re-invented it for bedtime stories (she preferred reading from a book to collaborative invention).

Stories are also important in the development of computer software. Not only the stories about how the system will be used, but also the stories about how it will work. As with the stories in the wider world they can take a number of forms: Among these are "design patterns" and that is the form that I want to examine in this essay.

Before continuing, I should mention that there are those that think "Patterns" are like the Platonic Ideals to be found in philosophy: unchangeable and perfect examples of a concept. (This is not to say that any particular write-up of a pattern is ever perfect - Plato's Ideals exist in the "real world", not the mundane one.)

For me the important aspect of a pattern is that it narrates the way that a design context is transformed by the application of a solution. I don't expect to ever meet an idealised design context and so don't demand that a pattern is the one true solution to a design problem. (Only that it tells me enough to decide for myself if applying the solution will advance the task.)

To demonstrate this I am going to present two "patterns" with a common beginning, one that many developers are familiar with. In fact, a good number of developers will be familiar with the whole of both stories to the extent that I'm not going to worry about some of the formalities of writing about patterns - such as citing "existing implementations". These are left as an exercise for the interested reader.

Story Number 1

Initial Context

When a program is working with numerical values it is necessary to represent these using the types and names available in the language. Many languages provide some native data types (such as the C/C++ types int and double) which support a rich array of operations and conversions. Some of these operations and conversion may not be appropriate to the types of numerical value being worked with. For example, double might provide the range of values and operations required for working with temperature and pressure. However, it is possible to make mistakes by, for example, assigning a temperature value to a pressure variable.

Problem

How can such (hard to diagnose and expensive to fix) mistakes be avoided?

Solution

Encode the problem domain types in the variable names so that the programmer is reminded of the appropriate use of the variable. For example, by using a prefix indicating the type of the variable. Vis: cgdEngine and ntnsOil .

Resulting Context

The developer can see from its name the appropriate manner in which to make use of a variable. An error like:

ntnsOil = cgdEngine

is noticeable "at a glance". However, there is an overhead to this solution: the developer needs to maintain a catalogue of problem domain type prefixes and the corresponding types within the programming language. Note that this catalogue may be explicit (written down) or implicit (what everyone knows).

This is a perfectly good pattern that is adequately captured in the above form. It would gain little from written in a more formal structure (e.g. Coplien). It could also be written in a less formal manner (e.g. Alexandrian) and still be considered a valid pattern.

As for existing usage: this is one of several design approaches referred to as "Hungarian Notation" and there is no shortage of developers willing to attest to its effectiveness.

Story Number 2

Initial Context

When a program is working with numerical values it is necessary to represent these using the types and names available in the language. Many languages provide some native data types (such as the C/C++ types int and double ) which support a rich array of operations and conversions. Some of these operations and conversion may not be appropriate to the types of numerical value being worked with. For example, double might provide the range of values and operations required for working with temperature and pressure. However, it is possible to make mistakes by, for example, assigning a temperature value to a pressure variable.

Problem

How can such (hard to diagnose and expensive to fix) mistakes be avoided?

Solution

Encode the problem domain types as user defined types so that the compiler enforces the appropriate use of the variable. For example, types " centigrade " and " newtons " may be defined so that they only support the appropriate operations and conversions.

Resulting Context

An error like:

oil_pressure = engine_temperature

will not compile. However, there is an overhead to this solution: the developer needs to maintain a library of problem domain types and code the support for the allowed operations and conversions.

Like the first story this is a perfectly good pattern but it is clearly a different one.

Once again it isn't hard to find developers that are willing to attest to the effectiveness of this approach.

Two Paths to Choose From

What are we to make of this? We have two "patterns" that start from the same position, and apply different solutions with "success". Which design should we choose for our work?

Well, it would be easy if one solution always leads to a better result than the other does. Then one pattern would be better than the other (and closer to that Ideal). But this turns out not to be the case - it is not always the same solution that produces the better result.

Clearly both approaches resolve the problem stated in the initial context. But when the two resulting contexts are examined closely it becomes apparent that a careful assessment of costs and benefits is required. Specifically: we have to chose between the cost of maintaining the "prefix catalogue" or of maintaining the "type library" described in the two solutions. (This may appear to be lower in the first story as the catalogue need not be maintained explicitly.) At the same time we have to choose between the benefits of visual and compiler based error detection.

In some circumstances the second solution may not even be an option: the cost of implementing user-defined types may be so high as to be prohibitive - in some languages there is no support for it. (Although it may be possible to add them - some "lint" style tools add the notion of "strong typedef" to C for this purpose.) Even where possible in the language, the developer skills available to the project may not be up to providing the desired user defined types reliably.

It is far rarer that the first solution isn't available: languages whose maximum identifier length is significantly constrained are thankfully rare these days. But there are occasions where identifiers are displayed to users who would not appreciate these prefixes.

It could even be worse: the initial context may sometimes be better than either of the resulting contexts described above. This happens whenever the cost of introducing a new type is higher than the risk involved in reusing an existing type - or when the language doesn't allow user-defined types or long identifiers.

Will the True Pattern Please Stand Up?

What is this: two (or three) patterns that might apply to the same problem? That doesn't fit with the popular view of patterns as a template for solving problems: match the initial context and the problem, and the solution follows automatically.

One way to defend this viewpoint is to insist that there are additional forces at work, ones not captured by the initial context. But look at what these forces are: the cost and/or benefits of aspects the resulting context. Discussing these as part of the initial context would seriously unbalance the story and lead to turgid prose. No, there is no escaping from it: there is still a role for the designer! These patterns don't replace thought - you still have to decide which option is better.






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.