Core Software Design Principles

Software architects and developers leverage a core set of design principles that have been established by professionals over the course of many years. Many of these principles are foundational to the most well-known 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.

  1. It is hard to change because every change affects too many other parts of the system. (Rigidity)
  2. When you make a change, unexpected parts of the system break. (Fragility)
  3. 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.

Unknown macro: {pop-layout}
Unknown macro: {portlet-a}
Unknown macro: {portlet-a}

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.

Unknown macro: {html}

<iframe src="" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>