solid-principles

SOLID in C#: The Dependency Inversion Principle

What Is This Principle?

The Dependency Inversion Principle is comprised of two rules:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.

This principle is primarily concerned with reducing dependencies amongst the code modules. We can think of it as needing the low-level objects to define contracts that the high-level objects can use, without the high-level objects needing to care about the specific implementation the low-level objects provide.

Please note that the DIP is not quite the same thing as Dependency Injection.

Dependency Inversion Principle - Would you solder a lamp directly to the electrical wiring in a wall?

A Simple Example

Let's imagine that we are building an notifications client (a trite example, I know, but bear with me). We want to be able send both email and SMS text notifications. Here are some sample classes:

public class Email  
{
    public string ToAddress { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }
    public void SendEmail()
    {
        //Send email
    }
}

public class SMS  
{
    public string PhoneNumber { get; set; }
    public string Message { get; set; }
    public void SendSMS()
    {
        //Send sms
    }
}

public class Notification  
{
    private Email _email;
    private SMS _sms;
    public Notification()
    {
        _email = new Email();
        _sms = new SMS();
    }

    public void Send()
    {
        _email.SendEmail();
        _sms.SendSMS();
    }
}

Notice that the Notification class, a higher-level class, has a dependency on both the Email class and the SMS class, which are lower-level classes. In other words, Notification is depending on the concrete implementation of both Email and SMS, not an abstraction of said implementation. Since DIP wants us to have both high and low-level classes depend on abstractions, we are currently violating this principle.

The two classes are said to have high-coupling. Remember from the post on the Single Responsibility Principle that we don't want such coupling in our code if it can be avoided, since that increases the risk that both of these classes will need to change if one of them changes implementation.

(One trick you can use to determine how tightly coupled your code is is to look for the new keyword. Generally speaking, the more instances of new keyword you have, the more tightly coupled your code is.)

So, since all the SOLID principles are about reducing dependencies, how can we refactor this code to remove the dependency between Notification and Email? We need to introduce an abstraction, one that Notification can rely on and that Email and SMS can implement. Let's call that IMessage.

public interface IMessage  
{
    void SendMessage();
}

Next, Email and SMS can implement IMessage:

public class Email : IMessage  
{
    public string ToAddress { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }
    public void SendMessage()
    {
        //Send email
    }
}

public class SMS : IMessage  
{
    public string PhoneNumber { get; set; }
    public string Message { get; set; }
    public void SendMessage()
    {
        //Send sms
    }
}

And, finally, we can make Notification depend on the abstraction IMessage rather than its concrete implementations:

public class Notification  
{
    private ICollection<IMessage> _messages;

    public Notification(ICollection<IMessage> messages)
    {
        this._messages = messages;
    }
    public void Send()
    {
        foreach(var message in _messages)
        {
            message.SendMessage();
        }
    }
}

With this refactoring, all Notification cares about is that there's an abstraction (the interface IMessage) that can actually send the notification, so it just calls that and calls it a day.

In short, we have allowed both high-level and low-level classes to rely on abstractions, thereby upholding the Dependency Inversion Principle.

Potential Hazards

We cannot just implement a bunch of interfaces and call that DIP. Creating code just for the sake of having it leads to unnecessary complexity, the mortal enemy of maintainability. But we can use those interfaces to implement the necessary contracts the high-level objects need to call.

The key word there is necessary. As with all other coding, we should only implement code that is necessary and provides a benefit to the application.

Is It Worth It?

YES. As with the other SOLID principles, the primary purpose of Dependency Inversion is to remove dependencies from our code, which is a noble goal. Dependency Inversion enables us to bake in some change tolerance to our code, to confront change and roll with it, allowing our system to adapt to large requirements and changing rules with as much grace as possible.

SOLID in C# - The Interface Segregation Principle

What Is This Principle?

