What are SOLID Principals for OOP?

  • Posted on May 19, 2022
  • Estimated reading time 5 minutes
Solid principals for OOP

I feel like this is a topic that I don't see many people talking about so much anymore, so this is my explanation of it.

SOLID is an acronym for 5 core principals. These are a set of principals designed to make your code more maintainable and robust when, coding in an object orientated language. They stand for:

  • Single Responsibility
  • Open/Close
  • Liskov Substitution
  • Interface Segregation
  • Dependency Inversion (injection)

Brace yourself! There is a lot to go through...

Single responsibility
Essentially your method/class should have one thing to do and do it well. For example, this is bad coding:

Bad Coding Example

It's bad because, it doesn't make much sense to be writing an arbitrary piece of text to the console inside of a method called "Count". It's unexpected and unnecessary behavior. This on the other hand, is good. It's good because, each method now has a specific job.

Bad Coding Example

Having an object only do one thing makes the code much easier to read and understand. Also, if it needs to be extended this can be done much more quickly.

Simply put, software entities (methods, classes, etc) should be open to extension and closed to modification. Hmmmm... time for another example, I think! In the code below we are trying to give the method too much functionality. In doing so we are modifying the expected behaviour of the method. As you can see to modify something like this could become very troublesome.

Bad Coding Example

Now consider this. We split up the class, add an interface and have each object specify how to handle the processing of an order. Now, we have something that is far more open to extension.

Bad Coding Example

As you can see by adding a layer of inheritance, we have not changed the underlying behaviour of our interface and yet we have opened it up for extension. Now to follow the above example you'd need to add an if statement in the calling class for both object and switch between them. But you get the point, by abstracting the functionality out we have made the code much more flexible, including derived classes being able to implement the function in the interface in whichever way that they want to.

Liskov substitution
Basically, what this principal means is that a software entity should be replaceable with a sub-type without creating any new errors. Sub-type in this context means something that inherits from something else. Such as Order to IOrder. I feel another example coming up:

Bad Coding Example

In the above sample we are violating the rule because, we should be able to use the BigOrder object in the same way as the Order object and well... right now we can't. Both objects are concrete implementations, of separate objects i.e., they don't have a common sub-type. This can be problematic in the real world, especially when testing components of your software. A better way of doing the exact same thing is either, create a new method to take in the BigOrder object (this is bad don't do it) or require an interface instead.

Bad Coding Example

In the above sample you can clearly see that the two implementations of IOrder (both BigOrder and Order inherit form IOrder) can be substituted with one another without effecting the functionality of the program.

Interface segregation
What this means is don't tack on new methods to an interface if they are unnecessary. Example, this is bad:

Bad Coding Example

Now clearly a car shouldn't have any need for a fly function at least not in 2021 maybe 2030 though... Likewise a plane shouldn't be able to drive. As such we have introduced redundant functionality to both objects. Which violates this principal. We can fix this by introducing new interfaces to better manage the new functionality.

Bad Coding Example

Simple when you see it in action.

Dependency inversion (injection):
Finally, the great DI (Dependency Inversion (Injection)). This can be a massive topic in itself. And there are many ways of achieving this. The way I like to think of it is don't use the "new" keyword. except or at the composition route of your application. This principal states that:

  1. High/ low level modules should depend on abstractions
  2. Abstractions should not depend on details

Essentially don't depend on concrete implementations in your code. A concrete implementation is an instance or an object that was created using the 'new' keyword. If you have read down as far as here. First of well done, secondly you would have seen DI being used in some of the samples. From the sample below you can see the rule being violated because, in BuildHouse method we are using the new keyword to create an instance of the BrickLayer object. This added dependency reduces the testability of our method.

Bad Coding Example

Now first off what we should do is to inject the BrickLayer into the BuildHouse method. However we can go one step further by abstracting the BrickLayer.

Bad Coding Example

Great we did it! Now for the proof.

Bad Coding Example

Super. Now, if we needed to put in a dummy IConstructionWorker object into the method to test it, we could and without any issues.

I hope my explanation helps with your understanding of SOLID principals. See you next time.

At Avanade, we’ve gathered thousands of hours of training around technical and professional skill and we welcome people to go deep into specialties and to embrace agility and pursue new skills in different areas of the business.

Interested? Find out more about a career at Avanade.

Techs and Specs Newsletter

Stay up to date with our latest news.

Contact Avanade

Next steps

Talk to us about how we can bring the power of digital innovation to your business.

Share this page