Aller au contenu
  1. Exemples/

Single Responsibility Principle (SRP) - Refactoring d'une Classe Invoice

·14 mins· loading · loading · · ·
Conception Back-End
Adrien D'acunto
Auteur
Adrien D’acunto
Sommaire

Single Responsibility Principle (SRP) : Refactoring d’une Classe Invoice
#

Introduction
#

Le Single Responsibility Principle (SRP) est le premier principe SOLID. Il stipule qu’une classe ne devrait avoir qu’une seule raison de changer, c’est-à-dire une seule responsabilité.

Dans cet article, nous allons analyser un cas classique de violation du SRP avec une classe Invoice qui fait “tout”, puis nous verrons comment la refactoriser en appliquant ce principe fondamental.

Problème : La Classe “God Object”
#

Classe Invoice Monolithique
#

Voici une classe Invoice typique qui viole le SRP :

class Invoice {
  private id: string;
  private customerName: string;
  private customerEmail: string;
  private items: Item[];
  private discount: number;
  private taxRate: number;

  constructor(customerName: string, customerEmail: string) {
    this.id = Math.random().toString();
    this.customerName = customerName;
    this.customerEmail = customerEmail;
    this.items = [];
    this.discount = 0;
    this.taxRate = 0.20;
  }

  addItem(item: Item): void {
    this.items.push(item);
  }

  calculateSubTotal(): number {
    return this.items.reduce((sum, item) => 
      sum + item.price  item.quantity, 0);
  }

  calculateTax(): number {
    return this.calculateSubTotal()  this.taxRate;
  }

  calculateTotal(): number {
    const subtotal = this.calculateSubTotal();
    const tax = this.calculateTax();
    return subtotal + tax - this.discount;
  }

  applyDiscount(amount: number): void {
    this.discount = amount;
  }

  validateCustomerEmail(): boolean {
    const emailRegex = /^[\w\d]+@[\w\d]+\.[\w\d]+$/;
    return emailRegex.test(this.customerEmail);
  }

  saveToDatabase(): void {
    const db = new DatabaseConnection();
    db.query('INSERT INTO invoices VALUES (...)');
    console.log('Facture sauvegardée en base');
  }

  sendEmailToCustomer(): void {
    if (!this.validateCustomerEmail()) {
      throw new Error('Email invalide');
    }
    const smtpServer = new SMTPConnection('smtp.company.com');
    smtpServer.send({
      to: this.customerEmail,
      subject: 'Votre facture',
      body: 'Bonjour, voici votre facture...'
    });
  }

  generatePDF(): Buffer {
    const pdf = new PDFGenerator();
    pdf.addText('Facture #' + this.id);
    pdf.addText('Client: ' + this.customerName);
    this.items.forEach(item => {
      pdf.addText(item.name + ': ' + item.price + '€');
    });
    pdf.addText('Total: ' + this.calculateTotal() + '€');
    return pdf.render();
  }

  printInvoice(): void {
    console.log('=== FACTURE ===');
    console.log('ID: ' + this.id);
    console.log('Client: ' + this.customerName);
    console.log('Total: ' + this.calculateTotal() + '€');
  }
}

Classe Item (Support)
#

class Item {
  name: string;
  price: number;
  quantity: number;
}

Analyse : Combien de Responsabilités ?
#

Diagramme de la Classe Monolithique
#

classDiagram
    class Invoice {
        -id: String
        -customerName: String
        -customerEmail: String
        -items: Item[]
        -discount: Number
        -taxRate: Number
        
        +calculateSubTotal() Number
        +calculateTax() Number
        +applyDiscount() void
        +validateCustomerEmail() boolean
        +sendTooDB() void
        +sendEmailToCustomer() void
        +generatePDF() Buffer
        +printInvoice() void
    }
    
    class Item {
        +name: String
        +price: Number
        +quantity: Number
    }
    
    Invoice "1" --> "" Item : contains

Les Questions Révélatrices
#