The Interface Segregation Principle states that no client code object should be forced to depend on methods it does not use. Basically, each code object should only implement what it needs, and not be required to implement anything else.

Interface Segregation Principle - You want me to plug this in, where?

Benefits

The ISP is all about reducing code objects down to their smallest possible implementation, and removing dependencies the object doesn't need to function properly. The result of implementing this principle, generally speaking, is to have a lot of small, focused interfaces that define only what is needed by their implementations.

A Simple Example

Let's say we are tasked with modeling a newly-opened clothing store that sells jeans. At the moment, that's all they will sell. Now we, being smart programmers, think that we should model this so that if the store wants to sell different products in the future, we can model them appropriately. So, we create an interface for the products and a class for Jeans:

public interface IProduct  
{
    int ID { get; set; }
    double Weight { get; set; }
    int Stock { get; set; }
    int Inseam { get; set; }
    int WaistSize { get; set; }
}

public class Jeans : IProduct  
{
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
}

Just a week later, the owner of the store comes to us and tells us that they're now going to sell baseball caps. So, wanting to use the interface we already created, we implement BaseballCap:

public class BaseballCap : IProduct  
{
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
    public int HatSize { get; set; }
}

But wait! Why does a baseball cap have an inseam or waist size? Those properties don't make sense for a baseball cap, but because they were defined in IProduct, BaseballCap must implement them.

So what's the solution? Refactor! But how?

Well, what properties do both BaseballCap and Jeans need? Those properties can become the new IProduce interface:

public class Jeans : IProduct  
{
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
}

We currently sell jeans, but Inseam and WaistSize can apply to any type of pants, so let's create an IPants interface:

public interface IPants  
{
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
}

We should be willing to bet that baseball caps won't be the only kinds of hats we'll sell, so we also make a focused IHat interface:

public interface IHat  
{
    public int HatSize { get; set; }
}

Now we can implement both Jeans and BaseballCap:

public class Jeans : IProduct, IPants  
{
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int Inseam { get; set; }
    public int WaistSize { get; set; }
}

public class BaseballCap : IProduct, IHat  
{
    public int ID { get; set; }
    public double Weight { get; set; }
    public int Stock { get; set; }
    public int HatSize { get; set; }
}

Each class now has only properties that they need. Now we are upholding the Interface Segregation Principle!

Potential Hazards

As you might have guessed from the example, the ISP can potentially result in a lot of additional interfaces. If the store was to start selling t-shirts, for example, we would probably create another interface IShirt. There is a possibility that we will have a LOT of interfaces if we strictly adhere to this rule.

There's another potential hazard with implementing this principle: we could have interfaces that are only ever used for one implementation. For example, if we create that IShirt interface and the store only ever sells t-shirts, did we really need the interface? I'd argue that such interfaces are code bloat and should be removed until such time as they are needed, but many people disagree with me.

Is It Worth It?

Maybe. This is the only SOLID principle I feel ambivalent about. On the one hand, aggressively adhering to this principle allows for much more flexible and modifiable code, something I'm a big fan of and is very useful in real-world projects. On the other hand, I'm an extreme deletionist, and would much prefer to delete code rather than write it, so writing a bunch of extra interfaces doesn't sit well with me.

What do you think? Is following the Interface Segregation Principle worth the potential benefits? Or is it more work than is necessary? Let me know in the comments!

SOLID in C#: The Liskov Substitution Principle

What Is This Principle?

The Liskov Substitution Principle(LSP), named for and originally defined by Barbara Liskov, states that we should be able to treat a child class as though it were the parent class. Essentially this means that all derived classes should retain the functionality of their parent class and cannot replace any functionality the parent provides. The LSP is very similar in principle to the Open/Closed Principle.

Liskov Substitution Principle - If it looks like a duck, quacks like a duck, but needs batteries, you probably have the wrong abstraction.

Benefits

