Published: 01 Nov 2010
By: Chris Zavaleta

This article summarizes the Open/Closed principle with examples and concepts in C#.

Contents [hide]

Introduction

When using agile development practices the usual bag of themes come into play: scalability, extensibility, decoupling, testability, opacity, software rot and so on. One of the cornerstone concepts in an agile design is the Open/Closed Principle. This principle, which was coined by Bertrand Meyer, states that "Software entities should be open for extensibility but closed for modification". What this means in practical terms is that software dependencies should be minimized by the use of abstractions. These abstractions will allow for concrete implementations of software to be extendable. An abstraction in this sense is either an abstract base class or an interface. For this article I'll be talking about using interfaces or "contracts" to serve as the abstraction layer and dependency injection as the pattern providing the hooks into the extensibility.

An example of open and not closed software

As an example, let's take a fictional order processing system. Our system will start off with 2 classes, Customers.cs and OrderProcessor.cs. For the persistence / infrastructure layer, let's add a 3rd party order processing system, the X-4000, steady as she goes. The business class, Customers.cs, has a function Customers.GetAllOrders(). The class in charge of the X-4000's business logic and facilitates direct communication with the X-4000 (infrastructure layer) is OrderProcessor.cs.

Figure 1: Customer - OrderProcessor – Infrastructure: Tight Dependencies

Customer - OrderProcessor – Infrastructure: Tight Dependencies

From a relational point of view, the Customers entity has a dependency on the OrderProcessor entity which in turn directly communicates with the X-4000, the infrastructure layer. In other words Customers is tightly coupled with the infrastructure layer. In the above scenario, the Customers entity is not "closed" for modifications. If the X-4000 goes away or is changed then the Customers class needs to be modified to support the lower level changes. At this point the design of the GetAllOrders function is in violation of the open/closed principle and is not unit testable.

NOTE

Though not necessarily the subject of this article, the dependency inversion principle is also violated with this design.

So what, my software works fine.

The steady X-4000 has been replaced by the X-5000 with support for WCF services. Customers can now use their own service architectures to communicate with the system. The CIO now has "SOA" under his belt, is going to a board meeting and is getting a bonus. For the developer however, the system needs to be modified. The source code of the Customers class needs to be changed, tests updated, regression and functionality testing needs to be revisited and a redeployment of software across a farm needs to occur. Not an ideal scenario. Let's revisit the design and add in an abstraction, an interface, in place of the concrete class OrderProcessor. We'll add IOrderProcessor to the mix.

Figure 2: Customer - IOrderProcessor – Infrastructure: Loose Dependencies

Customer - IOrderProcessor – Infrastructure: Loose Dependencies

Decoupling and extendibility support

As example 2 demonstrates, there is now an abstraction (decoupling) between Customers and OrderProcessor via IOrderProcessor. The implementation details surrounding the infrastructure layer (the X-4000) are now delegated to a concrete class but referenced via a contractual interface. The Customers entity has no knowledge of the OrderProcessor entity; rather, it uses a contract without implementation details.

It's now time to instantiate our concrete implementation, the "extensibility" part of the puzzle. For this we'll use a dependency injection (DI) pattern. Followed by the use of IoC, a dependency injection pattern is going to allow the concrete implementation of IOrderProcessor to be determined dynamically at runtime. This will allow for the benefit of scalability via a "plug and play" interface to concrete type architecture. If the concrete implementation needs to be modified then the interface to type mapping will handle the handshake. The user of the interface, Customers.cs, will not have to be modified. The Customers entity is now closed for modification but open for extendibility. There are many different ways to implement an injection pattern. However, some of the better known 3rd party tools are Structure Map, Microsoft’s Unity (patterns and practices block) and Ninject. For this article we'll be using Microsoft's Unity tool. I'm not going to go into too many details regarding Unity, but any Google search will get you on your way.

Dependency Injection Setup

Let's add a new class to handle the Unity container. UnityBootStrap.cs will be the class that registers the interface to concrete implementation mappings. Keep in mind the implementation details regarding this bootstrapper are for example purposes only. Depending on the project and particular architecture, a given Unity bootstrapper may be implemented differently. The main take away from this is that Unity, a dependency injection tool, is going to handle the mapping and resolving between interfaces and concrete implementations, the extensibility.

Listing 1: Unity Bootstrapper

NOTE

The implementation of the bootstrapper in example 3 is more of a conceptual example rather than an exact code example. The implementation of a Unity bootstrapper will be dependent on the particular architecture and application type being used.

Resolving the Interface

