Every Tuesday the Obtivians have a lunchtime gathering at our Chicago office. These gatherings are pretty informal and usually start off with a few folks presenting technical topics followed by the general sort of heckling and mischief that you'd expect a room full of guys to get into.
This last Tuesday found us with no one prepared to present a topic, so we broke off into small groups and quiet conversation. I sat down with my lunch among a few of my colleagues and engaged in the conversation they were having. About exactly what I can't remember now, but trust me - it was geeky.
I finished my lunch as the conversation wandered toward design patterns and Ruby on Rails. I made the casual comment that I thought it was a bad idea to name a framework after a design pattern (Rails ActiveRecord, I'm looking at you) and that sparked a furious debate.
My argument was that somewhere along its evolution ActiveRecord the framework ceased to incorporate Active Record the pattern as it had evolved into a Data Mapper which eventually became a full ORM framework, having heritage in the Data Mapper pattern . Thus rendering the name "ActiveRecord" quite misleading if you're familiar with the PoEAA definition. A few of my colleagues believed otherwise.
However, the core of this debate and the topic of this blog post is not about the implementation of ActiveRecord. It is about what defines a design pattern in software and how you identify code that implements a particular pattern in the field. So let's get to that, we'll finish the debate later.
I'm fairly young as programmers go, but I've read most all of the current literature on patterns as well as having the opportunity to work with some truly exceptional individuals. So while I may not be prepared to give a three day lecture on the topic I do believe I've formed some worthwhile opinions.
My belief is that a design pattern in software is defined by two things: The particular problem the pattern addresses and the manner in which that problem is addressed. In my mind the interface, the UML diagram, and the implementation in code that often accompany the patterns in a book have very little to do with the definition of the pattern itself. All of these things are the artifacts of one specific implementation of said pattern. If you read the GoF book carefully you'll find that they point this out directly. They warn us against misinterpreting the examples as the literal pattern. A lot of us didn't listen very well.
Patterns are conceptual entities and that can make them especially difficult to grasp for the newcomer. Examples help with this, I get that. However, the unfortunate side effect being that these early students often latch on to the well-meaning examples and miss the underlying concept of the pattern itself, the one thing that really matters. Even worse, it can take a long time to get out of this rut.
What I try to do is identify what makes a pattern's solution conceptually unique among its peers. Let's take PoEAA Active Record versus Data Mapper, which was the topic of our debate. An Active Record is a simple row-level representation of a table in a relational database. What makes it unique is that the data to be persisted and the logic that handles that persistence all reside inside the same object - the Active Record.
In contrast, the Data Mapper creates a separation between the data and the logic that persists it. How that separation is achieved, to me, is unimportant. How I interact with the persisted object is unimportant. The UML definition is unimportant. What is important and what makes Data Mapper different from Active Record is the separation of the data to be persisted and the logic that handles that persistence (yes, I said that twice). That is the essence of Data Mapper.
Therefore, Rails' Active Record is more of a PoEAA Data Mapper than it is a PoEAA Active Record.
Now let's entertain the counter argument for a minute. What if a pattern is defined by the way in which you interact with it (its interface). And what if we apply that to patterns we see in another field, say architecture?
In your lifetime, how many thousands of doors have you interfaced with? How many of those doors had the same interface? Not many. The doors in my house have a knob to engage the latching mechanism and a hinge at one side, but the door to my garage curls up over the cars on rails when I push a button. The door at the grocery store moves out of my way automatically.
Are we wrong to say that all of these things are Doors? That there is One True Door and all other impostors must be called something else? Of course not, that's silly. Why? Because the concept - the pattern - of a door has nothing to do with its interface, or the materials it is made of, or its picture in a book. A door is a conceptual thing. If I was to define Door I would say it is "An easily removable barrier between two spaces".
What I really want to know is how each of you feel about this? What defines a pattern in your mind? Please, leave a comment.
Thursday, May 15, 2008
Subscribe to:
Post Comments (Atom)
1 comment:
I agree with your sentiment that it's not a good idea to name a framework after a pattern because the framework may always evolve and improve beyond the original pattern implemented.
Still, Rails ActiveRecord maps object models closely to database tables by default. Data Mapper on the other hand creates an explicit separation between the Object model and the database tables, allowing them to evolve separately. While Rails ActiveRecord allows some customizations; it is not as flexible and explicit as say iBatis in data-mapping capabilities.
As for the "what makes a pattern?" question, the Gang of Four book, Design Patterns: Elements of Reusable Object Oriented Software, says that a pattern consists of four essential elements:
1. Pattern name
2. Problem
3. Solution
4. Consequences
The fourth element is quite important in my opinion because it is the element that enables us to see the pros and cons of a pattern, prevening it from becoming a dogmatic cookbook formula that bloats our code-bases with over-engineered solutions.
That said, while the specific object interfaces can indicate which pattern is applied, they are usually just one way to implement the pattern, and not necessarily a part of the specification. For example, while the Decorator pattern in Java requires the decorator to implement every method and delegate the work to the super-class except for the decorated methods. In Ruby, one only needs to implement decorated methods and override method-missing (or better yet mixin a Decorator)
Thanks Tyler for such a thought-provoking blog post.
Post a Comment