The Open/Closed Principle – OCP

This entry is part 4 of 4 in the series SOLID

The second principle we’re going to talk about is the Open/Closed Principle. If we look at Wikipedia, we will see the following definition:

The Open/Closed Principle states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Problem Statement

What it means, in essence, is that when we need to introduce a change, we shouldn’t dig deep into the system and change its behavior by changing dozens of classes. We should be able to introduce a change by adding new code, not by changing the existing. Such software which allows changes via introducing new classes rather than via changing the existing source code is open for extension and closed for modification.

How to change behavior without changing the code base? The answer is simple – dynamic dispatch or polymorphism. We can achieve a very supple design by harnessing the power of Object-Oriented programming. In practice, it means that we need to rely on abstractions, rather than on concrete or direct implementations. In C#, we will rely on interfaces and abstract classes.

Why OCP?

Ok, but what’s the problem with changing the existing code? Why do we need to adhere to the Open/Closed Principle at all?

  • The first reason is that there is a high chance of introducing bugs during the modification process. It’s much easier to introduce a bug modifying something that is complex and already exists than to introduce a bug adding new code.
  • The second reason is relevant, and it is almost always relevant when you’re trying to modify the behavior of an API which is already in use by many clients.

The first danger here is to change the behavior expected by clients. Imagine that there are one thousand clients of a method which returns 100 in the case of failure and that clients have some compensation logic for that case. Imagine then that you modified the behavior of the method upon which those one thousand clients depend. The consequences are dramatic. I would pray for that developer who decided to introduce such a change, which breaks the code of one thousand clients.

The second danger is to modify the API’s signatures. Such modifications immediately break all the dependent clients. Believe me; clients will not thank you for such modifications. By the way, breaking changes may also cause ripple effects because there can exist hundreds of clients which depend on other clients which depend on your code.

Modifying the behavior by adding new code is actually conforms to what customers think when they ask for new features. When customers ask for a new feature they think that features will be added, they don’t think that developers will modify anything.

The idea might seem ridiculous from the first sight. Indeed, is that possible to write code that will never be modified again? Of course, this is not possible in practice. Well, theoretically it’s possible to write all the code in this way, but it is not practical. In programming, we often set some ideal goals which can’t be ever achieved. For example, we strive to write correct code, and theoretically, we can write code the correctness of which can be statically verified. Unfortunately, in 99% of cases, such development process is so expensive that no one is capable of investing so much money.

Another point which is obvious but I need to mention it is that we absolutely must modify the existing code if it contains a bug. So, bug fixing is OK from the Open/Closed principle point of view.

Mayer’s Definition of OCP

Let’s take a step back and look back into 1988. 1988 is the year when Bertrand Meyer described OCP for the first time. Meyer treated OCP a little bit differently than Uncle Bob. OCP from Meyer’s point of view was essentially an equivalent to the Protected Variation pattern. The Protected Variation pattern means the following: Identify points of predicted variation and create a stable interface around them. Talking here about an interface, we don’t mean the C# interface construct. An interface implies any API. A class has its own interface which is represented by its public API. We can notice here a subtle difference between Meyer’s and Martin’s definitions. Meyer’s definition is more about backward compatibility at the API level. So, if changes don’t break the backward compatibility, then OCP is met.
When exactly may we need to change the behavior without changing the interface? For example, when a new client appears which requires very similar but a little bit different behavior.

Single Choice Principle

The last point I want to address is the Single Choice Principle closely related to Open/Closed principle. I prefer to name this principle the “Single Source Principle” and you’ll understand why shortly. Let’s imagine the case when we need to decide which implementer to create depending on an argument. For solving such problems, we often rely on the Factory pattern.

Here is an example:

public class BankTerminalFactory
{
    public static IBankTerminal CreateBankTerminal(BankTerminalModel model)
    {
        switch (model)
        {
            case BankTerminalModel.Brp:
                return new BrpTerminal();
            case BankTerminalModel.Dcp:
                return new DcpTerminal();
            default:
                throw new ArgumentException("Unknown model");
        }
    }
}

As you see here, we have a factory method which takes a parameter of BankTerminalModel enumeration. Depending on that parameter, the factory method creates a corresponding implementation of the IBankTerminal interface.
Does this factory method violate the Open/Closed principle? Of course, it does.

If we roll out a new implementation of the IBankTerminal interface, we will modify this code by adding a new case statement.

How can we solve the problem adhering the Open/Closed principle? We can create a factory of the BankTerminalFactory. But who will create that factory of this factory? Somewhere, in the end, we need to select the appropriate implementation and create the instance. We can partly solve the problem by introducing an IoC-Container, but this will just move the problem, it doesn’t solve the problem entirely.