Now that a Unity IoC container is setup and ready for use, we can start resolving the contracts to their respective implementations. We can now "inject" the OrderProcessor entity into the Customers entity. Same as in example 3 (our bootstrapper), there are many different approaches when injecting a dependency into a type. For this article we'll use what is known as "constructor injection", perhaps the most widely used technique. In constructor injection, the interface is a parameter in the constructor of the parent type. When a new instance of the parent is created, its constructor is invoked and the "injected" type is resolved and assigned to a local variable for use in the parent code.

Listing 2: Constructor Injection

The Results

As you can see in example 4, we've wrapped our hierarchy in a 3 tier system, service (aggregate root), domain object (entity) and repository. In terms of our example this is CustomersService -> Customers -> OrderProcessor. By doing this we've also started down the path of DIP or the Dependency Inversion Principle. The Dependency Inversion Principle is not the focus of this article (future posts), but states that in a given hierarchy, higher level, policy making types should not have dependencies on lower level types. In other words, the CustomersService root should not depend on the Customers entity and the Customers entity should not depend on OrderProcessor repository. These classes should be closed for modifications but open for extension. If Customer was to change, it should have no effect, in theory, on CustomersService.

However, let's get to the real importance of the structure, the constructor "injection". By resolving the Customers entity in the CustomersService root using Unity, the constructor of the Customers entity is instantiated and the IOrderProcessor parameter is automatically resolved via Unity. No setup is necessary for this behavior, Unity and all the other DI tools (Structure Map, Ninject, etc.) do this. This pattern is known as "constructor chaining". So, when the Customers entity is instantiated it has a dynamic and working version of a concrete object of type IOrderProcessor. If the system now required use of the X-5000, a new version of the OrderProcessor repository can be created independently of the Customers entity and be mapped in the Unity bootstrapper. The Customers entity remains blissfully unaware of the concrete implementation of the OrderProcessor repository. The Customers entity can now remain closed to modifications but open to extension via the abstraction IOrderProcessor.

Alright, you talked me into it, let's write a quick test

Now that the system is using these nifty interface things we can easily "mock" objects and write unit tests.

Listing 3: Unit testing the Customers.GetAllCustomerOrders() method

Conclusion

The Open/Closed principle is a key component of agile design and object oriented design. When using this principle as a guideline, the architecture will start to naturally fall in place by having less dependencies, supporting extensibility, scalability and testability. There is no one and only way to implement "open/closed" software. Also, this principle is of course not limited by any means to .NET development. The important take away items are:

The Principle: A software module, class, entity, etc. should be open for extension but closed for modifications.

Abstraction: The open/close principle is possible via abstractions and/or interfaces.

Extension: The "open" part of the open/close principle. Allow classes, modules, entities, etc. to be extend-able.

Dependency Injection: A software pattern that will allow for concrete representations of abstractions (interfaces) to be dynamically surfaced at runtime.

Dependency Inversion Principle: Arrange and organize software hierarchies so that policy driving higher level modules do not have dependencies on lower level modules.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Chris Zavaleta

Chris is a software engineer / consultant / architect specializing in Microsoft technologies...and whatever else he needs to learn in the next 10 minutes...

This author has published 2 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Developing a Hello World Java Application and Deploying it in Windows Azure - Part I
This article demonstrates how to install Windows Azure Plugin for Eclipse, create a Hello World appl...
Android for .NET Developers - Building a Twitter Client
In this article, I'll discuss the features and capabilities required by an Android application to ta...
Ref and Out (The Inside Story)
Knowing the power of ref and out, a developer will certainly make full use of this feature of parame...
Developing a Hello World Java Application and Deploying it in Windows Azure - Part II
In this article we will see the steps involved in deploying the WAR created in the first part of thi...
Android for .NET Developers - Using Web Views
In this article, I'll show a native app that contains a web-based view. The great news is that HTML ...

You might also be interested in the following related blog posts


Using WCF with SQL Azure and Telerik OpenAccess read more
Connecting to SQL Azure with Telerik OpenAccess read more
How to display data from different tables using one data source read more
Telerik OpenAccess WCF Wizard October CTP read more
Building A Product For Real read more
Dont Repeat Yourself read more
Principles, Patterns, and Practices of Mediocre Programming read more
Binding Hierarchical RadGrid for ASP.NET Ajax with Telerik OpenAccess ORM read more
Binding Hierarchical RadGrid with Telerik OpenAccess ORM read more
Using the Telerik OpenAccess WCF Wizard with Multiple Versions of OpenAccess read more
Top
 
 
 

Discussion


Subject Author Date
placeholder Great article Chris! Andrew Siemer 11/3/2010 10:58 AM

Please login to rate or to leave a comment.

Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.