The Pattern Movement in Software Engineering has become very popular and has produced many books, some of them arguably extremely good, but apart from automatic refactoring in modern IDEs, the number of supporting tools is surprisingly low.
What Is A Pattern Anyway
You can get a good overview about Design Patterns at the Portland Pattern Repository. For the purpose of this article we just take the classic definition from the Gang Of Four book:
A design pattern systematically names, motivates, and explains a general design that addresses a recurring design problem in object-oriented systems. It describes the problem, the solution, when to apply the solution, and its consequences. It also gives implementation hints and examples. The solution is a general arrangement of objects and classes that solve the problem. The solution is customized and implemented to solve the problem in a particular context.
The crucial bit is the last sentence: Design patterns need to be implemented specifically for each particular problem. The classes in an object-oriented design pattern are no classes at all. They are archetypes, and for the particular situation real classes have to be crafted after those archetypes.
To put it in other words, a design pattern is an archetypical solution to an archetypical problem, and that’s also the reason why you can’t press patterns into libraries. If you compare two instances of the pattern, i.e. two cases where the pattern has been used to solve the problem, you’ll find almost no code that could be factored out. The pattern is not code, the pattern is a way the code is structured. Lexically two instances of a pattern have nothing in common, everything is only similar, nothing is the same.
In some cases it is even possible to put a pattern into a library, but when you begin to do so, you quickly find out that the result is not useful. Too much varies, the result are endless parameter lists or endlessly overloaded variations of the same methods. Those libraries are hard to implement and they don’t feel natural.
Generics (templates in C++) are a step in the right direction, but although they make creating archetypical libraries easier, those libraries are not necessarily easy to understand and use, much less to implement.
The Most Simple Patterns
Design patterns don’t come only at the level of class interaction in object-oriented systems. Just look at programming languages. In the beginning there was only assembly language. We basically wrote machine instructions, but after a while it turned out, that we did similar things over and again.
One of those things was to build loops of code. People did completely different things in the bodies of their loops, but there were only a few patterns for how the loop was entered, exited, and how often it was executed. With the advent of higher programming languages, those patterns were cast in basic language constructs such as “WHILE-DO”, “DO-UNTIL”, “FOR” or “FOREACH”, and these language constructs are still with us.
Assembly language did not have those high-level loops, but it had a conditional branch. Loops had to be implemented with conditional branches. The direct equivalent to that is the combination of the “IF” and “GOTO” statements in programming languages, and of course you can use those to implement all your loops. It’s only more verbose, the intent is less clear, and that is, because you don’t express your intent by naming it. Patterns are much about naming.
Interestingly enough, an “IF” statement is also a pattern. Just look at the infinite number of conditions. This brings us to another important fact, the fact that patterns are not all born equal. They come in hierarchies. There are base patterns and composite patterns. Just like with “IF-GOTO”, you can use patterns to build other, more complex patterns.
Now, if you look at where the pattern movement came from, being initiated by the architect Christopher Alexander, and if you read his books, you see that he clearly understood the hierarchical nature of design patterns. That’s why he called it a Pattern Language.
Well, I’ve never tried to build a house, and if I were to build one, I’d probably not apply all of his patterns. Many of them I agree with, some I don’t, and for most there are alternatives that he does not cover. But that again coincides nicely with the notion of languages.
My native language is German, I regularly use English, have some low level, basic and spotty understanding of some of the languages originating in Latin, but that’s it. No idea of Chinese, no idea of any African language, no idea of anything else. And still, all those languages, that I don’t have a clue of, can express exactly the same things that German or English can. They use only different patterns to solve the same problems.
So I guess we can agree that it is possible to build different houses, houses that don’t fit into Christopher Alexander’s language, and we may still be able to live in them and stay sane.
For the adoption of Alexander’s method, this may really have been detrimental. It is much too obvious that, although it makes sense as a system, you may not be able to use it and build your dream. It would be Alexander’s dream instead, and when it comes to our dreams, we really dislike any intrusion.
This may explain Alexander’s limited success (and I don’t want to belittle him, he is really one of my heroes, even if he has not taken over the world), but it would not be in our way building programs, would it? After all, houses are for individuals, and individuals care for their individuality, but CRUD is just CRUD, ain’t it?
Maybe not. People seem to have a tendency to willingly and knowingly reinvent the wheel. Look at me: I could be satisfied using the tools I have, using them to create the things I’m paid for, and otherwise have a life. Or else just join the development of the Eclipse Modeling tools. In a way there’s so much already out there, I can’t have much hope to make a valuable contribution. And still I do what I do, knowing that I’m bound to reinvent, willingly accepting it. And why? Because it’s fun
I don’t know if this explains anything, but fact is, that the Design Pattern movement in computer science has not produced anything that even remotely comes close to Alexander’s hierarchical completeness. With Alexander’s language you can build a house, a street, a village, a town, a region, and in the other direction you can go down to details like the actual building materials.
You can’t do that with design patterns in computer programming. The high-level patterns are all missing. The discipline has evolved to the point where it has become possible to talk to each other using design patterns, but it’s only at that certain level of detail where we talk about basic object interaction.
The Necessary Next Step
Looking into some very simple patterns of repeated assembly code, we found that these patterns became the loop constructs of modern programming languages. Those loop patterns describe arrangements of assembly instructions, but the important thing to note here is, that today nobody arranges those instructions themselves. We use compilers for that. This is very different from how we work with Gang-Of-Four patterns, because they have to be instantiated by hand.
A compiler can do what it does, because it has a complete model of the program. All variables, that the built-in patterns like loop constructs refer to, are also defined in the same model. The whole semantic is defined in terms of the language and its patterns, and where this is not the case, the semantics are defined by standardized libraries. So, actually what this boils down to is, that a programming language allows us to express the desired semantics by constructing a model. The compiler than applies its patterns to this model and this way constructs either code or calls into supporting libraries.
That’s exactly what code generators do. They take a model and translate it into code of a different, simpler language, and where it makes sense, they generate not code but calls into external libraries. In this context, libraries have two purposes:
- they avoid code duplication
- they allow us to express semantics that can’t be expressed in the language’s patterns
#1 is nice to have, but #2 is really important, because it means that we can generate code, even when our patterns are not a complete, self-sufficiant system. What we can’t express in our language, we simply assume implemented in libraries.
This is the way to go. We have to build higher-order languages, languages that implement recurring patterns just the way as today’s programming languages implement assembly loop patterns. Once we have such languages, we can translate them automatically into code. Just like with conventional programming languages, those things will be the more powerful, the more self-sufficient they are, but that does not mean that small and incomplete steps can’t be incredibly useful in their own right.
What I want to implement as my project are three things:
- an environment to specify models,
- a set of patterns that, when applied to such a model, turns the model into an
application, and finally
- a code generator, that makes this process automatic
That’s it. Easy, huh?