Intent/Definition
Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
There should never be more than one reason for a class to change.
Rules of Thumb?
- If you cannot come up with a meaningful name for your class focused on single responsibility, then it's probably doing too much.
- Every object in our web application should have a single responsibility, and all object's services should be focused on carrying that single responsibility(SRP).
- If you put more than one functionality in one Class in Java it introduces coupling between two functionality and even if you change one functionality there is a chance you broke coupled functionality, which requires another round of testing to avoid any surprise on the production environment.
Single Responsibility Principle Example
Let's see bad code design then we will see how to improve it using this principle.
In this example, we have a sample User Registration example.
When user register to the System then System will send mail t the user for verification.
Bad code design
First, let's see "bad" design and implementation. In bad design shows the functionalities like save user to the database and sending mail to user email address for verification mixed in a single class.
Below is an example which violates the Single Responsibility Principle.
User.java
public class User {
private String firstName;
private String lastName;
private String email;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
IUserService.java
public interface IUserService {
public void registerUser(User user);
}
UserService.java
public class UserService implements IUserService {
public void registerUser(User user) {
// save user to database
// send mail to user for verfication
final String fromEmail = user.getEmail(); // requires valid gmail // id
final String password = "mypassword"; // correct password for gmail id
final String toEmail = "myemail@yahoo.com"; // can be any email id
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com"); // SMTP Host
props.put("mail.smtp.port", "587"); // TLS Port
props.put("mail.smtp.auth", "true"); // enable authentication
props.put("mail.smtp.starttls.enable", "true"); // enable STARTTLS
// create Authenticator object to pass in Session.getInstance argument
Authenticator auth = new Authenticator() {
// override the getPasswordAuthentication method
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(fromEmail, password);
}
};
Session session = Session.getInstance(props, auth);
sendEmail(session, toEmail, "TLSEmail Testing Subject", "TLSEmail Testing Body");
}
private void sendEmail(Session session, String toEmail, String subject, String body) {
try {
MimeMessage msg = new MimeMessage(session);
// set message headers
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply@gmail.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply@gmail.com", false));
msg.setSubject(subject, "UTF-8");
msg.setText(body, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
System.out.println("Message is ready");
Transport.send(msg);
System.out.println("EMail Sent Successfully!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note that in above code, the User services and Email services are combined in a single class and hence it violates the Single Responsibility Principle.
Good code design
Let's refactor the code to make "good" design using SRP?
According to this principle, we need to separate out email functionality and its responsibility should be entirely encapsulated by the class. Below is an example to demonstrate the Single Responsibility Principle.
Class diagram
Let's write source code according to a class diagram.
User.java
public class User {
private String firstName;
private String lastName;
private String email;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
IUserService.java
public interface IUserService {
public void registerUser(User user);
}
UserService.java
public class UserService implements IUserService {
private EmailInfo emailInfo;
private IEmailService emailService;
public void registerUser(User user) {
// save user to database
// send mail to user for verification.
emailInfo = new EmailInfo("some subject", "some body", user.getEmail());
emailService = new EmailService();
emailService.sendEmail(emailInfo);
}
}
EmailInfo.java
public class EmailInfo {
private String subject;
private String body;
private String email;
public EmailInfo(String subject, String body, String email) {
super();
this.subject = subject;
this.body = body;
this.email = email;
}
// add required fields for advanced mailing...
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
IEmailService.java
public interface IEmailService {
void sendEmail(EmailInfo emailInfo);
}
EmailService.java
public class EmailService implements IEmailService {
private final static String password = "mypassword"; // correct password for gmail id
private final static String fromEmail = "myemail@yahoo.com"; // can be any email id
public void sendEmail(EmailInfo emailInfo) {
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com"); // SMTP Host
props.put("mail.smtp.port", "587"); // TLS Port
props.put("mail.smtp.auth", "true"); // enable authentication
props.put("mail.smtp.starttls.enable", "true"); // enable STARTTLS
// create Authenticator object to pass in Session.getInstance argument
Authenticator auth = new Authenticator() {
// override the getPasswordAuthentication method
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(fromEmail, password);
}
};
Session session = Session.getInstance(props, auth);
sendEmail(session, emailInfo.getEmail(), emailInfo.getSubject(), emailInfo.getBody());
}
private void sendEmail(Session session, String toEmail, String subject, String body) {
try {
MimeMessage msg = new MimeMessage(session);
// set message headers
msg.addHeader("Content-type", "text/HTML; charset=UTF-8");
msg.addHeader("format", "flowed");
msg.addHeader("Content-Transfer-Encoding", "8bit");
msg.setFrom(new InternetAddress("no_reply@gmail.com", "NoReply-JD"));
msg.setReplyTo(InternetAddress.parse("no_reply@gmail.com", false));
msg.setSubject(subject, "UTF-8");
msg.setText(body, "UTF-8");
msg.setSentDate(new Date());
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toEmail, false));
System.out.println("Message is ready");
Transport.send(msg);
System.out.println("EMail Sent Successfully!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note that in the above code, the responsibility of EmailService is separated from UserService so now UserService does its responsibility related user operations and EmailService does it's responsibility related email operations.
Do comment if you like this post or give us a suggestion if any improvements needed.
Posts Related to SOLID Principles
- Single Responsibility Principle
- Open Closed Principle
- Liskov's Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Learn complete Oops concepts and SOLID Principles on Object Oriented Design in Java Tutorial
Learn beginners to expert Core Java on Java Tutorial (300 + Articles)
You can find all the top tutorials of this site on Java/J2EE Tutorials on JavaGuides
Free Spring Boot Tutorial | Full In-depth Course | Learn Spring Boot in 10 Hours
Watch this course on YouTube at Spring Boot Tutorial | Fee 10 Hours Full Course