This principle aims to keep functionality intact. It's main purpose is to guarantee that objects lower in a relational hierarchy can be treated as though they are objects higher in the hierarchy. Basically, any child class should be able to do anything the parent can do.

A Simple Example

We'll use the classic Circle-Ellipse problem to demonstrate this principle. Let's imagine that we need to find the area of any ellipse. So, we create a class that represents an ellipse:

public class Ellipse  
{
    public double MajorAxis { get; set; }
    public double MinorAxis { get; set; }

    public virtual void SetMajorAxis(double majorAxis)
    {
        MajorAxis = majorAxis;
    }

    public virtual void SetMinorAxis(double minorAxis)
    {
        MinorAxis = minorAxis;
    }

    public virtual double Area()
    {
        return MajorAxis * MinorAxis * Math.PI;
    }
}

We know from high school geometry that a circle is just a special case for an ellipse, so we create a Circle class that inherits from Ellipse, but SetMajorAxis sets both axes (because in a circle, the major and minor axes must always be the same, which is just the radius):

public class Circle : Ellipse  
{
    public override void SetMajorAxis(double majorAxis)
    {
        base.SetMajorAxis(majorAxis);
        this.MinorAxis = majorAxis; //In a cirle, each axis is identical
    }
}

See the problem now? If we set both axes, attempting to calculate the area gives the wrong result.

Circle circle = new Circle();  
circle.SetMajorAxis(5);  
circle.SetMinorAxis(4);  
var area = circle.Area(); //5*4 = 20, but we expected 5*5 = 25  

This is a violation of the Liskov Substitution Principle. However, the best way to refactor this code is not obvious, as there are quite a few possibilities. One solution might be to have Circle implement SetMinorAxis as well:

public class Circle : Ellipse  
{
    public override void SetMajorAxis(double majorAxis)
    {
        base.SetMajorAxis(majorAxis);
        this.MinorAxis = majorAxis; //In a cirle, each axis is identical
    }

    public override void SetMinorAxis(double minorAxis)
    {
        base.SetMinorAxis(minorAxis);
        this.MajorAxis = minorAxis;
    }

    public override double Area()
    {
        return base.Area();
    }
}

Another solution, one with less code overall, might be to treat Circle as an entirely separate class:

public class Circle  
{
    public double Radius { get; set; }
    public void SetRadius(double radius)
    {
        this.Radius = radius;
    }

    public double Area()
    {
        return this.Radius * this.Radius * Math.PI;
    }
}

