Skip to main content

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.

TypeScript Course Instructor Image
TypeScript Course Instructor Image

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 DETAILS

Real 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.

YouTube @cloudaffle