If you’re not familiar with the notion of Dependency Injection and IoC-containers, as a side note, I’d recommend you a book which is an absolute bestseller written by Mark Seemann “Dependency Injection in .NET”.

So, the Principle of Single Choice sounds like this:

“Whenever a software system must support a set of alternatives, one and only one module in the system should know their exhaustive list.”

Yes, we’re violating the OCP by having such an implementation of the factory method, but there is no meaning to try to push the OCP further by any means. We should understand that in such cases all we need is to isolate such a responsibility in a single module and only that module should be changed in case of introducing new implementations.

Classic Violation

Here is an example of a classic OCP violation:

public string FindDevice(DeviceModel model)
{
    SerialPort port = new SerialPort();
    switch (model)
    {
        case DeviceModel.BillAccepterCashCode:
            {
                port.BaudRate = 9600;
                port.Parity = Parity.Even;
                port.Handshake = Handshake.RequestToSend;
                return Find(port);
            }
        case DeviceModel.BillDispenserEcdm:
            {
                port.BaudRate = 4800;
                port.Parity = Parity.Mark;
                port.Handshake = Handshake.RequestToSendXOnXOff;
                return Find(port);                        
            }              
        case DeviceModel.CoinAccepterNri:
            {
                port.BaudRate = 19200;
                port.Parity = Parity.Odd;
                port.Handshake = Handshake.XOnXOff;
                return Find(port);                        
            }
        case DeviceModel.CoinDispenserCube4:
            {
                port.BaudRate = 9600;
                port.Parity = Parity.Space;
                port.Handshake = Handshake.None;
                return Find(port);                        
            }
        case DeviceModel.CoinDispsenerSch2:
            {
                port.BaudRate = 4800;
                port.Parity = Parity.Even;
                port.Handshake = Handshake.None;
                return Find(port);                        
            }
        default:
            throw new ArgumentException($"Unknown model: {model}.");
    }            
}

We have a switch statement here to run the searching for a device method. In fact, if there is only one switch statement used with the DeviceModel enumeration, then the OCP is not violated. If switch statements used with that enumeration start to spread all over the code base then we have a violation of the OCP. That would be a violation since you couldn’t easily extend that enumeration. Adding a value to that enumeration would force you to change all the corresponding switch statements in the code base (handling new value). This example demonstrates the OCP violation from the Martin’s point of view. We don’t have any problems with this code from the Mayer’s point of view. At least yet.

Classic Fix

We firstly need to abstract away the Find method by extracting the IDevice interface.

public interface IDevice
{
    string Find();
}   

Now we can implement it in different device models. Here is just one example:

public class CoinDispenserCube4 : IDevice
{
    public CoinDispenserCube4()
    {
        Port = new SerialPort
        {
            BaudRate = 9600,
            Parity = Parity.Space,
            Handshake = Handshake.None
        };
    }

    public SerialPort Port { get; }

    public string Find()
    {            
        foreach (string portName in SerialPort.GetPortNames())
        {
            //test if device is can be connected
            Port.Write("special code");
            if (Port.ReadByte() == 0)
                return portName;
        }
        return null;
    }
}

Each device has to implement this interface. So, the internal details of searching for a concrete device are encapsulated in corresponding device classes.

The last piece I want to implement is an orchestrator class which accepts the IDevice interface and delegates the searching process to the IDevice implementer:

public class DeviceFinder
{
    private readonly IDevice _device;

    public DeviceFinder(IDevice device)
    {
        _device = device;
    }

    public string Find()
    {
        return _device.Find();
    }
}

If you have some common logic between devices, it might be beneficial to use an abstract class instead of an interface. All in all, we ended up with a design which allows us to roll out a new Device implementation without changing the code base, we just should add a new class which represents a device. So, our code is closed for modification and open for extension.
In both cases, we end up with multiple classes which inherit either from an abstract class or an interface.

Consequences

Such approach provides simpler extension if a new type is required, but it makes harder to add new operation since you’ll have to implement them in each class. As I’ve already said, there is always some kind of tradeoff involved. Welcome to engineering!

In this case, we harness the power of inheritance and composition. Client code, in this case, depends on abstraction. Client code should pass a plug into a model. The model itself doesn’t rely on inheritance; it relies on composition, it just stores the IDevice interface delegating to it appropriate responsibilities.
There is one little thing I want to mention before moving further. Do you think that applying that refactoring we took into account all the possible directions in which the system can grow? Not a chance!

