Patterns in Practice

The Open Closed Principle

Jeremy Miller

Contents

Single Responsibility Principle
The Chain of Responsibility Pattern
Double Dispatch
Liskov Substitution Principle
Finding Closure

This is the first installment of a new MSDN® Magazine column on software design fundamentals. My marching orders are to discuss design patterns and principles in a manner that isn't bound to a specific tool or lifecycle methodology. In other words, my plan is to talk about the bedrock knowledge that can lead you to better designs in any technology or project.

I'd like to start with a discussion of the Open Closed Principle and other related ideas popularized by Robert C. Martin in his book, Agile Software Development, Principles, Patterns, and Practices. Don't be turned off by the word "agile" in the title, because this is all about striving for good software designs.

Ask yourself this: how many times do you start writing a brand new application from nothing versus the number of times you start by adding new functionality to an existing codebase? Chances are good that you spend far more time adding new features to an existing codebase.

Then ask yourself another question: is it easier to write all new code or to make changes to existing code? It's usually far easier for me to write all new methods and classes than it is to break into old code and find the sections I need to change. Modifying old code adds the risk of breaking existing functionality. With new code you generally only have to test the new functionality. When you modify old code you have to both test your changes and then perform a set of regression tests to make sure you didn't break any of the existing code.

So you generally work on an existing codebase, but yet it's easier to write all new code than it is to change old code. Wouldn't you like extending an existing codebase to be as productive and frustration-free as writing all new code? This is where the Open Closed Principle comes into play. To paraphrase, the Open Closed Principle is stated as: software entities should be open for extension but closed for modification.

It sounds like a contradiction in terms, but it's not. All it means is that you should structure an application so that you can add new functionality with minimal modification to existing code. I used to think that the Open Closed Principle just meant using plug-ins, but there's more to the story than that.

What you want to avoid is to have one simple change ripple through the various classes of your application. That makes the system fragile, prone to regression problems, and expensive to extend. To isolate the changes, you want to write classes and methods in such a way that they never need to change once they are written.

So how do you go about structuring code to isolate changes? I'd say that the very first step is to follow the Single Responsibility Principle.

Single Responsibility Principle

In following the Open Closed Principle, I want to be able to write a class or a method and then turn my back on it, comfortable that it does its job and I won't have to go back and change it. You'll never reach true Open Closed nirvana, but you can inch much closer by rigorously following the related Single Responsibility Principle: a class should have one, and only one, reason to change

One of the easiest ways to write classes that never have to change is to write classes that only do one thing. This way, a class only needs to be modified if the exact thing that it does needs to change. Figure 1 shows a sample that does not follow the Single Responsibility Principle. I seriously doubt you're designing a system like this, but it's good to remember just why we don't structure code this way.

Figure 1 This Class Does Too Much

