Single Resonposility Principle in GO
Introduction
The single Resonposility Principle (SRP) states that a class, function, module, or type
should have only reason to change.
The idea is to design classes or types that are focused on a single task or responsibility
. This makes the code more modular, maintainable, and easier to understand
.
What happens if SRP is not followed?
Violating this principle leads to classes or types with more than one responsibility, which results in code that is challenging to
maintain
test
extend
Such violations often lead to tightly coupled code, reduced reusability, and an increased likelihood of errors.
Adhering to SRP ensures that classes or types are focused on a single responsibility, promoting modularity, maintainability, and code clarity.
Example: SRP Violation
package main
import "fmt"
type User struct {
Username string
Password string
}
type UserService struct {
users []User
}
func (us *UserService) RegisterUser(username, password string) {
newUser := User{Username: username, Password: password}
us.users = append(us.users, newUser)
fmt.Println("User registered:", newUser.Username)
}
func (us *UserService) AuthenticateUser(username, password string) bool {
for _, user := range us.users {
if user.Username == username && user.Password == password {
fmt.Println("User authenticated:", username)
return true
}
}
fmt.Println("Authentication failed for:", username)
return false
}
func main() {
userService := UserService{}
userService.RegisterUser("Ajay Gupta", "AjayGuptaPassword")
userService.AuthenticateUser("Ravi Verma", "RaviVermaPassword")
}
In this example, the UserService handles both user registration and user authentication. However, this violates the SRP because the type UserService
has two distinct responsibilities,
RegisterUser
AuthenticateUser
Now, let's consider the implications of a change in the user registration logic. Suppose you need to modify the way user registration works, perhaps adding additional validation steps or changing the underlying storage mechanism. If the registration logic is bundled with user authentication, any change to the registration process might also affect the authentication logic. This interdependence makes the code more fragile and harder to maintain.
A very important point to notice is both methods RegisterUser
and AuthenticateUser
follow the SRP but the type UserService
violates the SRP.
When SRP is compliant
A more SRP-compliant approach would involve separating these responsibilities into different types,
RegistrationService
AuthenticationService
package main
import "fmt"
type User struct {
Username string
Password string
}
type RegistrationService struct {
users []User
}
func (rs *RegistrationService) RegisterUser(username, password string) {
newUser := User{Username: username, Password: password}
rs.users = append(rs.users, newUser)
fmt.Println("User registered:", newUser.Username)
}
type AuthenticationService struct {
users []User
}
func (as *AuthenticationService) AuthenticateUser(username, password string) bool {
for _, user := range as.users {
if user.Username == username && user.Password == password {
fmt.Println("User authenticated:", username)
return true
}
}
fmt.Println("Authentication failed for:", username)
return false
}
func main() {
registrationService := RegistrationService{}
registrationService.RegisterUser("Ajay Verma", "AjayVermaPassword")
authenticationService := AuthenticationService{users: registrationService.users}
authenticationService.AuthenticateUser("Ravi Bisnoi", "RaviBisnoiPassword")
}
Now, the responsibilities of user registration and authentication are separated into RegistrationService
and AuthenticationService
types, respectively, adhering to the Single Responsibility Principle.
Summary
In summary:
Initial Example (SRP Violation):
UserService
handles both user registration and authentication.Changes to registration logic might affect authentication logic, and vice versa.
The type has multiple reasons to change, violating SRP.
Refactored Example (SRP Adherence):
RegistrationService
handles user registration.AuthenticationService
handles user authentication.Changes in registration logic won't impact authentication logic and vice versa.
Each type has a single responsibility, adhering to SRP.
By adhering to the Single Responsibility Principle, you create code that is more modular, easier to understand, and less prone to unintended consequences when changes are made. This separation of concerns contributes to better maintainability and flexibility in the long run.
Subscribe to my newsletter
Read articles from Ankit Chahal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by