Classes are crucial in object-oriented programming but poorly designed ones can lead to problematic code.
Clean Code Chapter 10 highlights the importance of cohesive classes with a single responsibility.
In this article, we'll share key insights and demonstrate their application in JavaScript.
Cohesion refers to how closely related the responsibilities of a class are.
A cohesive class focuses on a single purpose and has responsibilities that naturally fit together.
This keeps the class simple, readable, and easy to maintain.
Example: Low Cohesion
class User { constructor(name, email) { this.name = name; this.email = email; } // Handles user registration register() { // Registration logic } // Sends email to user sendEmail(message) { // Email sending logic } // Logs activity of the user logActivity(activity) { console.log(`${this.name} performed: ${activity}`); } }
In the example above, the User class has three unrelated responsibilities: user registration, sending emails, and logging activity.
This class lacks cohesion because it tries to do too many things simultaneously.
The Single Responsibility Principle states that a class should have one, and only one, reason to change. This means that each class should focus on a single concern.
If a class has more than one responsibility, changes in one area could break the functionality of another.
Let’s refactor the above example to adhere to SRP:
class User { constructor(name, email) { this.name = name; this.email = email; } } class UserRegistrationService { register(user) { // Registration logic } } class EmailService { sendEmail(user, message) { // Email sending logic } } class ActivityLogger { logActivity(user, activity) { console.log(`${user.name} performed: ${activity}`); } }
Now, each class has a single responsibility:
With this structure, changes to the email system won't affect the user registration logic, and vice versa.
Maintainability: When a class has a single responsibility, it's easier to locate and fix bugs. You don't need to wade through unrelated logic.
Scalability: As your project grows, adhering to SRP makes it simpler to add new features. New functionalities can be added in new classes without touching existing ones.
Testability: Classes with a single responsibility are easier to test. Each class has a limited scope, so unit tests can focus on individual pieces of functionality.
To ensure that your classes are cohesive, look for areas where multiple responsibilities are being combined.
Often, a class starts simple, but as features are added, it can accumulate extra duties.
Example: Refactoring a Payment System
Let’s say we have a PaymentProcessor class that handles multiple tasks:
class PaymentProcessor { constructor() { this.paymentGateway = new PaymentGateway(); } processPayment(paymentDetails) { // Payment processing logic } validatePaymentDetails(paymentDetails) { // Validation logic } logTransaction(paymentDetails) { console.log(`Payment processed: ${JSON.stringify(paymentDetails)}`); } }
Here, PaymentProcessor is responsible for processing payments, validating details, and logging transactions.
This is an opportunity to refactor and split responsibilities:
class PaymentProcessor { constructor(paymentGateway, validator, logger) { this.paymentGateway = paymentGateway; this.validator = validator; this.logger = logger; } processPayment(paymentDetails) { if (this.validator.isValid(paymentDetails)) { this.paymentGateway.process(paymentDetails); this.logger.logTransaction(paymentDetails); } } } class PaymentValidator { isValid(paymentDetails) { // Validation logic return true; // Simplified for example } } class PaymentLogger { logTransaction(paymentDetails) { console.log(`Payment processed: ${JSON.stringify(paymentDetails)}`); } }
Now, the PaymentProcessor class has a single responsibility: processing payments.
Validation and logging have been moved to separate classes (PaymentValidator and PaymentLogger).
Designing classes with cohesion and adhering to the Single Responsibility Principle ensures your codebase remains flexible, maintainable, and easy to understand.
By splitting responsibilities into separate, focused classes, you reduce the complexity of individual components and make your system more robust.
In JavaScript, where classes are often used in larger frameworks or applications, following these principles will significantly improve the quality of your code.
As the Clean Code book explains, writing clean classes isn’t just about organizing code—it’s about creating a system that can evolve without becoming a nightmare to maintain.
Happy Coding!
Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.
Copyright© 2022 湘ICP备2022001581号-3