public class OrderProcessingModule {
  public void Process(OrderStatusMessage orderStatusMessage) {
    // Get the connection string from configuration
    string connectionString = 
      ConfigurationManager.ConnectionStrings["Main"]
      .ConnectionString;

    Order order = null;

    using (SqlConnection connection = 
      new SqlConnection(connectionString)) {
      // go get some data from the database
      order = fetchData(orderStatusMessage, connection);
    }

    // Apply the changes to the Order from the OrderStatusMessage
    updateTheOrder(order);

    // International orders have a unique set of business rules
    if (order.IsInternational) {
      processInternationalOrder(order);
    }

    // We need to treat larger orders in a special manner
    else if (order.LineItems.Count > 10) {
      processLargeDomesticOrder(order);
    }

    // Smaller domestic orders
    else {
      processRegularDomesticOrder(order);
    }

    // Ship the order if it's ready
    if (order.IsReadyToShip()) {
      ShippingGateway gateway = new ShippingGateway();

      // Transform the Order object into a Shipment 
      ShipmentMessage message = 
        createShipmentMessageForOrder(order);
      gateway.SendShipment(message);
  } 
}

The OrderProcessingModule is pretty busy. It's doing data access, grabbing configuration file information, running business rules for order processing (probably very complicated by itself), and transforming completed orders into shipments. Chances are, if you were to build the OrderProcessingModule this way, you would be constantly breaking into this code to make changes. Many changes in system requirements would cause far too many changes to the OrderProcessingModule code, making the system risky and expensive to change.

Instead of one big blob of code, you should follow the Single Responsibility Principle to divide the entire OrderProcessingModule class into a subsystem of related classes, with each class fulfilling its own specialized responsibility. For example, you might put all of the data access functions into a new class called OrderDataService and the order business logic in a different class (I'll go into this in more detail in the next section).

In terms of the Open Closed Principle, by dividing the business logic and data access responsibilities into separate classes, you should be able to change either concern independently without affecting the other. A change in physical database deployment might cause you to replace the data access with something completely different (open for extension), while the order logic classes remain completely untouched (closed for modification).

The point of the Single Responsibility Principle isn't just to write smaller classes and methods. The point is that each class should implement a cohesive set of related functions. An easy way to follow the Single Responsibility Principle is to constantly ask yourself whether every method and operation of a class is directly related to the name of that class. If you find some methods that do not fit with the name of the class, you should consider moving those methods to another class.

The Chain of Responsibility Pattern

Business rules will probably face more changes throughout the lifecycle of a codebase than any other part of the system. In the OrderProcessingModule class, there was quite a bit of branching logic for order processing based on what type of order was received:

if (order.IsInternational) {
  processInternationalOrder(order);
}

else if (order.LineItems.Count > 10) {
  processLargeDomesticOrder(order);
}

else {
  processRegularDomesticOrder(order);
}

It's very likely that a real order processing system would grow to encompass more types of orders as the business grows—and accumulate plenty of special exception cases such as those for government and favored customers, and special of the week offers. It would be very advantageous for you to be able to write and test new order handling logic without risking breaking one of the existing business rules.

To that end, you can move closer to the Open Closed Principle for the order processing example by using a form of the Chain of Responsibility pattern, as demonstrated in Figure 2. The first thing I did was to make every conditional branch in the original OrderProcessingModule into a separate class that implements the IOrderHandler interface:

public interface IOrderHandler {
  void ProcessOrder(Order order);
  bool CanProcess(Order order);
}

Figure 2 Introducing a Chain of Responsibility

public class OrderProcessingModule {
  private IOrderHandler[] _handlers;

  public OrderProcessingModule() {
    _handlers = new IOrderHandler[] {
                new InternationalOrderHandler(),
                new SmallDomesticOrderHandler(),
                new LargeDomesticOrderHandler(),
    };
  }

  public void Process (OrderStatusMessage orderStatusMessage, 
    Order order) {
    // Apply the changes to the Order from the OrderStatusMessage
    updateTheOrder(order);

    // Find the first IOrderHandler that "knows" how
    // to process this Order
    IOrderHandler handler = 
      Array.Find(_handlers, h => h.CanProcess(order));

    handler.ProcessOrder(order);
  }

  private void updateTheOrder(Order order) {
  }
}   

I would then write a separate implementation of IOrderHandler for each type of Order, including the logic that basically says, "I know what to do with this Order, let me handle it."

Now that the business logic for each type of Order is isolated to a separate handler class, you can change the business rules for one type of Order without worrying about breaking the rules for another type of Order. Better yet, you can add completely new types of order processing with very minimal change to the existing code.

For example, let's say that, at a later time, I have to add support for government orders in the system. With the Chain of Responsibility pattern, I can write a completely new class called GovernmentOrderHandler that implements the IOrderHandler interface. Once I'm satisfied that GovernmentOrderHandler works the way it's supposed to, I can add the new government order processing rules by a one-line change to the constructor function of OrderProcessingModule:

public OrderProcessingModule() {
  _handlers = new IOrderHandler[] {
              new InternationalOrderHandler(),
              new SmallDomesticOrderHandler(),
              new LargeDomesticOrderHandler(),
              new GovernmentOrderHandler(),
  };
}

By following the Open Closed Principle with the order processing rules, I've made it much easier to add new types of order handling logic to the system. I was able to add the government order rules with much less risk of destabilizing the other types of orders than I would have faced if a single class had implemented all of the various types of order handling.

Double Dispatch

What if the steps become more complicated in the future? What if pure polymorphism just isn't enough to allow for all the possible variations coming up in the future? We can use a pattern called Double Dispatch to pull out the variation into the subclasses in such a way that we don't break existing interface contracts.

For example, let's say that I am building a composite desktop application that displays one screen at a time in some sort of main panel. Every time I open a new screen in the application I need to a number of things. I might need to change the available menus, check the state of the screens already open, do any number of things to customize the display of the whole screen and, oh yeah, display the new screen in some way.

I typically use some variety of the Model View Presenter (MVP) pattern for my desktop client architectures, and I generally use the Application Controller pattern to coordinate the various MVP triads in the application. Given the usage of MVP with an Application Controller (for more information on MVP, see Jean-Paul Boodhoo's MSDN Magazine Design Patterns column on MVP, available at msdn2.microsoft.com/magazine/cc188690), the screen activation might involve these three basic parts:

  1. A Presenter for a single screen. Each Presenter knows everything there is to know about a single specific screen.
  2. An ApplicationShell for the main form of the application. The ApplicationShell is responsible for displaying individual views within a Panel or TabControl of some kind in itself. The ApplicationShell will also include all the menus.
  3. An ApplicationController to act as the traffic cop of the application. The ApplicationController knows about the ApplicationShell and every Presenter that passes through the application. The ApplicationController governs the screen activation and deactivation lifecycles.

If all I need to do is simply show the View in the ApplicationShell on activation the code might look like Figure 3. This is perfectly workable for a simple application, but what if the application becomes more complicated? What if, in the second release, I have new requirements to add menu items to the main shell when some screens are active? And what if I want to start showing additional controls in a new pane along the left edge of the main screen for some views, but not all?

Figure 3 A Simple View-Based App

public interface IApplicationShell {
  void DisplayMainView(object view);
}

public interface IPresenter {
  // Just exposes a getter for the inner WinForms UserControl or Form
  object View { get; }
}

public class ApplicationController {
  private IApplicationShell _shell;

  public ApplicationController(IApplicationShell shell) {
    _shell = shell;
  }

  public void ActivateScreen(IPresenter presenter) {
    teardownCurrentScreen();

    // Setup the new screen
    _shell.DisplayMainView(presenter.View);
  }

  private void teardownCurrentScreen() {
    // teardown the existing screen
  }
}

I still want the architecture to be pluggable so that I can add new screens to the application by simply plugging in new Presenters, so the knowledge of these new menu and left pane constructs should go into the existing Presenter abstraction. I'd then have to change the ApplicationShell or the ApplicationController to react to the new menu items and the additional controls placed in the left pane.

Figure 4 shows a possible solution. I've added new properties to the IPresenter interface to model the new menu items and any additional controls that might be added to the new left pane. I also added some new members to IApplicationShell for these new concepts. Then I added new code to the ApplicationControl­ler.ActivateScreen(IPresenter) method.

Figure 4 Trying to Extend IPresenter

public class MenuCommand
{
    // ...
}
public interface IApplicationShell
{
    void DisplayMainView(object view);
    
    // New behavior
    void AddMenuCommands(MenuCommand[] commands);
    void DisplayInExplorerPane(object paneView);
}
public interface IPresenter
{
    object View { get; }

    // New properties
    MenuCommand[] Commands{ get; }
    object[] ExplorerViews { get; }
}
public class ApplicationController
    {
    private IApplicationShell _shell;
        
    public ApplicationController(IApplicationShell shell)
        {   
        _shell = shell;
        }

    public void ActivateScreen(IPresenter presenter)
    {
        teardownCurrentScreen();
        
        // Setup the new screen
        _shell.DisplayMainView(presenter.View);

        // New code
        _shell.AddMenuCommands(presenter.Commands);
        foreach (var explorerView in presenter.ExplorerViews)
        {
        _shell.DisplayInExplorerPane(explorerView);
        }
    }

    private void teardownCurrentScreen()
    {
        // teardown the existing screen
    }
}

So, did this solution conform to the Open Closed Principle? Not in the slightest. First, I had to modify the IPresenter interface. Since it is an interface, I would have had to find every implementer of the IPresenter interface in my codebase and add empty implementations of these new methods just so that my code could compile again. That's often an intolerable change, especially if any of these IPresenter implementers are outside your immediate control. More on this later.

I also had to modify the ApplicationController class so it knows about all of the new types of customizations each screen might need on the main ApplicationShell. Finally, I needed to modify ApplicationShell to support the new shell customizations. The changes were minor, but then again, it's not unlikely I'll want to add even more screen customizations later.

In a real application, the ApplicationController class can get complicated enough without having to assume additional responsibilities for configuring ApplicationShell. It would be nice if we could keep this responsibility back in each Presenter.

The change to each IPresenter implementation could be alleviated by using an abstract class called Presenter instead of an interface. I could just add default implementations to the abstract class as shown in Figure 5. And I wouldn't have to change any of the existing Presenter implementations to add the new behavior.

Figure 5 Using an Abstract Presenter

public abstract class BasePresenter
{
    public abstract object View { get;}

    // Default implementation of Commands
    public virtual MenuCommand[] Commands
    {
         get
         {
             return new MenuCommand[0];
         }
    }

    // Default ExplorerViews
    public virtual object[] ExplorerViews
    {
         get
         {
             return new object[0];
         }
    }
}

There is another way to move closer to the Open Closed Principle ideal that might be cleaner in the end. Instead of putting getters on the IPresenter or BasePresenter abstraction, I could use the double dispatch pattern.

I got an unexpected demonstration of the double dispatch pattern in real life the other day. My team had just moved into a new office space and we were battling networking issues. Our networking guru called me last week and started telling me what my coworker should do to connect to the VPN. He rattled off a bunch of networking mumbo jumbo I didn't understand, so I finally just handed the phone off to my coworker to let them talk directly.

Now, let's do the same thing for the ApplicationController. Instead of the ApplicationController asking each Presenter what needs to be displayed in the ApplicationShell, the Presenter will just skip the middle man and tell the ApplicationShell what it needs to do for each screen (see Figure 6).

Figure 6 Double Dispatch Presenter

public interface IPresenter {
  void SetupView(IApplicationShell shell);
}

public class ApplicationController {
  private IApplicationShell _shell;

  public ApplicationController(IApplicationShell shell) {
    _shell = shell;
  }

  public void ActivateScreen(IPresenter presenter) {
    teardownCurrentScreen();

    // Set up the new screen using Double Dispatch
    presenter.SetupView(_shell);
  }

  private void teardownCurrentScreen() {
    // tear down the existing screen
  }
} 

I would have to change ApplicationShell for the new menu customization and left pane controls no matter what I had done at first, but if I had started with the Double Dispatch strategy, I could have made the new changes with fewer modifications to both ApplicationController and each Presenter. I no longer need to touch either the ApplicationController or the Presenter classes to create additional screen concepts. The architecture is open to be extended for new shell concepts, but the ApplicationController and the individual Presenter classes are closed for modification.

Liskov Substitution Principle

As I said earlier, the most common manifestation of the Open Closed Principle is using polymorphism to substitute an existing part of the application with a brand new class. Let's say that at the beginning of the day you have a class called BusinessProcess whose job is to, well, execute a business process. Along the way, it needs to access data from a data source:

public class BusinessProcess {
  private IDataSource _source;

  public BusinessProcess(IDataSource source) {
    _source = source;
  }
}
public interface IDataSource {
  Entity FindEntity(long key);
}

The design follows the Open Closed Principle if you can extend the system by swapping out implementations of IDataSource without making any change to the BusinessProcess class. You might start out with a simple XML file-based mechanism, and then move to using a database for storage, followed eventually by some sort of caching—but you still don't want to change BusinessProcess class. All of that is possible, but only if you follow a related principle: the Liskov Substitution Principle.

Roughly stated, you are following the Liskov Substitution Principle if you can use any implementation of an abstraction in any place that accepts that abstraction. The BusinessProcess should be able to use any implementation of IDataSource without modification. BusinessProcess should not know anything about the internals of IDataSource other than what is communicated through the public interface.

To put it into perspective, Figure 7 shows an example that flunks the Liskov Substitution Principle. This version of the BusinessProcess class has specific logic to bootstrap a FileSource and also relies on knowledge of some specific error handling logic for the DatabaseSource class. You create the implementers of IDataSource such that they can handle all of their specific infrastructure needs. Doing this will enable the BusinessProcess class to be written, as shown in Figure 8.

Figure 7 BusinessProcess that Cannot Abstract IDataSource

public class BusinessProcess {
  private IDataSource _source;

  public BusinessProcess(IDataSource source) {
    _source = source; 
  }

  public void Process() {
    long theKey = 112;

    // Special code if we're using a FileSource
    if (_source is FileSource)  {
      ((FileSource)_source).LoadFile();
    }

    try {
      Entity entity = _source.FindEntity(theKey);
    }
    catch (System.Data.DataException) {
      // Special exception handling for the DatabaseSource,
      // This is an example of "Downcasting"
      ((DatabaseSource)_source).CleanUpTheConnection(); 
    }
  }
}

Figure 8 Better BusinessProcess

public class BusinessProcess {
  private readonly IDataSource _source;

  public BusinessProcess(IDataSource source) {
    _source = source;
  }

  public void Process(Message message) {
    // the first part of the Process() method

    // There is NO code specific to any implementation of     // IDataSource here
    Entity entity = _source.FindEntity(message.Key);

    // the last part of the Process() method
  }
}

Finding Closure

Just remember, the Open Closed Principle is only realized by polymorphism if a class only depends on the public contract of the other classes it interacts with. If a class that uses an abstraction has to downcast to a specific subclass in one section, you're not following the Open Closed Principle.

If a class that uses another class embeds knowledge about the internal workings of its dependency (like assuming that results of a method are always sorted from big to small), then you can't actually substitute another implementation for that dependency. Those types of implicit coupling to a specific implementation are particularly pernicious because they aren't obvious to a reader of your code. Don't let the consumer of an abstraction depend on anything but the public contract of that abstraction.

I would recommend that you treat the Open Closed Principle as a design vector rather than an outright goal. If you try to make everything that you think could possibly change fully pluggable, you're likely going to create an over-designed system that is very difficult to work with. You may not always manage to write code that satisfies the Open Closed Principle in every aspect, but even moving partway there can be beneficial.

Send your questions and comments to mmpatt@microsoft.com.

Jeremy Miller A Microsoft MVP for C#, Jeremy is the author of the open source StructureMap (structuremap.sourceforge.net) tool for Dependency Injection with .NET and the forthcoming StoryTeller (storyteller.tigris.org) tool for supercharged FIT testing in .NET. Visit his blog, "The Shade Tree Developer," at codebetter.com/blogs/jeremy.miller, part of the CodeBetter site.