Posez-vous les bonnes questions :

  1. Combien de responsabilités la classe Invoice couvre-t-elle ?

    • Gestion des données de facturation
    • Calculs financiers (sous-total, taxes, remises)
    • Validation d’email
    • Persistance en base de données
    • Envoi d’emails
    • Génération de PDF
    • Affichage console

    Réponse : 7 responsabilités différentes !

  2. Que se passe-t-il si la structure de la base de données change ?

    • Il faut modifier la classe Invoice
    • Risque de régressions sur des fonctionnalités sans rapport (calculs, PDF, email)
  3. Que se passe-t-il si je dois traiter un autre taux de TVA ?

    • Il faut modifier la classe Invoice
    • Impact potentiel sur la sauvegarde, l’envoi d’email, etc.
  4. Essayez d’estimer le nombre de fois où je devrais remettre le nez dans cette classe

    • Changement de serveur SMTP → Modification de Invoice
    • Nouveau format de PDF → Modification de Invoice
    • Migration de base de données → Modification de Invoice
    • Nouveau calcul de taxe → Modification de Invoice
    • Validation email plus stricte → Modification de Invoice

    Réponse : TROP SOUVENT !

Violations du SRP
#

Cette classe viole le SRP car elle a plusieurs raisons de changer :

Raison du Changement Responsabilité Impactée Risque
Modification des règles de calcul de TVA calculateTax() Bugs dans PDF, email, BDD
Changement de serveur email sendEmailToCustomer() Bugs dans les calculs
Migration de base de données saveToDatabase() Bugs partout
Nouveau format de PDF generatePDF() Bugs dans les emails
Validation email plus stricte validateCustomerEmail() Bugs dans la sauvegarde

Solution : Séparation des Responsabilités
#

Architecture Refactorisée
#

Au lieu d’une classe monolithique, nous allons créer :

  1. Invoice : Gestion des données métier uniquement
  2. InvoiceRepository : Persistance en base de données
  3. EmailService : Envoi d’emails
  4. PDFGenerator : Génération de documents PDF
  5. PricingStrategy : Calculs financiers (taxes, remises)
  6. CreateInvoice : Orchestration (use case)

Diagramme de Classes Refactorisé
#

classDiagram
    class Invoice {
        -id: String
        -customer: Customer
        -items: InvoiceItem[]
        
        +addInvoiceItem(item: InvoiceItem) void
        +calculateTotal() Number
        +getInvoicesItems() InvoiceItem[]
        +setCustomer(customer: Customer) void
    }
    
    class Customer {
        -name: String
        -email: String
        
        +getName() String
        +getEmail() String
        +isValidEmail() boolean
    }
    
    class InvoiceItem {
        -name: String
        -price: Number
        -quantity: Number
        
        +getTotal() Number
    }
    
    class InvoiceRepository {
        <>
        +save(invoice: Invoice) void
    }
    
    class EmailService {
        <>
        +send(invoice: Invoice) void
    }
    
    class PDFGenerator {
        <>
        +generate(invoice: Invoice) void
    }
    
    class PricingStrategy {
        <>
        +calculate(items: InvoiceItem[]) number
    }
    
    class StandardPricingStrategy {
        -taxRate: Number
        -discount: Number
        
        +calculate(invoice: Invoice) void
    }
    
    class CreateInvoice {
        -repository: InvoiceRepository
        -emailService: EmailService
        -pdfGenerator: PDFGenerator
        -pricingStrategy: PricingStrategy
        
        +process() void
    }
    
    Invoice "1" --> "1" Customer : has
    Invoice "1" --> "" InvoiceItem : contains
    
    CreateInvoice ..> Invoice : uses
    CreateInvoice --> InvoiceRepository : uses
    CreateInvoice --> EmailService : uses
    CreateInvoice --> PDFGenerator : uses
    CreateInvoice --> PricingStrategy : uses
    
    PricingStrategy <|.. StandardPricingStrategy : implements

Vue Simplifiée : Flux de Données
#

graph LR
    A[CreateInvoice] --> B[Invoice]
    A --> C[InvoiceRepository]
    A --> D[EmailService]
    A --> E[PDFGenerator]
    A --> F[PricingStrategy]
    
    F --> G[StandardPricingStrategy]
    
    style A fill:#4CAF50
    style B fill:#2196F3
    style C fill:#FF9800
    style D fill:#9C27B0
    style E fill:#F44336
    style F fill:#00BCD4

Implémentation Refactorisée
#

1. Entité Invoice - Données Métier Uniquement
#

class Invoice {
  private id: string;
  private customer: Customer;
  private items: InvoiceItem[];

  constructor(customer: Customer) {
    this.id = Math.random().toString();
    this.customer = customer;
    this.items = [];
  }

  addInvoiceItem(item: InvoiceItem): void {
    this.items.push(item);
  }

  getInvoicesItems(): InvoiceItem[] {
    return this.items;
  }

  setCustomer(customer: Customer): void {
    this.customer = customer;
  }

