C# Best Practices - SOLID Principals

C# Best Practices - SOLID Principals

Basic principals of object oriented programming

These principals helps you in object oriented programming and designing classes. It can save you from a lot of hassles. SOLID represents 5 basic principles one for each letter of S O L I D.

These principals are defined to enhance dependability and maintainability. They are defined for decoupling things, simplifying things and lowering the overall complexity of application.

S = Single Responsibility principal

O = Open / Close principal = Inheritance

L = Liskov Substitution

I = Interface Segregation

D = Dependency Inversion – Coupling /  Decoupling

Single Responsibility

It is the most basic principal,

It simply states that a Class should have a single responsibility. That single responsibility should be entirely encapsulated by the class. All of the service of the class should be narrowly aligned with that responsibility. What it means that we create a class and that class should do only one thing, and that one thing should be completely done by that class, and we don’t want any other thing that is being done by that class.

This gets hard when we develop small features those get add to the class, but that does not refer to the original responsibility of the class.

For example we have a Customer Class which is an entity class, and that class interacts with database, for creating, reading, updating and deleting all basic CRUD operations. That’s what this class should do, it should present that interface and ONLY that interface i.e. CRUD operations to user.

What it should not do

It should not manage Customer Orders, this is not responsibility of customer class.

It should not provide helpers methods to output, just like providing HTML that will be displayed on  a web page.

It should not LOG exceptions, well, it should be doing exception handling but it should not be logging exceptions, for logging purpose we should have another class i.e. Logger. 

Open / Close principal 

This principal say’s that software entities (classes, modules, functions) should be open for extension but close for modification, which means once a class or module has been written, it has been closed for modifications, if we want to change its functionality we should extend our software entity and provide additive or changed functionality in extended versions. Rather than we change the functionality with in that class. In C# we can achieve this by using inheritance i.e. once a class is created we modify its functionality only by inheritance rather than modifying its original functionality.

Having said that, we should be thinking when we are creating a class that what should be public, what should be protected and what should be private. If we want to change our original class that change should only be done only if, it is really related to that class responsibility. But for new or modified features we should always be inheriting or extending a class.

So in our classic example of User, there can be multiple users, like AdminUser, MemberUser and GuestUser. So normally when we make a User class, we can give Type property in that class so that each type represents a different user. And then following in the code we basically giving If Else and Switch statement. And later if we want to change something then we will be changing code in the User class. Which is violation of Single Responsibility principal.

In order to avoid this situation the best way that supports Open / Close principal, is to make a base class User and then all types of users will be inheriting classes, like, Admin inheriting from User, Member Inheriting from User and Guest inheriting from User.

So according to this principal Never use one single class as User, always create classes and inherit them from User class. So we can enhance this concept in different examples. Like where ever we need types of different entities we can simply inherit them into more classes. 

liskov substitution principal (LSP) 

This principal is about strong sub typing. This is built IN language feature in C#. We don’t think a lot about this principal but we should be familiar with this principal.

This principal say’s that a child object of any class will be substituted by parent class. Any class or method in parameter if using an object of parent class, can also use an object of child class. And nothing bad will going to happen. The substitution will not affect the overall behavior of the program in terms of correctness and task performed.

So in our classic example MemberUser, GuestUser, AdminUser are sub classes inherited from User base class. So all the sub classes must implement interface of base class User. And we do not want NOT Implemented exception in the sub classes methods. So if any client application used Sub Type of User, and it expect that any method will successfully runs, without throwing an exception. And if method of sub class throws any NOT IMPLEMENTED exception, that’s not what we want. That’s exactly the idea behind Liskov Substitution Principal. 

Interface segregation principal (ISP) 

This principal is basically simplifying principal, this principal states that no client should be forced to depend on methods it does not use. Which results in splitting large interfaces into smaller ones. Those small interfaces are more focused on specific functionality in the client. So client will need to know those methods, in which clients are interested in.

In .NET you may have noticed that many classes have implemented multiple interfaces, and this is an example of segregation principal. Many small functionalities can be combine into one, but a lot of other classes may not use all of them. That is why we split large one interface into many small interfaces.

Splitting large interfaces into smaller, makes the code easy to refactor, change and redeploy.

So In our classic user example, we have subtypes AdminUser, GuestUser, MemberUser inherit from User base type. So According to this principal, Method those are specific for AdminUser should not be included in User Class or interface. Because member classes for Guest and Member do not need them.

So we split User interface into three small interfaces, like IUser, IAdminUser and IMemberUser. So in IUser interface we will have basic methods those are related to User, and in IAdminUser and ImemberUser we will going to have methods those are specific to Admin and Member Interfaces. And then in our User Class we will implement all three interfaces. 

 Dependency inversion principle 

This principle is simply about Inversion of Control, it states that, High level modules those are parent classes, should not depend on low level classes. In other words High level modules does not know about low level modules and low level module does not know about high level modules. But what they do know about is “Abstraction”, So through abstraction they are communicating with each other.          

So if we take our example analogy, our User class want to log some exceptions not directly but by using a logger class. User class will simply create the instance of logger class and logger class starts logging exceptions. But here is a tricky part, Logger class can log exception on multiple media, like file system, database or windows event handler. So what Dependency Inversion principal is saying that User class does not know anything about Media. Where the exception is being logged. Whether on File system, or database system or Event Handler. What user class will know that it needs to call some method of Logger class for handling exception, writing exception data, and that’s it. Everything about media where to log exception data is totally out of scope of User class according of our Single responsibility principle. 

Conclusion 

So these are our SOLID principles, if you think right from the beginning of creating an application it will really help to avoid really nasty errors to be occurred. It also help your application to grow particularly when you are thinking about adding features. When you are adding more feature try to think not to violate these SOLID principles, because it’s very easy to violate them, it looks shorter path but in the long run when you will add more and more feature and classes become inter related many nasty bugs can come across.

Hi Eman! Thanks for share this principal with us.

To view or add a comment, sign in

More articles by Eman Ali Mughal

Others also viewed

Explore content categories