According to Wikipedia the single responsibility principle states that every class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility. The single responsibility principle is one of the SOLID design principles as defined by Robert Martin in his book Agile Software Development: Principles, Patterns, and Practices.
An important thing to note is that Martin defines a responsibility as “a reason to change” so when I think of the single responsibility principle I paraphrase it as a class should have only one reason to change. When I am designing a system and it is time to start creating classes I always ask myself what things change together and what things change independently of one another. When I find things that change together they can and often should be put together in the same class when they vary independently, I make sure and separate those things by using different classes.
Let’s revisit my Interface Segregation Principle post. In that post we were discussing sending a message via an SMS and an email system and how we could use interfaces to make sending messages more robust. Consider the problem of creating and sending messages from a different angle, consider how sending messages has changed over time. People have been sending messages to one another since the beginning of humanity. A message is, after all, just a communication “transmitted” from a sender to a receiver. Sometimes messages can have metadata attached to them such as a priority, a title, a signature, or a timestamp. The notion of what constitutes a message has evolved with technology but not by a lot. Now consider how the way we transmit messages has changed over time. People have transmitted messages using smoke signals, telegraph wires, horses, the postal service, email, text messages, and many more. Clearly the notion of a message and the notion of transmitting a message have changed independently of one another over time and we can predict that this trend may continue in the future. This prediction would suggest that we should be separating the notion of a message into one class and the delivery of the message into another. By Isolating these two things into their own class we can isolate code that constructs or consumes a message from changes to the delivery mechanism. We also then isolate code that is concerned only with message delivery from changes to a message. This is a big testing win. By putting these responsibilities into different classes we have reduced the number of things that must be tested when one or the other change which to me is the whole point of the single responsibility principle.
From my last post, this is the set of interfaces and classes we ended with:
public interface IMessage { IList<String> ToAddresses { get; set; } string MessageBody { get; set; } bool Send(); } public interface IEmailMessage:IMessage { string Subject { get; set; } IList<String> BccAddresses { get; set; } } public class SmtpMessage : IEmailMessage { public IList<String> ToAddresses { get; set; } public IList<String> BccAddresses { get; set; } public string MessageBody { get; set; } public string Subject { get; set; } public bool Send() { //Do the real work here return true; } } public class SmsMessage : IMessage { public IList<String> ToAddresses { get; set; } public string MessageBody { get; set; } public bool Send() { //Do the real work here return true; } }
Now if we Apply the Single Responsibility Principle to our design we recognize that we should not be mixing the Send method with the IMessage interface. We might come up with something like this:
public interface IMessageServer { bool Send(IMessage message); } public interface IMessage { IList<String> ToAddresses { get; set; } string MessageBody { get; set; } } public interface IEmailMessage : IMessage { string Subject { get; set; } IList<String> BccAddresses { get; set; } } public class SmtpMessage : IEmailMessage { public IList<String> ToAddresses { get; set; } public IList<String> BccAddresses { get; set; } public string MessageBody { get; set; } public string Subject { get; set; } } public class SmsMessage : IMessage { public IList<String> ToAddresses { get; set; } public string MessageBody { get; set; } } public class SmsMessageServer:IMessageServer { public bool Send(IMessage message) { //Do the real work here return true; } } public class SmtpMessageServer : IMessageServer { public bool Send(IMessage message) { //Do the real work here return true; } }
With this design, classes implementing the IMessageServer interface only need to concern themselves with transporting the message and classes implementing the IMessage interface only need to concern themselves with building a message. So how do we tie these 2 things together? Look for my next post on the Dependency Inversion Principle to pull them together.
So these business objects (SmsMessage, SmtpMessage) basically become the data access object?
I appreciate this post–it’s an answer to my question. Just feel like this leads to TONS of classes like SmtpMessageServer with no state but they’re still instantiated all over the place.