  getCustomer(): Customer {
    return this.customer;
  }

  getId(): string {
    return this.id;
  }

  // Calcul simple basé sur une stratégie externe
  calculateTotal(): number {
    // Délégation à une stratégie de pricing
    return 0; // Sera calculé par PricingStrategy
  }
}

2. Entités Support
#

class Customer {
  private name: string;
  private email: string;

  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }

  getName(): string {
    return this.name;
  }

  getEmail(): string {
    return this.email;
  }

  isValidEmail(): boolean {
    const emailRegex = /^[\w\d]+@[\w\d]+\.[\w\d]+$/;
    return emailRegex.test(this.email);
  }
}

class InvoiceItem {
  constructor(
    public name: string,
    public price: number,
    public quantity: number
  ) {}

  getTotal(): number {
    return this.price  this.quantity;
  }
}

3. Interface InvoiceRepository - Persistance
#

interface InvoiceRepository {
  save(invoice: Invoice): void;
}

class DatabaseInvoiceRepository implements InvoiceRepository {
  save(invoice: Invoice): void {
    const db = new DatabaseConnection();
    db.query('INSERT INTO invoices VALUES (...)');
    console.log('Facture sauvegardée en base');
  }
}

// Possibilité d'ajouter d'autres implémentations
class FileInvoiceRepository implements InvoiceRepository {
  save(invoice: Invoice): void {
    // Sauvegarde dans un fichier JSON
    console.log('Facture sauvegardée dans un fichier');
  }
}

4. Interface EmailService - Envoi d’Emails
#

interface EmailService {
  send(invoice: Invoice): void;
}

class SMTPEmailService implements EmailService {
  send(invoice: Invoice): void {
    const customer = invoice.getCustomer();
    
    if (!customer.isValidEmail()) {
      throw new Error('Email invalide');
    }

    const smtpServer = new SMTPConnection('smtp.company.com');
    smtpServer.send({
      to: customer.getEmail(),
      subject: 'Votre facture',
      body: `Bonjour ${customer.getName()}, voici votre facture...`
    });
    
    console.log('Email envoyé à ' + customer.getEmail());
  }
}

// Alternative avec un service cloud
class SendGridEmailService implements EmailService {
  send(invoice: Invoice): void {
    // Utilisation de SendGrid API
    console.log('Email envoyé via SendGrid');
  }
}

5. Interface PDFGenerator - Génération PDF
#

interface PDFGenerator {
  generate(invoice: Invoice): void;
}

class StandardPDFGenerator implements PDFGenerator {
  generate(invoice: Invoice): void {
    const pdf = new PDFLib();
    const customer = invoice.getCustomer();
    
    pdf.addText('Facture #' + invoice.getId());
    pdf.addText('Client: ' + customer.getName());
    
    invoice.getInvoicesItems().forEach(item => {
      pdf.addText(`${item.name}: ${item.price}€ x ${item.quantity}`);
    });
    
    return pdf.render();
  }
}

// Alternative avec un template plus élaboré
class FancyPDFGenerator implements PDFGenerator {
  generate(invoice: Invoice): void {
    // Génération PDF avec design avancé
    console.log('PDF généré avec template professionnel');
  }
}

6. Interface PricingStrategy - Calculs Financiers
#

interface PricingStrategy {
  calculate(items: InvoiceItem[]): number;
}

class StandardPricingStrategy implements PricingStrategy {
  private taxRate: number;
  private discount: number;

  constructor(taxRate: number = 0.20, discount: number = 0) {
    this.taxRate = taxRate;
    this.discount = discount;
  }

  calculate(items: InvoiceItem[]): number {
    // Calcul du sous-total
    const subtotal = items.reduce((sum, item) => 
      sum + item.getTotal(), 0);
    
    // Calcul de la taxe
    const tax = subtotal  this.taxRate;
    
    // Total final
    return subtotal + tax - this.discount;
  }
}

// Stratégie alternative pour clients premium
class PremiumPricingStrategy implements PricingStrategy {
  calculate(items: InvoiceItem[]): number {
    const subtotal = items.reduce((sum, item) => 
      sum + item.getTotal(), 0);
    
    // 10% de réduction automatique + pas de taxe
    return subtotal  0.90;
  }
}

7. Use Case CreateInvoice - Orchestration
#

class CreateInvoice {
  constructor(
    private repository: InvoiceRepository,
    private emailService: EmailService,
    private pdfGenerator: PDFGenerator,
    private pricingStrategy: PricingStrategy
  ) {}

