Like every part of life, principles are laid out as rules that must be followed in order to get the best result possible. C# is no different. There are guiding rules that make writing the language super easy, understandable, and executable. Failure to abide by these rules, more often than not, creates a number of avoidable situations in your code. Some of the issues include: developing bugs, lack of maintainability of the codebase, full knowledge of the system before working on the application, and a host of other problems. All of these can be traced to design flaws that stem from the non-application of these principles.
These principles are all known together as the SOLID principles. This is an acronym that stands for Single Responsibility Principle (SRP). Open-closed principle (OCP), Liskov substitution principle (LSP), interface segregation principle (ISP), and dependency inversion principle (DIP) SOLID principles are the design principles that can help us contain different software design issues. The person who brought about these principles was Robert C. Martin in the early 1990s. The importance of these guidelines is to provide ways to migrate from tightly coupled code to a better-working, loosely coupled, and encapsulated one that meets business requirements. Now, I know this is a long list, but it will get simpler as we look at them one after the other. Let's dive right in.
The single responsibility principle states that every class should be responsible for only one task. This simply means that everything a class does should serve only one purpose. This does not say you can't have other methods or properties in a class; however, everything the class does must be towards a single responsibility, such that altering A part of it does not require altering the entire class. Having the single responsibility rule obeyed when writing your codes gives you a good way of identifying the classes and what they do at the design stage of the application.
`public class UsersRepo { public void Registeration(string emailAddress, string password) { if (!CheckEmail(email)) throw new ValidationException("This is not an email"); var user = new User(emailAddress, password);
SendEmail(new MailMessage("prestine@gmail.com", emailAddress) { Subject="You are registered" });
} public virtual bool CheckEmail(string email) { return email.Contains("@"); } public bool SendEmail(Message message) { _smtpClient.Send(message); } }`
Even though this looks fine, it does not follow the single responsiblity rule. The send email and Check email methods haev nothing to do with the UserRepo and hence let's see a better way to present this:
`public class UserRepo { EmailService _emailService; UsersDbContext _dbContext; public UserRepo(EmailService emailService, UsersDbContext dbContext) { _emailService = emailService; _dbContext = dbContext; } public void Registeration(string emailAddress, string password) { if (!_emailService.CheckEmail(email)) throw new ValidationException("This is not an email"); var user = new User(emailAddress, password); _dbContext.Save(user); emailService.SendEmail(new MailMessage("prestine@gmail.com", emailAddress) {Subject="Hello. Hope you are good!"});
}
} public class EmailService { SmtpClient _smtpClient; public EmailService(SmtpClient aSmtpClient) { _smtpClient = aSmtpClient; } public bool virtual CheckEmail(string email) { return email.Contains("@"); } public bool SendEmail(Message message) { _smtpClient.Send(message); } }`
This principle states that a software class or module is open for extension and closed for modification." The way this works is that you should design a class in such a way that it is only open for extension when there is a new requirement and The functionality for that can be added. This can be done using an object-oriented programming method called inheritance. The closed for modification part of that rule means that once a class is developed and has undergone unit tests, it should not be altered, except if bugs are found.
The LSP states that "you should be able to use any derived class instead of the parent class and have it behave in the same manner without modifying it". This principle ensures that a derived class must be substitutable for its parent class without hassle. This principle is an extension of the open and close principle. A typical real-life scenario to further explain this rule will be that a son can represent the father, who is the owner of the company in a meeting as CEO and not as a clerk, which the father is not.
The interface segregation principle states that "clients should not be forced to implement interfaces they do not use. Instead of having one big interface, Several smaller interfaces can be built based on groups, each serving a submodule". Here, it is important that the methods on an interface are defined by the methods needed by a client (class) rather than those it merely implements. Therefore, classes should not be forced to depend on interfaces they don't use. Like a class, each interface should have only one specific responsibility. A class need not implement methods it does not need, hence reducing Interfaces with specificity are key. The larger the interface, the more likely it is to take in methods it does not need.
This principle states that "high-level modules or classes should not depend on low-level modules, but both should depend upon abstractions. Furthermore, Abstractions should not depend upon details, and details should not depend upon abstractions. The class the user interfaces with directly is the highest level class, while the class it needs to work with is the lowest level class.
To read further about SOLID principles and get codesnippet examples, please click on this link: SOLID Principles