|
|
|
|
What is good design?In our day-to-day adventures in computer science, we are constantly confronted with examples of good and bad design. For most of us, we probably see more examples of bad design than good. Maybe we are not even aware of what constitutes good design, and as a result, we may fail to recognize it when we see it. Martin Fowler frequently speaks of “bad smells” in code –- bad practices that hint at the fact that bad design may be present. Reading Mr. Fowler’s books and online articles may prepare us to recognize such smells. However, I would argue that it is more important to recognize and appreciate good design in the real world around us. Doing so prepares us to translate ideas from the real world to the world of software. |
Oriented towards objects
Introductory OO programming classes repeat the fact that object oriented programming arose out of a desire to accurately represent the world around us. It was also based on the understanding that software systems were essentially based on the ability to model the properties, functionality and interactions of real-world objects. We all absorbed this information, and then promptly forgot it once our teachers started talking to us about such esoteric ideas as encapsulation and polymorphism. How many of us took a step back to ask the following question, “If OO is based on the real world, and we’re learning about encapsulation and polymorphism, then shouldn’t these ideas exist in the real world?”
Design patterns and the real world
A similar question arises with respect to design patterns. I frequently emphasize the fact that design patterns are solutions to problems in context. It is these last two words that many software ‘designers’ often forget. Just because a software system has been put together using design patterns does not mean that it possesses good design.
Let’s take a walk into the real world to explore this idea a bit. Assume that I am building an office building, and I need to create a way for my employees to enter the building. Given this problem, a novice architect might suggest that we install a door (Let’s call this the Door (with a capital ‘D’) design pattern. However, when I add context to my problem, other solutions surface. I realize that I actually need two separate ways to allow employees to enter:
- At the entrance to the building, I need a door, but since I am planning to have security cameras installed, I want to ensure that people enter one at a time. Additionally, I want to control traffic flow in and out of the lobby area. Finally, because the building is insulated in summer and winter, I want to ensure that the outside environment does not interfere with the climate-controlled inside
- Once employees are inside, I am still interested in controlling the flow of traffic, but I want to install an access point, so that IDs can be checked, and employees admitted to the building once their credentials have been verified.
Upon careful thought, I realize that I can use the following design patterns from the real world to solve these problems. At an abstract level, they are extensions of the Door design pattern. These design patterns are RevolvingDoor and Turnstile, respectively. Look around, and you will see another useful design pattern – SlidingDoor (subway cars use them). As a building architect, I would seek to accumulate an understanding of as many of these patterns as I can. However, I would also carry around with me the understanding that context is more important than the pattern itself. Think of how ridiculous it would be to install a RevolvingDoor where a SlidingDoor was called for. Now ask yourself if you’ve seen software design patterns used in contexts for which they were ill suited. I suspect that each one of us can think of at least one.
The real world and components
A well-designed component can be one of the most beautiful things in the real world. Frequently, the best components are so well designed that we do not even notice their presence. Components help drive the economy. The success of technology is based on something called ‘network externalities’ – a topic that I will explore in a future article. Components in the real world also follow design patterns. Think about a cassette tape – it has a published interface that it uses to communicate with its container (the tape player). The existence of a container-component interface allows multiple vendors to provide tape players as well as the tapes themselves. So you can see the why having different component and container vendors is a useful idea. This is not an idea that exists solely in the world of software.
What does all of this have to do with the New York City Subway?
I decided to write this article because I use the subway every day. The subway system is an example of the real world system (and in my opinion, a well designed system). Since we are in the business of developing software systems, there must be lessons that we can learn. Here, I present some of the ways in which observations of the subway have helped me refine some of my ideas about system design.
- Keep things simple
If you’ve ever attempted to navigate the subway on your own, you will have noticed that you are presented with a bewildering array of choices – uptown, downtown, local, express, letters, numbers, etc. However, the subway map is a model of simplicity. After a while, it becomes almost automatic to figure out mentally how to get from one place to another using the subway. It helps if you realize that the subways in Manhattan generally follow the Avenues, and that the express stops (where you can transfer to the local lines) are located on the designated two-way streets. At its heart, the subway system is designed around the idea of making things as simple as possible, and no simpler. The result is a system that uses its resources in an optimized fashion, and keeps its customers happy. - Provide feedback and information
The subway system recently began replacing the classic ‘Redbird’ cars with modern cars that display the time of day as well as the next scheduled stop. As a result, passengers no longer have to decrypt what can best be described as the garbled static of announcements over the PA system. Another interesting feature is a route map that plots all the stations on that particular route (see the picture below), and tells you which direction the train is moving.
However, I discovered that this is actually bad design. It is a perfect example of ‘tight coupling’. Essentially, a subway car is a component. So it should be possible to take a subway car that runs on the ‘2’ line, for example, and plug it into the ‘5’ line. But as soon as you do this, the nice helpful route map is no longer valid. To their credit, the designers did include a portion of the map that lights up, stating, “This route is not valid for this trip”.
- Keep your customers satisfied
This should be the objective of anyone who aspires to be a designer. Good design is not that which can be appreciated by your peers, but rather by those who use it on a daily basis. New York is known to be a city that’s short on forgiveness. If something doesn’t work, you’ll be sure to hear about it. In fact, a group known as the Straphangers maintains a site (http://www.straphangers.org/) that tracks the performance and reliability of the subway system. Imagine a scenario where a system that you designed was laid bare for the world to see. How frequently do you think of your users when you design systems?
There are other ideas that I will hint at, but leave up to you to explore. If you can manage it, riding the subways for work or for pleasure is highly recommended. See if you can recognize examples of interoperability between systems (for example, being able to use the subway’s Metrocard to pay for your PATH fare), the use of standards (why is the track a certain gauge?), other examples of container-component contracts, and of course design patterns. At some point, it would be useful to keep a notebook to see if the subway is a resource for design ideas, including design idioms and antipatterns. Happy riding!