  process(customer: Customer, items: InvoiceItem[]): void {
    // 1. Créer la facture
    const invoice = new Invoice(customer);
    
    // 2. Ajouter les items
    items.forEach(item => invoice.addInvoiceItem(item));
    
    // 3. Calculer le total avec la stratégie
    const total = this.pricingStrategy.calculate(invoice.getInvoicesItems());
    console.log(`Total calculé: ${total}€`);
    
    // 4. Sauvegarder en base
    this.repository.save(invoice);
    
    // 5. Générer le PDF
    this.pdfGenerator.generate(invoice);
    
    // 6. Envoyer par email
    this.emailService.send(invoice);
    
    console.log('Facture traitée avec succès !');
  }
}

Diagramme de Séquence : Création de Facture
#

sequenceDiagram
    participant Client
    participant UseCase as CreateInvoice
    participant Invoice
    participant Strategy as PricingStrategy
    participant Repo as InvoiceRepository
    participant PDF as PDFGenerator
    participant Email as EmailService
    
    Client->>UseCase: process(customer, items)
    UseCase->>Invoice: new Invoice(customer)
    Invoice-->>UseCase: invoice
    
    loop Pour chaque item
        UseCase->>Invoice: addInvoiceItem(item)
    end
    
    UseCase->>Strategy: calculate(items)
    Strategy-->>UseCase: total
    
    UseCase->>Repo: save(invoice)
    Repo-->>UseCase: sauvegardé
    
    UseCase->>PDF: generate(invoice)
    PDF-->>UseCase: PDF généré
    
    UseCase->>Email: send(invoice)
    Email-->>UseCase: email envoyé
    
    UseCase-->>Client: Facture traitée

Utilisation Pratique
#

Avant : Code Couplé et Rigide
#

// Tout est mélangé dans la classe Invoice
const invoice = new Invoice('John Doe', 'john@example.com');
invoice.addItem({ name: 'Laptop', price: 999, quantity: 1 });
invoice.addItem({ name: 'Mouse', price: 29, quantity: 2 });
invoice.applyDiscount(50);

console.log('Total:', invoice.calculateTotal());
invoice.saveToDatabase();
invoice.generatePDF();
invoice.sendEmailToCustomer();

Après : Code Découplé et Flexible
#

// Chaque responsabilité est séparée

// Configuration des dépendances (IoC/DI)
const repository = new DatabaseInvoiceRepository();
const emailService = new SMTPEmailService();
const pdfGenerator = new StandardPDFGenerator();
const pricingStrategy = new StandardPricingStrategy(0.20, 50);

// Use case
const createInvoice = new CreateInvoice(
  repository,
  emailService,
  pdfGenerator,
  pricingStrategy
);

// Utilisation
const customer = new Customer('John Doe', 'john@example.com');
const items = [
  new InvoiceItem('Laptop', 999, 1),
  new InvoiceItem('Mouse', 29, 2)
];

createInvoice.process(customer, items);

Flexibilité : Changer le Comportement Facilement
#

// Scénario 1 : Client premium avec stratégie différente
const premiumStrategy = new PremiumPricingStrategy();
const createPremiumInvoice = new CreateInvoice(
  repository,
  emailService,
  new FancyPDFGenerator(), // PDF plus élaboré
  premiumStrategy
);

// Scénario 2 : Mode test avec mock
const mockRepository = new InMemoryInvoiceRepository();
const mockEmailService = new NoOpEmailService();
const createTestInvoice = new CreateInvoice(
  mockRepository,
  mockEmailService,
  pdfGenerator,
  pricingStrategy
);

// Scénario 3 : Sauvegarde fichier au lieu de BDD
const fileRepository = new FileInvoiceRepository();
const createFileInvoice = new CreateInvoice(
  fileRepository,
  emailService,
  pdfGenerator,
  pricingStrategy
);

Comparaison Avant/Après
#

Tableau Comparatif
#

Aspect Classe Monolithique (Avant) Architecture SRP (Après)
Nombre de responsabilités 7 dans une seule classe 1 par classe
Lignes de code par classe ~150 lignes ~30 lignes en moyenne
Testabilité Difficile (dépendances cachées) Facile (injection de dépendances)
Modification BDD Change Invoice Change uniquement InvoiceRepository
Modification Email Change Invoice Change uniquement EmailService
Modification PDF Change Invoice Change uniquement PDFGenerator
Nouvelle stratégie de pricing Change Invoice Ajoute nouvelle PricingStrategy
Réutilisabilité Faible (tout est couplé) Forte (composants indépendants)
Compréhension Difficile (code complexe) Simple (responsabilités claires)

