Règle
Cours devraient avoir responsabilité responsabilité.
Les classes gestion plusieurs problèmes violent
la Responsabilité principe .
Langages pris en charge : JS, TS, PY, JAVA, C/C++,
C#, Swift/Objective C, Ruby. PHP, Kotlin,
Scala, Rust, Haskell, Groovy, Dart. Julia,
Elixit, Klojure, OCaml, DelphiIntroduction
Les classes qui en font trop deviennent des goulots d'étranglement. Une classe gérant l'authentification, les e-mails et la validation nécessite des modifications chaque fois qu'une préoccupation évolue, risquant de provoquer des ruptures dans des fonctionnalités non liées. Les tests exigent de simuler l'intégralité de la classe, même lors du test d'un seul aspect. Le principe de responsabilité unique (Single Responsibility Principle) stipule qu'une classe ne devrait avoir qu'une seule raison de changer.
Pourquoi c'est important
Maintenabilité du code : Les classes ayant de multiples responsabilités changent plus souvent car l'évolution de toute préoccupation affecte la classe entière.
Complexité des tests : Tester des classes à responsabilités multiples nécessite de simuler (mocking) toutes les dépendances, même pour tester une seule fonctionnalité.
Réutilisabilité : Il est impossible d'extraire une responsabilité sans entraîner toutes les dépendances. Les développeurs dupliquent le code plutôt que de démêler des classes à responsabilités multiples.
Coordination d'équipe : Plusieurs développeurs travaillant sur la même classe pour différentes fonctionnalités génèrent de fréquents conflits de fusion. Les classes à responsabilité unique permettent un développement parallèle sans conflits.
Exemples de code
❌ Non conforme :
class UserManager {
async createUser(userData) {
const user = await db.users.insert(userData);
await this.sendWelcomeEmail(user.email);
await this.logEvent('user_created', user.id);
await cache.set(`user:${user.id}`, user);
return user;
}
async sendWelcomeEmail(email) {
const template = this.loadEmailTemplate('welcome');
await emailService.send(email, template);
}
async logEvent(event, userId) {
await analytics.track(event, { userId, timestamp: Date.now() });
}
}
Pourquoi c'est incorrect : Cette classe gère les opérations de base de données, l'envoi d'e-mails, la journalisation et la mise en cache. Toute modification des modèles d'e-mails, des formats de journalisation ou de la stratégie de cache nécessite de modifier cette classe. Tester la création d'utilisateurs implique de simuler les services d'e-mails, d'analyse et de cache, ce qui rend les tests lents et fragiles.
✅ Conforme :
class UserRepository {
async create(userData) {
return await db.users.insert(userData);
}
}
class EmailNotificationService {
async sendWelcomeEmail(email) {
const template = await this.templateLoader.load('welcome');
return await this.emailSender.send(email, template);
}
}
class UserEventLogger {
async logCreation(userId) {
return await this.analytics.track('user_created', {
userId,
timestamp: Date.now()
});
}
}
class UserService {
constructor(repository, emailService, eventLogger, cache) {
this.repository = repository;
this.emailService = emailService;
this.eventLogger = eventLogger;
this.cache = cache;
}
async createUser(userData) {
const user = await this.repository.create(userData);
await Promise.all([
this.emailService.sendWelcomeEmail(user.email),
this.eventLogger.logCreation(user.id),
this.cache.set(`user:${user.id}`, user)
]);
return user;
}
}
Pourquoi c'est important : Chaque classe a une responsabilité claire : persistance des données, envoi d'e-mails, journalisation des événements ou orchestration. Les modifications des modèles d'e-mails n'affectent que EmailNotificationService. Le test de la création d'utilisateurs peut s'appuyer sur de simples stubs pour les dépendances. Les classes peuvent être réutilisées indépendamment à travers différentes fonctionnalités.
Conclusion
Le principe de responsabilité unique ne vise pas à rendre les classes aussi petites que possible, mais à garantir que chaque classe ait une raison claire de changer. Lorsqu'une classe commence à gérer plusieurs préoccupations, refactorisez en extrayant chaque responsabilité dans sa propre classe avec une interface dédiée. Cela rend le code plus facile à tester, à maintenir et à faire évoluer sans provoquer de changements en cascade sur des fonctionnalités non liées.
.avif)