That is a fundamental problem of predicting the future. Unfortunately, we can only assume what will happen in the future. That’s why customers always can find features which don’t fit into your design. Implementing them, you’ll have to redesign and rewrite parts of the system which already exist.
Does this fact make OCP and other principles unimportant? Does it mean that we can forget about any principles, in case we can’t predict the future anyway?

And here is the answer. Yes, unfortunately, we can’t achieve a super-supple design which allows to introduce any possible features without problems. We can only struggle to overcome the problem of unpredictable future. Developers came up with complex development methodologies the intent of which is to allow us to not bother so much about the problem of unpredictable future.

The first one is an old waterfall process, or the so-called “big design upfront”. This methodology suggests struggling with unpredictable future by trying to consider all the details of the project, creating all the possible abstractions, introducing extension points in all those places where a system can potentially be changed. The worldwide practice shows that in most cases adhering to the principles of this methodology leads to overengineering and thus to expenses which grow very fast.

The second methodology is called “Agile”. There are many agile methodologies nowadays, and we are not going to discuss them. I’ll only give a short description of what “Agile” is in essence. Agile methodologies are aimed at setting relationships between the development team and a customer. From the first day of a project, development team starts to work on a customer rolling out software by small increments. So, the Agile methodology suggests the iterative process of development with high involvement of a customer into the development process. In conjunction with SOLID principles and other best practices it allows to anticipate possible changes and react to them as quickly as possible by performing the refactoring. Of course, Agile doesn’t imply that a development team doesn’t design anything upfront. Agile is somewhere between two extreme points of no design up front and big design up front.

Of course, there are no techniques which can for sure protect you from all the possible changes. But the Agile process is how we’re trying to live with all that injustice in the modern days.

Common Smells

The most common smell of OCP violation is the appearance of many conditional branches either with if-else or with switch/case statements.

You have three common approaches to adhere to the OCP.

  • Building a handling sequences with delegates which are naturally the implementations of the chain of responsibility pattern. Look for the Chain of Responsibility pattern. This pattern is amost built-in with events into the .NET platform. So, we use it when we need to pass a message and handle it by several handlers. The chain of responsibility provides the way to implement that in a loosely coupled manner. Otherwise you can end up with coupled objects.
  • Another way is to rely on a classic inheritance with a template method pattern or to implement a Visitor pattern if you need to provide the ability for a client to define operations.
  • The last way is to rely on a composition rather than on inheritance. Most likely you would rely on the Strategy pattern to implement a composition. Remember, that composition is generally more preferred than classic inheritance.

Conclusion

The open/closed principle is all about changes. Following OCP, you can achieve a very supple design which is ready for introducing new features without pain. To achieve the supple design, it should be open for extension and closed for modification. So, the most frequently changing parts of the system have to be isolated.

One very important thing is that we need to isolate a responsibility for creating objects in a single module and only that module should be changed in case of introducing new implementations. This principle is called the “Single Choice” principle.

You’ve also learned that we don’t have at our disposal any magic tools which allow us to protect ourselves from all the possible changes completely. And it doesn’t mean that we should start to create tons of extensions points anticipating changes in the future all over the code base. To overcome the problem, we’re trying to use the so-called “agile design.”


You can take the full video course “Software Architecture: Meta and SOLID Principles” with a huge discount clicking here.

Also, consider the option to become a patron on Patreon, thank you in advance!

Single Responsibility Principle – SRP

This entry is part 3 of 4 in the series SOLID

SRP Definition

In this article, we will look at what is single responsibility principle and look at some simple examples in C# which demonstrate the single responsibility principle and how to adhere to the SRP.
Single Responsibility Principles or SRP in short states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. This is the definition taken from Wikipedia. It’s very hard to understand what does it mean. For example, the first two questions which come to my mind are:

  • how to define those responsibilities?
  • how to calculate the number of responsibilities of a certain class?

Continue reading

Software Architecture: Meta and SOLID Principles in C#

This entry is part 1 of 4 in the series SOLID

Learn how to develop maintainable software systems applying Meta and SOLID Principles.

Teaching Approach

No fluff, no ranting, no beating the air. I esteem your time. The course material is succinct, yet comprehensive. All important concepts are covered. Particularly important topics are covered in-depth. For absolute beginners, I offer my help on Skype absolutely free, if requested.

Take this course, and you will be satisfied!

SOLID is an acronym which stands for SRP, OCP, LSP, ISP and DIP. These five acronyms in their turn stand for:

  • Single Responsibility Principle
  • Open/Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

In this course, you’ll learn how to apply meta and SOLID principles so that your application will live a long healthy life. It means you are going to learn how to write code of the high quality: readable, understandable and reliable.

Continue reading

Abstract Class VS Interface in C#

