A deep dive into the Single Responsibility Principle
"A class should have only one reason to change." -- Robert C. Martin
This means that a class should only have one responsibility or job. If a class has more than one responsibility, it has more than one reason to change, which can make it more complex and difficult to maintain. This principle promotes cohesion in classes and reduces the likelihood of brittle code that can break in unexpected ways when changes are made.
Single Responsibility Principle (SRP)
The Single Responsibility Principle (SRP) is a principle of object-oriented programming that states that a class should only have one reason to change. This principle is part of the SOLID principles, a popular set of five principles for designing and organizing classes and methods in an object-oriented way that promotes long-term maintainability.
This principle suggests that a class should only do one thing and do it well. If a class has more than one responsibility, it becomes coupled, and a change to one responsibility can affect the others. This coupling can make the code more difficult to understand, modify, and maintain.
The Goal of SRP
The goal is to minimize the impact of change by isolating it. If we have to change something, we should only need to update one class. If a class has too many responsibilities, a change in the requirements of one responsibility might affect the other responsibilities of the class.
SRP in TypeScript
In TypeScript, as in any other object-oriented programming language, SRP can be applied by breaking down large, complex classes into smaller, more manageable ones. Each of these smaller classes should have a single responsibility.
Here's an example. Let's say you have a User
class in a web application, and
this class handles both user data management (like user details and preferences)
and user authentication. According to SRP, these should be separate classes
because they represent different areas of responsibility. A potential change in
the way user data is managed should not affect the way user authentication is
handled, and vice versa.
class User {
name: string;
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}
class UserAuthentication {
user: User;
constructor(user: User) {
this.user = user;
}
authenticate(password: string): boolean {
// Implement authentication logic here
}
}
In the above example, we have separated the User
class, which is responsible
for managing user information, from the UserAuthentication
class, which
handles the authentication of the user. Each class now has a single reason to
change, which is aligned with the Single Responsibility Principle.
Time To Transition From JavaScript To TypeScript
Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.
SEE COURSE DETAILSReal World Applications of SRP
Let's consider a real-world example in a blogging application where we
might initially have a BlogPost
class responsible for both content
management (like creating, updating, and deleting blog posts) and displaying
posts in HTML. This class might look something like this:
class BlogPost {
title: string;
content: string;
constructor(title: string, content: string) {
this.title = title;
this.content = content;
}
// Methods related to content management
createPost() {
// Implementation here
}
updatePost() {
// Implementation here
}
deletePost() {
// Implementation here
}
// Method related to post display
displayHTML() {
return `<h1>${this.title}</h1><p>${this.content}</p>`;
}
}
Here, the BlogPost
class has two reasons to change. One is when the content
management functionality changes, and the other is when the way we want to
display the posts changes. This design does not follow the Single Responsibility
Principle.
We can refactor this to follow SRP by separating the content management and display responsibilities into two classes:
class BlogPost {
title: string;
content: string;
constructor(title: string, content: string) {
this.title = title;
this.content = content;
}
// Methods related to content management
createPost() {
// Implementation here
}
updatePost() {
// Implementation here
}
deletePost() {
// Implementation here
}
}
class BlogPostDisplay {
blogPost: BlogPost;
constructor(blogPost: BlogPost) {
this.blogPost = blogPost;
}
displayHTML() {
return `<h1>${this.blogPost.title}</h1><p>${this.blogPost.content}</p>`;
}
}
Now, BlogPost
class is responsible only for content management,
and BlogPostDisplay
class is responsible for displaying posts. Each of these
classes has one reason to change, which adheres to the Single Responsibility
Principle.
This separation improves maintainability and understandability. If we need to
change how posts are displayed, we only need to modify the BlogPostDisplay
class. Similarly, if we need to change how posts are managed, we only need to
modify the BlogPost
class. This reduces the likelihood that a change in one
area of functionality will inadvertently affect another.
What Can You Do Next 🙏😊
If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.