software patterns. While a software system may have its own set of unique principles to guide its design, we believe that all systems can benefit from the use of this core set.
Separate code that varies from code that stays the same
If you have some aspect of your code that tends to change frequently, consider separating that code from the code that does not change. In other words, you should encapsulate the parts that vary so that you can later alter or extend those parts without touching the parts that do not vary.
This concept may be simple, but it is extremely important; it forms the basis for almost every software design pattern.
Program to an interface, not an implementation
When you program to concrete implementations of classes, you get locked into those concrete implementations. If you program to an interface instead, your only reliance is on the interface and not the implementation. So, you're not locked in; you can swap out the implementation of some aspect of your program more easily.
Favor composition over inheritance
Consider the HAS-A relationship: a Person has a WalkBehavior and a TalkBehavior and the person delegates talking and walking to these behaviors.
When you put two classes like this together, you are using composition. Instead of inheriting her behavior, the Person gets her behavior by being composed with the right behavior objects.
Using composition in lieu of inheritance gives you greater flexibility. It lets you encapsulate a family of algorithms into their own set of classes and it also lets you change behavior at runtime as long as the object you've composed implements the correct behavior interface.
Composition is used in many design patterns. The Strategy Pattern, in particular, promotes composition over inheritance.
Strive for loose coupling
When two objects are loosely coupled, they can interact, but have very little knowledge of each other.
Loosely coupled designs allow us to build systems that are more flexible because they minimize the interdependecy between objects.
Classes should be open for extension, but closed for modification
Classes should be easily extended to incorporate new behavior without modifying existing code. When we accomplish this, our designs are resiliant to change and flexible enough to take on new functionality and meet changing requirements.
Depend upon abstractions, not concretes
This is also known more formally as the Dependency Inversion Principle. It is similar to the concept of programming to an interface rather than an implementation, but it makes an even stronger statement about abstraction. It suggests that high-level components should not depend on low-level components; they should both depend on abstractions. One example can be visualized by imagining one abstract class inserted between a parent object that contains many child objects. Instead of making the parent refer to the child objects directly, it refers to their abstract base class.
The Factory Pattern is one of the most powerful examples of adhering to the Dependency Inversion Principle.
Principle of Least Knowledge - interact only with your immediate friends
For any object in a system, you need to be careful of the number of classes it interacts with and how it comes to interact with those classes.
This principle prevents us from creating systems that have a large number of classes coupled together where changes in one part of the system cascade to other parts. A system with too many dependencies is expensive to maintain and difficult for others to understand.
The Hollywood Principle - Don't call us, we'll call you
Low-level components can participate in a computation, but the high-level components should control when and how. A low-level component should never call a high-level component directly.
A class should have only one reason to change
Assign each responsibility to one class, and only one class.
Modifying code provides opportunities for new problems to arise. If a class has many ways to change, the probability of affecting multiple aspects of a system are increases when the class is changed.
This principle might also be called Separation of Responsibility, but we think our chosen heading expresses more. Separating responsibility sounds deceptively simple, but it's can actually be quite hard to do. The human brain is design to classify, organize, and group concepts, so doing the exact opposite can be a bit counter-intuitive.
Design to avoid Rigidity, Fragility, and Immobility
In his paper, "Dependency of Inversion Principal", Robert C. Martin wrote the following which I think makes a perfect conclusion to this article.
But there is one set of criteria that I think all engineers will agree with. A piece of software that fulfills its requirements and yet exhibits any or all of the following traits has a bad design.
- It is hard to change because every change affects too many other parts of the system. (Rigidity)
- When you make a change, unexpected parts of the system break. (Fragility)
- It is hard to reuse in another application because it cannot be disentangled from the current application. (Immobility)
Moreover, it would be difficult to demonstrate that a piece of software that exhibits none of those traits, i.e. it is flexible, robust, and reusable, and that also fulfills all its requirements, has a bad design. Thus, we can use these three traits as a way to unambiguously decide if a design is 'good' or 'bad'.
Keeping all of these principles in mind, as we design, we can create systems that are more easily understood and modified. We can create systems that are less rigid, less fragile, and that are assembled by reusable parts that can be leveraged again and again.
Do you want to know more?
Following are some of the best resources about software design patterns and principles that we've found useful in our own experience.
- Freeman, Eric & Elisabeth (ttp://www.oreillynet.com/pub/au/2002). Head First Design Patterns. O'Reilly Media, Inc., 2004.
- Design Patterns - at Wikipedia
- Patterns Library - at Hillside.net
- Patterns - at the ServerSide.com
- J2EE Patterns Catalog
If you really want to learn about patterns (and you should), this book provides a great way to do it. It is well illustrated, easy to understand, and chock-full of concrete examples.
<iframe src="http://rcm.amazon.com/e/cm?t=burtecgrollc-20&o=1&p=8&l=as1&asins=0596007124&fc1=000000&IS2=1<1=_blank&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>