You can open any C# tutorial and you’ll find some information about abstract classes and interfaces. Most likely, you’ll not find any information about what is the difference between abstract class and interface.
This theme was discussed earlier on the Internet several times but I want to consolidate all the most important thoughts regarding the problem of choosing between an abstract class and interface in this post.
Continue reading

Designing and Implementing API in C#

Nowadays I’m writing my new programming video course. I chose an interesting topic for the next course: “Designing and Implementing API in C#”.

How to design API? In this course, you’ll learn how to design and implement types in C# so that the other developers won’t hate you when using one of the types developed by you. It means you are going to learn how to write code of the high quality: readable, understandable and reliable.

Improve your knowledge in object-oriented programming in the context of clean coding and building types of high quality.

  • Understand the characteristics of a well designed type
  • Grasp the principles of the convenient API development
  • Write clean code, get rid of unpleasant smells
  • Learn about what exceptions are intended for and how to throw and catch them properly
  • Protect your types from the incorrect usage making them properly encapsulated

And this is far from the full list of topics we will cover in this course.
Continue reading

MVVM: When EventAggregator (aka MessageBus) is an Anti-Pattern

Very often we can see that developers tend to use static message buses for all kind of interactions between objects. For those, who unaware of this pattern I want to recall that this pattern allows organizing loosely coupled communication channel between objects which don’t want (or can’t) to know each other.
Let’s have a brief look at how a regular example of using this pattern may look like:

public class Sender {
    private readonly IEventAggregator eventAggregator;

    public Sender(IEventAggregator eventAggregator) {
        this.eventAggregator = eventAggregator;
    }

    public void Action() {
        eventAggregator.Publish(new Message());
    }
}

public class Receiver : IHandle<Message> {
    public Receiver(IEventAggregator eventAggregator) {
        eventAggregator.Subscribe(this);
    }
    public void Handle(Message message) {            
    }
}

Continue reading

Money Type as Value Object, or Don’t Rely on Primitive Types!

Primitive Types Obsession Problem

Today I’m going to discuss the problem of using primitive types instead of abstractions. This problem was discussed in the blog of Mark Seemann. Read it, if you haven’t read it yet.
In this post I’m going to talk about Money type as an abstraction instead of using decimal type for representing money-values.
In the last project I’ve been participating in, we relied on a decimal and integer types for a long time. From the beginning, we knew that using primitive types for values of that kind is an anti-pattern, but we stubbornly have been using them. In the US there are cents and dollars. In the Russian Federation – rubles and kopeks. 1 ruble = 100 kopeks. Our system inter-operated with an external system which performed all its calculations in kopeks. So it required kopeks as the input and returned kopeks as the output. If we wanted to pass in 2rubles and 50kopeks, then we passed in Int32 amount = 250;

Continue reading

When Method Is Better Than Property?

That’s a well known question. Actually, I knew the difference between methods and properties long ago. Despite of that, recently I stumbled upon my own incorrect choice between those semantic constructions. That’s why I decided to write this post – in order to solidify the understanding of the difference between property and method.

Method vs Property fail in BCL

The most notorious fail of creating a property inside the BCL (FCL) in .NET instead of a method is the DateTime.Now property. In my opinion, this particular case is not the worst in history, though it’s an embarrassing one. So, what’s wrong with that the DateTime.Now is implemented as a property rather than a method? This isn’t wrong from the point of the actual implementation, it is implemented correctly, the point is that this is semantically wrong from the API design perspective. If every time the getter of a property returns different values, then this is a method, not a property. Why? Because a property is an attribute of an entity. For example, Year is an attribute of a Date, so it would be correct to get the year as it follows: DateTime.Now().Year. Yes, that’s it, that’s how it should look like, not like a chain wreck – DateTime.Now.Year. By the way, similar by the case Guid.NewGuid() member is implemented as a method. In this case, the word “New” dictates that this should be a method, but for example they could have named it Guid.Next and even in this case it should be a method, as well as the DateTime.Now!

Continue reading

Hidden Dependencies as a Smell

Mark Seemann has written a nice post  “Service Locator violates encapsulation”. The name of the post speaks for itself that it’s about a pattern (anti-pattern) named Service Locator. When a programmer arbitrarily inside the code base calls for the IoC-container to resolve a dependency of an object – he uses a Service Locator anti-pattern. Mark provides the following example:

public class OrderProcessor : IOrderProcessor
{
    public void Process(Order order)
    {
        var validator = Locator.Resolve<IOrderValidator>();
        if (validator.Validate(order))
        {
            var shipper = Locator.Resolve<IOrderShipper>();
            shipper.Ship(order);
        }
    }
}

Continue reading