SOLID Made Easy – Single Responsibility Principle Posted on October 1, 2015 by Benjamin Medina III The SOLID principles are a foundation of good OOP practices. In order to develop high-quality software applications, it is essential for every developer to understand and to practice them. SOLID stands for: S – Single Responsibility Principle O – Open/Closed Principle L – Liskov’s Substitution I – Interface Segregation D – Dependency Inversion Single Responsibility Principle Any object should only have a single responsibility and should only have one reason to change. I read somewhere that some people applied this by having one function per class which is, of course, incorrect. A class can have multiple functions as long as the functions are all in relation to the responsibility of the class. The responsibility of the class would depend on the context of the object. The context refers to the depth and the scope of the object. For example in a restaurant POS, we have a Manager model and the manager can perform transactions but cannot void transactions. In that example, the only time that we will change the Manager model is when the owner decides to say that the manager can void transactions. A clear violation of the single responsibility principle is a god object. A god object is an object that does and knows too much. Here is an example of a god object: //// a violation of the single responsibility principle public class RestaurantPOS { protected UserType CurrentUserType { get; set; } public RestaurantPOS(UserType userType) { this.CurrentUserType = userType; } public void Process(string actionRequested) { if (actionRequested.Equals("create transaction")) { //// the code for creating the transaction goes here } else if (actionRequested.Equals("void transaction")) { if (this.CurrentUserType == UserType.Manager) { //// the code for the void transaction goes here } } else if (actionRequested.Equals("add menu item")) { if (this.CurrentUserType == UserType.Owner) { //// the code for adding a menu item goes here } } } } public enum UserType { Owner, Manager, Waiter } 12345678910111213141516171819202122232425262728293031323334353637383940 //// a violation of the single responsibility principle public class RestaurantPOS{ protected UserType CurrentUserType { get; set; } public RestaurantPOS(UserType userType) { this.CurrentUserType = userType; } public void Process(string actionRequested) { if (actionRequested.Equals("create transaction")) { //// the code for creating the transaction goes here } else if (actionRequested.Equals("void transaction")) { if (this.CurrentUserType == UserType.Manager) { //// the code for the void transaction goes here } } else if (actionRequested.Equals("add menu item")) { if (this.CurrentUserType == UserType.Owner) { //// the code for adding a menu item goes here } } }} public enum UserType{ Owner, Manager, Waiter} This is an example of a god object because the RestaurantPOS class will be responsible for everything that is related to it. In the example, performing a transaction and managing the menu are shown but it would also include other modules like managing the employee, generating sales reports, etc. The class will get really complex and bloated. Once the system is finished this class will surely have thousands of lines of code. Maintaining and debugging this class will be extremely hard. Imagine if something goes wrong in the processing of the transaction, you need to dig through this huge class in order to find and fix what’s wrong. Technically, in context, the RestaurantPOS is responsible for all the methods it has. What went wrong here is not on how it was defined but on how it was designed and implemented. //// a sample that complies with the single responsibility principle //// the interface for all the methods of a transaction public interface ITransaction { void CreateTransaction(); void VoidTransaction(); } //// the interface for all the methods to manage the menu public interface IMenuManagement { void AddItem(); } public class Transaction : ITransaction { public void CreateTransaction() { //// the code for creating the transaction goes here } public void VoidTransaction() { //// the code for the void transaction goes here } } public class Menu : IMenuManagement { public void AddItem() { //// the code for adding a menu item goes here } } 1234567891011121314151617181920212223242526272829303132333435 //// a sample that complies with the single responsibility principle //// the interface for all the methods of a transactionpublic interface ITransaction{ void CreateTransaction(); void VoidTransaction();} //// the interface for all the methods to manage the menupublic interface IMenuManagement{ void AddItem();} public class Transaction : ITransaction{ public void CreateTransaction() { //// the code for creating the transaction goes here } public void VoidTransaction() { //// the code for the void transaction goes here }} public class Menu : IMenuManagement{ public void AddItem() { //// the code for adding a menu item goes here }} By creating separate interfaces for the transaction and the menu management and implementing them in separate classes will make the code more maintainable and easier to debug. If something goes wrong in the Menu class, we can easily navigate to it without seeing the code for the transactions that we don’t care about at the moment. Also, we will be more confident that when we need to change something in the menu that it will only affect that object. If the owner says that we should be able to update an item on the menu then we need to change the interface and let the class implement it. //// the interface for all the methods to manage the menu public interface IMenuManagement { void AddItem(); void UpdateItem(); } public class Menu : IMenuManagement { public void AddItem() { //// the code for adding a menu item goes here } public void UpdateItem() { //// the code for adding a menu item goes here } } 12345678910111213141516171819 //// the interface for all the methods to manage the menupublic interface IMenuManagement{ void AddItem(); void UpdateItem();} public class Menu : IMenuManagement{ public void AddItem() { //// the code for adding a menu item goes here } public void UpdateItem() { //// the code for adding a menu item goes here }} The single responsibility principle must be followed every time as it will immediately make our code better. It will make it more maintainable, extensible and testable. Before adding a property to a model, a method to a class, a set of new business logic to a method, ask yourself, is the object responsible for this? If you have an opinion, feedback, or thoughts, do leave a comment. I’d love to read them. Pingback: You_Source SOLID Made Easy - Open-Closed Principle()