Diagramme de Dépendances
#

graph TB
    subgraph "AVANT - Couplage Fort"
        A1[Invoice]
        A1 -.-> A2[Calculs]
        A1 -.-> A3[Base de données]
        A1 -.-> A4[Email SMTP]
        A1 -.-> A5[PDF]
        A1 -.-> A6[Validation]
        A1 -.-> A7[Affichage]
    end
    
    subgraph "APRÈS - Couplage Faible"
        B1[CreateInvoice]
        B2[Invoice]
        B3[InvoiceRepository]
        B4[EmailService]
        B5[PDFGenerator]
        B6[PricingStrategy]
        
        B1 --> B2
        B1 --> B3
        B1 --> B4
        B1 --> B5
        B1 --> B6
    end

Bénéfices Concrets du SRP
#

1. Testabilité Améliorée
#

Avant :

// Difficile à tester - dépendances cachées
describe('Invoice', () => {
  it('should save and send email', () => {
    const invoice = new Invoice('John', 'john@test.com');
    // Comment mocker la BDD ? Comment mocker SMTP ?
    // Impossible sans modifier le code source !
  });
});

Après :

// Facile à tester - injection de mocks
describe('CreateInvoice', () => {
  it('should process invoice correctly', () => {
    const mockRepo = new MockInvoiceRepository();
    const mockEmail = new MockEmailService();
    const mockPDF = new MockPDFGenerator();
    const mockPricing = new MockPricingStrategy();
    
    const useCase = new CreateInvoice(
      mockRepo,
      mockEmail,
      mockPDF,
      mockPricing
    );
    
    const customer = new Customer('John', 'john@test.com');
    const items = [new InvoiceItem('Test', 100, 1)];
    
    useCase.process(customer, items);
    
    expect(mockRepo.saveCalled).toBe(true);
    expect(mockEmail.sendCalled).toBe(true);
  });
});

2. Maintenance Simplifiée
#

Exemple : Changement de serveur email

Avant :

// Modification dans la classe Invoice (risque de régression)
class Invoice {
  sendEmailToCustomer(): void {
    // Changer de smtp.company.com à smtp.newprovider.com
    const smtpServer = new SMTPConnection('smtp.newprovider.com');
    // ... reste du code
    // Risque : bug dans calculateTotal(), generatePDF(), etc.
  }
}

Après :

// Modification isolée dans EmailService
class SMTPEmailService implements EmailService {
  send(invoice: Invoice): void {
    // Changement isolé - aucun impact sur Invoice, PDF, etc.
    const smtpServer = new SMTPConnection('smtp.newprovider.com');
    // ...
  }
}

3. Évolutivité
#

Ajouter un nouveau mode de calcul (TVA différente par pays) :

// Nouvelle stratégie sans toucher au code existant
class EuropeanPricingStrategy implements PricingStrategy {
  calculate(items: InvoiceItem[]): number {
    const subtotal = items.reduce((sum, item) => 
      sum + item.getTotal(), 0);
    // TVA européenne variable selon pays
    return subtotal  1.21; // 21% Belgique par exemple
  }
}

// Utilisation immédiate
const euInvoice = new CreateInvoice(
  repository,
  emailService,
  pdfGenerator,
  new EuropeanPricingStrategy() // Aucun changement ailleurs
);

4. Réutilisabilité
#

// EmailService peut être réutilisé pour d'autres cas
class SendOrderConfirmation {
  constructor(private emailService: EmailService) {}
  
  send(order: Order): void {
    // Réutilisation du même service email
    this.emailService.send(order);
  }
}

// PDFGenerator peut servir pour d'autres documents
class GenerateReport {
  constructor(private pdfGenerator: PDFGenerator) {}
  
  create(report: Report): void {
    this.pdfGenerator.generate(report);
  }
}

Principes SOLID Appliqués
#

Single Responsibility Principle (SRP)
#

Cœur de notre refactoring ! Chaque classe a une seule raison de changer :

  • Invoice : structure des données change
  • InvoiceRepository : mode de persistance change
  • EmailService : provider email change
  • PDFGenerator : format PDF change
  • PricingStrategy : règles de calcul changent

Open/Closed Principle (OCP)
#

Les classes sont ouvertes à l’extension (nouvelles stratégies) mais fermées à la modification.