Both solutions have their own drawbacks. The first can be considered a hack, since we have two different methods on the Circle class that essentially do the same thing. The second could be considered improper modeling, as we are treating Circle like a separate class even though it really is a special case of Ellipse. Given the choice, though, I personally am more likely to choose the second solution rather than the first one, as I feel it provides a better overall model (and doesn't use any redundant code).

Is It Worth It?

Yes, with reservations. As with the other SOLID principles, we cannot always expect the real world to allow us to model it perfectly. However, LSP is very useful in maintaining functionality over hierarchies, and in that purpose, it is suited particularly well.

By the way, if you're confused on how Liskov Substitution and Open/Closed are different, read this wonderful Programmers Stack Exchange answer.

What do you think, readers? Which of the potential solutions to the circle-ellipse problem above do you like better? Or do you know of any alternate solutions? Share in the comments!

SOLID in C#: The Open/Closed Principle

What Is This Principle?

The Open/Closed Principle, as originally formulated by Bertrand Meyer, states that a given software entity should be open for extension, but closed for modification. Essentially, any given class (or module, or function, etc) should allow for its functionality to be extended, but not allow for modification to its own source code.

Open/Closed Principle - Open chest surgery is not needed when putting on a coat

Benefits

This principle aims to reduce the introduction of bugs and other errors into your code by requiring classes to not change their own implementation unless absolutely necessary, as other derived or implemented classes may be relying on that implementation to function properly.

It also wants us to implement classes that can easily have their functionality extended, and by allowing the class to be open for extension, we allow for many real-world changes to occur without completely disrupting our design.

A Simple Example

Let's imagine a scenario in which we are given several Rectangles and need to calculate the total combined area of all of them. We then come along and create a solution that looks something like this:

public class Rectangle  
{
    public double Width { get; set; }
    public double Height { get; set; }
}

public class CombinedAreaCalculator  
{
    public double Area(object[] shapes)
    {
        double area = 0;
        foreach (var shape in shapes)
        {
            if (shape is Rectangle)
            {
                Rectangle rectangle = (Rectangle)shape;
                area += rectangle.Width * rectangle.Height;
            }
        }
        return area;
    }
}

This code does exactly what we want it to do, and it works great for rectangles. But, what happens if some of our shapes are circles?

public class Circle  
{
    public double Radius { get; set; }
}

We have to change the CombinedAreaCalculator to accommodate this:

public class CombinedAreaCalculator  
{
    public double Area(object[] shapes)
    {
        double area = 0;
        foreach (var shape in shapes)
        {
            if (shape is Rectangle)
            {
                Rectangle rectangle = (Rectangle)shape;
                area += rectangle.Width * rectangle.Height;
            }
            if (shape is Circle)
            {
                Circle circle = (Circle)shape;
                area += (circle.Radius * circle.Radius) * Math.PI;
            }
        }

        return area;
    }
}

By doing this we have violated the Open/Closed Principle; in order to extend the functionality of the CombinedAreaCalculator class, we had to modify the class's source. What happens when some of our shapes are triangles, or octogons, or trapezoids? In each case, we have to add a new if clause to the CombinedAreaCalculator.

In essence, CombinedAreaCalculator is not closed for modification, and isn't really open for extension (what good would inheriting from CombinedAreaCalculator do for another class?).

So, we need to refactor. There are many ways to refactor this to uphold Open/Closed; I'm going to show you just one of them. Let's create an abstract class that all the shapes can inherit from:

public abstract class Shape  
{
    public abstract double Area();
}

Notice that our abstract Shape class has a method for Area. We're moving the dependency for calculating the area from one centralized class (CombinedAreaCalculator) to the individual shapes. The CombinedAreaCalculator will just call each individual Shape class's Area method. The individual shape classes can now be implemented like this:

public class Rectangle : Shape  
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double Area()
    {
        return Width * Height;
    }
}

public class Circle : Shape  
{
    public double Radius { get; set; }
    public override double Area()
    {
        return Radius * Radius * Math.PI;
    }
}

public class Triangle : Shape  
{
    public double Height { get; set; }
    public double Width { get; set; }
    public override double Area()
    {
        return Height * Width * 0.5;
    }
}

And finally, we can create the new CombinedAreaCalculator class:

public class CombinedAreaCalculator  
{
    public double Area(Shape[] shapes)
    {
        double area = 0;
        foreach (var shape in shapes)
        {
            area += shape.Area();
        }
        return area;
    }
}

We've made the Shape abstract class and the CombinedAreaCalculator class open for extension and closed for modification, thereby upholding the Open/Closed Principle. If we need to add any other shapes, we just create a class for them that inherits from Shape and we're good to go.

Potential Hazards

You shouldn't interpret this rule as "don't change already implemented classes, ever." Of course scenarios will arise that will force or require you to change classes that are already implemented. However, we should use discretion when attempting to make these modifications, and keeping OCP in mind allows us to do that in a more efficient manner.

We only need to keep in mind that modules, ideally, should be open for extension and closed for modification. But if we have to change the code to support new rules and requirements, and the best way to support those requirements is to change existing class functionality, we shouldn't be afraid to just do it.

Is It Worth It?

Absolutely, though I don't feel as strongly about it as I do the Single-Responsibility Principle. Still, it's an excellent guide to making any extensions to functionality simpler to implement, and minimizing the work that we programmers have to do to make changes whenever they arise.