SOLID Principles for C# Developers: Full Guide

by admin

The SOLID principles are a set of guidelines for creating highly testable software.

Apply the SOLID principles to your next project to write simpler and easier code to manage.

Let’s take a look at what each principle means and how you can benefit from implementing them into your next project.

Single Responsibility Principle (SRP)

The SOP is defined as each software “module” needing one and only one reason to change.

A module refers to a class, method, or a similar structure in your C# code.

This principle refers to a module as a multipurpose tool often coupling together things that shouldn’t be related, making the module harder to use and more complex to alter.

This multipurposeness is closely related to “coupling” and “cohesion”. The ideal practice is to implement “high cohesion” and “low coupling” while keeping modules focused and concise.

Coupling

Modules can be referred to as either being “tightly coupled” or “loosely coupled”. When things implement tight coupling, it means that modules are dependent on each other.

So if there is a class that is a multipurpose tool, the single purpose details may become challenging to work with and more vulnerable to problems with modification or expansion to the module.

Cohesion

Cohesion outlines how closely related elements within a module are to each other. A module with a lot of elements doing different things becomes hard to maintain and is explained as being “low cohesion”.

Open-Closed Principle (OCP)

The OCP is defined as software modules being open for extension but closed for modification.

Open to extension refers to modules that new logic can be added to in the future.

Closed for modification is when the only way to update/change the behaviour of the module is to change the source code.

This is beneficial because it means that working code that’s been tested cannot be modified therefore minimising bugs and issues with dependent code.

The three main approaches to implementing OCP are:

  1. Parameters
  2. Inheritance
  3. Injection (see DIP section)

Liskov Substitution Principle (LSP)

The LSP is the third SOLID principle. It refers to good practices with regard to inheritance.

The LSP states that inheritance in object-oriented coding whereby class A is a kind of class B is insufficient and should instead be replaced with class A “is substitutable for” class B.

An example to demonstrate the problem with class A being a kind of class B and non “a substitute for” is with a square and rectangle. A square satisfies the conditions of a rectangle and therefore the square (subclass) could be inherited from and could be defined as being a kind of rectangle (superclass).

In order for this to work, the square class must override any area calculation logic so that all sides are always equal. This introduces a problem where a subclass should not enforce stricter rules than its superclass. This is because if a rectangle’s width and height are set, but a square is instantiated, the output value will not be as expected.

Interface Segregation Principle (ISP)

ISP defines how interfaces should be used with the SOLID principles in mind. Interface refers to anything that is accessible by the “client” from an instance.

It refers to a client not being forced to depend on methods they do not need or use. Where client refers to the code that is interacting with the instance of the interface.

An example of when ISP is violated could be when a service inherits another interface with many methods, but when the service only has a need for one of the multiple methods.

Assuming you have control of the code and a read-only SDK or framework isn’t used, ISP violations can be mitigated by dividing the methods of the inherited interface into separate interfaces. And with C#’s support for multiple interface inheritances, it shouldn’t be a problem to implement this on legacy code whereby there are existing dependencies.

For read-only interfaces that you do not have access to modify, consider using the adapter design pattern.

Dependency Inversion Principle (DIP)

DIP is defined as high-level modules not depending on low-level modules but instead depending on abstractions. It also expands on this and states that abstractions should not depend on details and vice versa. These principles in practice implement a separation of concerns.

Abstractions are generally interfaces or abstract base classes. Abstractions describe what something will do. For example, this code will calculate the journey time, whereas details will explain how it will do this. For example, this code will use Google Maps distance matrix API to calculate the journey time in minutes from point A to point B.

The difference between high-level and low-level modules is related to the n-tier application concept. With high-level modules being more business-critical and abstract and low-level modules being more input-output related. For example, in a program that collects and then runs calculations for a business report, the methods to collect and calculate the information would be low-level, but the code that formulates the report based on this information would be high-level.

Conclusion

It is often advised that pain-driven development should be implemented when using SOLID principles. This refers to creating concrete code and only using principles outlined in this article when issues/pain arises.

Otherwise, the code could be very hard to maintain and read, with, for example, all interfaces being unnecessarily split up.

As discussed, the SOLID principles provide a highly effective structure for creating high-quality code. Understanding and applying them will help you ensure that your programs are stable, testable, easy to maintain, and easy to improve.

Related Posts

Leave a Comment