// Ajouter une nouvelle stratégie SANS modifier le code existant
class PromotionalPricingStrategy implements PricingStrategy {
  calculate(items: InvoiceItem[]): number {
    // 20% de réduction sur tout !
    const subtotal = items.reduce((sum, item) => 
      sum + item.getTotal(), 0);
    return subtotal  0.80;
  }
}

Dependency Inversion Principle (DIP)
#

CreateInvoice dépend des abstractions (interfaces), pas des implémentations concrètes.

// CreateInvoice ne connaît que les interfaces
class CreateInvoice {
  constructor(
    private repository: InvoiceRepository, // Interface
    private emailService: EmailService,   // Interface
    private pdfGenerator: PDFGenerator,   // Interface
    private pricingStrategy: PricingStrategy // Interface
  ) {}
}

Checklist SRP : Comment Identifier les Violations
#

Posez-vous ces questions pour identifier les violations du SRP :

  • Ma classe a-t-elle plus d’une raison de changer ?
  • Puis-je décrire ma classe en une seule phrase sans utiliser “et” ?
  • Ma classe dépend-elle de bibliothèques externes multiples ? (BDD, SMTP, PDF, etc.)
  • Si je dois modifier X, dois-je toucher à Y ?
  • Ma classe fait-elle plus de 200 lignes ?
  • Ma classe a-t-elle des méthodes qui n’utilisent qu’une partie des attributs ?
  • Est-ce que je peux diviser ma classe en sous-classes cohérentes ?

Si vous répondez OUI à 2+ questions → Violation probable du SRP !

Anti-Patterns à Éviter
#

God Object / Classe Dieu
#

// MAUVAIS - Fait tout
class Application {
  connectDatabase() {}
  sendEmail() {}
  generatePDF() {}
  processPayment() {}
  validateUser() {}
  logActivity() {}
  // ... 50 autres méthodes
}

Responsabilités Cachées
#

// MAUVAIS - Responsabilités masquées dans le constructeur
class User {
  constructor(name: string) {
    this.name = name;
    this.saveToDatabase(); // Side-effect caché !
    this.sendWelcomeEmail(); // Side-effect caché !
  }
}

Mixage Logique Métier et Infrastructure
#

// MAUVAIS - Logique métier + SQL dans la même classe
class Order {
  calculateTotal(): number {
    // Logique métier OK
    return this.items.reduce((sum, item) => sum + item.price, 0);
  }
  
  save(): void {
    // Infrastructure SQL dans l'entité métier
    db.query('INSERT INTO orders...');
  }
}

Conclusion
#

Le Single Responsibility Principle transforme le code de manière radicale :

Résumé des Bénéfices
#

Avant SRP Après SRP
Classe monolithique de 150+ lignes Classes ciblées de ~30 lignes
7 responsabilités mélangées 1 responsabilité par classe
Impossible à tester unitairement Tests simples avec mocks
Couplage fort (tout dépend de tout) Couplage faible (dépendances claires)
Modification risquée (effet domino) Modification sûre (impact isolé)
Réutilisation difficile Composants réutilisables
Compréhension complexe Code auto-documenté

La Règle d’Or du SRP
#

“Une classe ne devrait avoir qu’une seule raison de changer”

— Robert C. Martin (Uncle Bob)

Prochaines Étapes
#

Maintenant que vous maîtrisez le SRP, découvrez comment le combiner avec les autres principes SOLID :

  • Open/Closed Principle (OCP) : Extension sans modification
  • Liskov Substitution Principle (LSP) : Substitution sans surprises
  • Interface Segregation Principle (ISP) : Interfaces ciblées
  • Dependency Inversion Principle (DIP) : Dépendre des abstractions

Conseil pratique : Commencez toujours par appliquer le SRP avant les autres principes SOLID. C’est la fondation de toute architecture propre !

Articles connexes

Les Interfaces en Programmation Orientée Objet - Principe SOLID et Exemple Pratique
·7 mins· loading · loading
Conception Back-End
Le pattern Singleton - garantir une instance unique
·12 mins· loading · loading
Conception Back-End
Modélisation de base de données - Le cas Langlois
·7 mins· loading · loading
Conception Back-End
Les patrons de conception
·7 mins· loading · loading
Conception
Comment les base de données fonctionnent - Guide Complet
··25 mins· loading · loading
Back-End
Génération automatique de documentation de backlog avec Google Sheets et Apps Script
·2 mins· loading · loading
Documentation