SOLID – S: Single Responsibility Principle SOLID – L: Liskov Substitution Principle
Jul 20

SOLID is a combination of five important design principles:
SSingle Responsibility Principle
OOpen Closed Principle
LLiskov Substitution Principle
IInterface Segregation Principle
DDependency Inversion Principle

This post is about the Open Closed Principle.

Definition

The open closed principle states that classes should be open for extension but closed for modification.
You shouldn’t touch written code, you should instead add new. Whether it’s done by sub classing and overriding or otherwise.

What are the benefits?

Stability – If you don’t change existing code, you can’t break it.
Error Reduction – If you always have to make more or less the same changes over and over again you might make a mistake.
Clarity and Readability – Switch statements aren’t that great for readability / clarity.

When should you apply it?

If you see large if and else if blocks or switch statements, you know you could and maybe should refactor to the open closed principle. Using an IoC container makes it child’s play.

A bad Example

Lets say you have a system that deals with messages, therefore, you have different kinds of messages that all inherit from IMessage. Usually you’d have some kind of MessageHandler that has a method like void Handle(IMessage message), without the OCP you’d just make an large switch statement for each message type you’d like to handle.

public interface IMessage
{
    Guid Id { get; }
    DateTime SendingTime { get; set; }
}

public interface IMessageHandler
{
    void Handle(IMessage message);
}

public class MessageHandler : IMessageHandler
{
    public void Handle(IMessage message)
    {
        if (message is ChatMessage)
        {
            HandleChatMessage((ChatMessage)message);
        }
        else if(message is PrivateChatMessage)
        {
            HandlePrivateChatMessage((PrivateChatMessage)message);
        }
        else if(message is UserConnectedMessage)
        {
            HandleUserConnectedMessage((UserConnectedMessage) message);
        }

    }
}

The Handle method might quickly span multiple screens and the MessageHandler itself might easily span many hundreds of lines as contains every handling method.

By the way: this is also a classical violation of the Liskov Substitution Principle, the L in SOLID.

With the help of an IoC container we can easily refactor it and thereby make it follow the OCP and the LSP.

The better Way

public interface IMessageHandler
{
    Type Handles { get; }
    void Handle(IMessage message);
}

public interface IMessageHandler<T> : IMessageHandler where T : IMessage
{
    void Handle(T message);
}

public abstract class AbstractMessageHandler<T> : AbstractMessageHandler,
    IMessageHandler<T> where T : IMessage
{
    public override Type Handles
    {
        get { return typeof (T); }
    }

    public abstract void Handle(T message);

    public override void Handle(IMessage message)
    {
        Handle((T) message);
    }
}

public abstract class AbstractMessageHandler : IMessageHandler
{
    public abstract Type Handles { get; }

    public abstract void Handle(IMessage message);

    protected IMessageTargetChoice SendMessage(IMessage message)
    {
        return new MessageTargetChoice(message);
    }
}

public class PrivateChatMessageHandler : AbstractMessageHandler<PrivateChatMessage>
{
    public override void Handle(PrivateChatMessage message)
    {
        SendMessage(message).To(message.ReceiverId);
    }
}

In order to handle a different method you just subclass the AbstractMessageHandler. No clutter in any other place.

On a side note: the PrivateChatMessageHandler in this case just forwards the received message to its destination.

The Dispatcher

public class MessageDispatcher
{
    private readonly Dictionary<Type, IMessageHandler> _handlers;

    public MessageDispatcher(IMessageHandler[] handlers)
    {
        _handlers = new Dictionary<Type, IMessageHandler>();

        foreach (var handler in handlers)
        {
            _handlers.Add(handler.Handles, handler);
        }
    }

    public void Dispatch(IMessage message)
    {
        new Thread(() =>
                       {
                           var type = message.GetType();

                           if (!_handlers.ContainsKey(type))
                           {
                               throw new ArgumentException();
                           }

                           _handlers[type].Handle(message);
                       });
    }
}

As you can see, the dispatcher takes an array of IMessageHandler as its constructor argument, every good IoC container will happily fill it when the MessageDispatcher is requested. In StructureMap for example, you’d just say that it should take all classes that implement IMessageHandler, then all you have to do is sub class the AbstractMessageHandler.

Critics about the OCP

Some state that it is in conflict with YAGNI (you ain’t gonna need it) or KISS (keep it simple, stupid). I’d say it depends, if you do it upfront with the vague expectation you might need it, then yes, you at least might violate those two principles. If on the other hand, you have something like a message handler, I’d say its more than fine to implement it in such a way upfront as a message handler handles different message types by design.
Anyway, it’s usually easy to refactor those cases.

Bookmark and Share

9 Responses to “SOLID – O: Open Closed Principle”

  1. [...] of five important design principles: S – Single Responsibility Principle O – Open Closed Principle L – Liskov Substitution Principle I – Interface Segeration Principle D – [...]

  2. SOLID – O: Open Closed Principle | Coding Efficiency…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  3. 9eFish says:

    SOLID – O: Open Closed Principle | Coding Efficiency…

    9efish.感谢你的文章 – Trackback from 9eFish…

  4. [...] of five important design principles: S – Single Responsibility Principle O – Open Closed Principle L – Liskov Substitution Principle I – Interface Segeration Principle D – [...]

  5. [...] of five important design principles: S – Single Responsibility Principle O – Open Closed Principle L – Liskov Substitution Principle I – Interface Segregation Principle D – [...]

  6. [...] is a combination of five important design principles: S – Single Responsibility Principle O – Open Closed Principle L – Liskov Substitution Principle I – Interface Segregation Principle D – Dependency [...]

  7. [...] SOLID – O: Open Closed Principle | Coding Efficiency http://www.codingefficiency.com/2009/07/20/solid-o-open-closed-principle – view page – cached This post is about the Open Closed Principle. The Open Closed Principle is part of SOLID, five important design principles. — From the page [...]

  8. [...] Open Closed Principle (OCP) – A classe deve ser aberta para extensão (sublcasses e sobrescrita de metodos) e fechada para modifiação * [...]

  9. [...] responsabilidades: Baixo Acoplamento Padrões para atribuir responsabilidades: Alta Coesão SOLID – O: Open Closed Principle | Coding Efficiency SOLID – I: Interface Segregation Principle | Coding Efficiency SOLID – D: Dependency [...]

Leave a Reply

preload preload preload