S.O.L.I.D. principles seem to be a hot topic. The guys over at LosTechies.com explored it last year in March as their topic of the month and did a great job of it. I know when I read blogs I enjoy when different authors post on similar topics. I find that often the different examples and explanations paint a clearer picture than can be had with only one example. I'd like to share an area where we use the Single Responsibility Principle (SRP) in our application
Note: The "aha" moment won't be some thrilling display of how we averted some disastrous bug or how we eliminated some thousands of lines of code. It will be much more subtle but that's ok. A good design should strive for an element of simplicity. Great design is asking to hear the answer to a riddle and then thinking, "duh! of course" When you hear the answer you realize how "obvious" it was.
In our application we queue all emails to be handled by a different process since sending email is a blocking call which slows your response times (Note to BCL junkies: yes I know there is a SendAsync method). There are a few instances in our application where we want email to be sent right away and not queued. An example of this would be a confirmation email for a new account or a password reset request.
We already had a QueuedEmailService class so adhering to SRP, I built a RealTimeEmailService class whose job, and only job, it is to send the email right away. Thinking of responsibility driven design, classes:
With that in mind I needed another class to make the decision to when to send the email through the RealTimeEmailService or when to use the QueuedEmailService:
(At this point some of you might be reading thinking how easy of a problem this would be to solve and you're quite right. What I want to point out is not the problem, but how one solves the problem. Nearly anyone could've gone into the QueuedEmailService and added an if statement and sent emails real-time if that is what is needed, thus ignoring SRP. By lumping all of the work together you aren't building object oriented software...sorry. And while you might be okay with that and you're software WILL work, you run the risk of more maintenance issues and modularity problems).
Here's what a quick summary of what I've got:
1: public QueuedEmailService : IEmailService
2: {
3: public void Send(...)
4: {
5: // add to some queue
6: }
7: }
8:
9: public RealTimeEmailService : IEmailService
10: {
11: public void Send(...)
12: {
13: // send right now
14: }
15: }
16:
17: public PriorityBasedEmailService : IEmailService
18: {
19: public void Send(...)
20: {
21: if (priority == MailPriority.High)
22: // send using realtime service
23: else
24: // send using queued service
25: }
26: }
27:
28:
I know many people who would look at the code above and think it is possibly overkill. I would disagree for a few reasons:
There are cases when a design can go overboard, but for the vast majority of cases this simply isn't the case. I'd rather see a solution where someone has gone too far than what I typically see in classes that do it all, with no clear responsibility.
I hope this helps augment your knowledge of SRP. If you're still a little fuzzy on SRP and what it is, let me know, I'd love to discuss it with you.
This blog contains the thoughts and discoveries of Tim Barcz, a technologist with a interests in computer programming technologies.