Using the single responsibility Principle (SRP) is one of the best changes I have ever personally done to the way I develop software.
To boil things down, SRP means that a class should have one, and only one reason to change. The way to accomplish this is to write a class that only does one thing. Now most developers I have worked with may have something like this in their code:
public class CustomerUtility
{
public void Save()
public void SaveOrder(Order order)
public Customer GetCustomerById(int id)
public Customer GetCustomerByOId(int id)
public IList<Customer> GetAllCustomers()
public IList<Customer> GetAllCustomersA()
private void UpdateCustomer(Customer customer);
private void GetCustomerByCriteria(string criteria);
private Customer GetCustomerByCriteria(string criteria);
private IList<Customer> GetCustomersByCriteria(string criteria);
private void SaveRecord(object itemToSave);
}
I am showing this without the methods implemented as it takes up too much space as this class is 150 lines long. Looking at this we have 13 methods that do different things and so therefore probably 13 reasons to change things in this class.
There are a few problems with this "class" right now:
- If we want to extend this class we will have to know about any intricacies of the class and how these methods relate to each other. So what we can override and what we can expect the behaviours to be
- Using this class is harder if the methods are not well named (as you can see from the above example it is hard to tell the difference is between GetAllCustomers() and get GetAllCustomersA() is)
- Methods that are unrelated to each other are in the same place (ok it works but it feels wrong)
- It takes longer to seek through the code. I know most people think it is only a few seconds to scroll through the code to find the method I want but those few seconds add up.
- You know have more people wanting to work on the same class at the same time. This can lead to contention over source control locks or having to do lots of merging.
By moving to SRP the code does not perform better (nor does it perform worse). It does become easier to work with, easier to navigate, and easier to extend. My first wave of refactorings would look something like this:
public class SaveOrder
{
public void Save(Order order)
}
public class SaveCustomerToDatabase
{
public void Save(Customer customer)
}
public class GetCustomer
{
public Customer GetCustomerById(int id)
public Customer GetCustomerByOrderId(int id)
private void GetCustomerByCriteria(string criteria)
}
public class GetAllCustomers
{
private IList<Customer> GetAllCustomers()
private IList<Customer> GetAllActiveCustomers()
private IList<Customer> GetCustomersByCriteria(string criteria)
}
As you can see we have a lot more of our code nicely isolated into its own class that does one or two things.
Using SRP does not mean that we need to only have one method per class though. In the GetCustomer and GetAllCustomers their are two public methods on each class. Both of these methods are very similar and relate to the class they are a part of. This is the golden rule with SRP: Do the methods in a class relate to the name of that class? If so then they probably belong there.
Now the reason people put all the code into one class is usually to navigate with intellisense (or a false belief that fewer classes make a big performance difference). To those people I say "THAT IS WHAT NAMESPACES ARE FOR!"
By wrapping a few namespaces around our classes:
namespace Orders
{
public class SaveOrder
{
public void Save(Order order)
}
}
namespace Customers
{
public class SaveCustomerToDatabase
{
public void Save(Customer customer)
}
}
namespace Customers.Retrieval
{
public class GetCustomer
{
public Customer GetCustomerById(int id)
public Customer GetCustomerByOrderId(int id)
private void GetCustomerByCriteria(string criteria)
}
public class GetAllCustomers
{
private IList<Customer> GetAllCustomers()
private IList<Customer> GetAllActivceCustomers()
private IList<Customer> GetCustomersByCriteria(string criteria)
}
}
We can now easily navigate to what we want quite logically and in a quick fashion.
It is also fast and easy to navigate in your solutions explorer as well:
If you think this is something you can benefit from, try it. If you don't think you can
benefit from it, try it twice. It is seriously something that I can not develop without.