Files
2025-11-30 09:05:34 +08:00

12 KiB
Raw Permalink Blame History

Refactor

Implémente une refactorisation de code sûre et progressive, évalue quantitativement le respect des principes SOLID. Visualise la dette technique et clarifie les priorités d'amélioration.

Utilisation

# Identification de code complexe et plan de refactorisation
find . -name "*.js" -exec wc -l {} + | sort -rn | head -10
"Refactorisez ces gros fichiers pour réduire la complexité"

# Détection et intégration de code dupliqué
grep -r "function processUser" . --include="*.js"
"Unifiez ces fonctions dupliquées avec Extract Method"

# Détection des violations des principes SOLID
grep -r "class.*Service" . --include="*.js" | head -10
"Évaluez si ces classes suivent le principe de responsabilité unique"

Exemples de base

# Détection de méthodes longues
grep -A 50 "function" src/*.js | grep -B 50 -A 50 "return" | wc -l
"Divisez les méthodes de plus de 50 lignes avec Extract Method"

# Complexité des branchements conditionnels
grep -r "if.*if.*if" . --include="*.js"
"Améliorez ces déclarations conditionnelles imbriquées avec le pattern Strategy"

# Détection des code smells
grep -r "TODO\|FIXME\|HACK" . --exclude-dir=node_modules
"Résolvez les commentaires qui sont devenus de la dette technique"

Techniques de refactorisation

Extract Method (Extraction de méthode)

// Avant : Méthode étendue
function processOrder(order) {
  // 50 lignes de traitement complexe
}

// Après : Séparation des responsabilités
function processOrder(order) {
  validateOrder(order);
  calculateTotal(order);
  saveOrder(order);
}

Replace Conditional with Polymorphism

// Avant : instruction switch
function getPrice(user) {
  switch (user.type) {
    case "premium":
      return basePrice * 0.8;
    case "regular":
      return basePrice;
  }
}

// Après : pattern Strategy
class PremiumPricing {
  calculate(basePrice) {
    return basePrice * 0.8;
  }
}

Score des principes SOLID (0-100 points)

Critères d'évaluation et notation

S - Single Responsibility (20 points)
├─ Nombre de responsabilités de classe : 1 (20 pts) | 2 (15 pts) | 3 (10 pts) | 4+ (5 pts)
├─ Nombre de méthodes : <7 (+5 pts) | 7-15 (+3 pts) | >15 (0 pts)
├─ Clarté de la raison de changement : Claire (+5 pts) | Ambiguë (0 pts)
└─ Exemple de score : UserService(authentification+traitement de données) = 10 points

O - Open/Closed (20 points)
├─ Points d'extension : Strategy/Template Method (20 pts) | Héritage seul (10 pts) | Aucun (5 pts)
├─ Changements de code existant lors de l'ajout de nouvelles fonctions : Non nécessaire (+5 pts) | Minimal (+3 pts) | Nécessaire (0 pts)
├─ Utilisation d'interfaces : Appropriée (+5 pts) | Partielle (+3 pts) | Aucune (0 pts)
└─ Exemple de score : PaymentProcessor(Strategy) = 20 points

L - Liskov Substitution (20 points)
├─ Adhésion au contrat des classes dérivées : Complète (20 pts) | Partielle (10 pts) | Violation (0 pts)
├─ Renforcement des préconditions : Aucun (+5 pts) | Existe (-5 pts)
├─ Affaiblissement des postconditions : Aucun (+5 pts) | Existe (-5 pts)
└─ Exemple de score : Square extends Rectangle = 0 points (violation)

I - Interface Segregation (20 points)
├─ Taille d'interface : 1-3 méthodes (20 pts) | 4-7 (15 pts) | 8+ (5 pts)
├─ Implémentation de méthodes non utilisées : Aucune (+5 pts) | 1-2 (+2 pts) | 3+ (0 pts)
├─ Clarté du rôle : Rôle unique (+5 pts) | Multiples rôles (0 pts)
└─ Exemple de score : Séparation Readable/Writable = 20 points

D - Dependency Inversion (20 points)
├─ Direction de dépendance : Abstraction seule (20 pts) | Mixte (10 pts) | Concrète seule (5 pts)
├─ Utilisation de DI : Constructor Injection (+5 pts) | Setter (+3 pts) | Aucune (0 pts)
├─ Capacité de test : Mockable (+5 pts) | Difficile (0 pts)
└─ Exemple de score : Repository Pattern = 20 points

Score total = S + O + L + I + D
├─ 90-100 points : Excellent (Conformité SOLID complète)
├─ 70-89 points : Bon (Marge d'amélioration légère)
├─ 50-69 points : Acceptable (Refactorisation recommandée)
├─ 30-49 points : Pauvre (Amélioration à grande échelle nécessaire)
└─ 0-29 points : Critique (Révision de conception obligatoire)

Quantification de la dette technique

Formule de calcul de la dette

Dette technique (temps) = Score de complexité × Portée d'impact × Difficulté de correction

Score de complexité :
├─ Complexité cyclomatique : 1-5(faible) | 6-10(moyenne) | 11-20(élevée) | 21+(dangereuse)
├─ Complexité cognitive : Profondeur d'imbrication × Nombre de branchements conditionnels
├─ Lignes de code : <50(1 pt) | 50-200(2 pts) | 200-500(3 pts) | 500+(5 pts)
└─ Taux de duplication : 0-10%(1 pt) | 10-30%(2 pts) | 30-50%(3 pts) | 50%+(5 pts)

Portée d'impact :
├─ Nombre de modules dépendants : Dépendance directe + Dépendance indirecte × 0.5
├─ Fréquence d'utilisation : Nombre d'appels API/jour
├─ Importance métier : Critique(×3) | Élevée(×2) | Moyenne(×1) | Faible(×0.5)
└─ Connaissance de l'équipe : 1 personne qui comprend(×3) | 2-3(×2) | 4+(×1)

Difficulté de correction :
├─ Couverture de tests : 0%(×3) | <50%(×2) | 50-80%(×1.5) | >80%(×1)
├─ Documentation : Aucune(×2) | Insuffisante(×1.5) | Suffisante(×1)
├─ Relations de dépendance : Couplage fort(×3) | Modéré(×2) | Couplage faible(×1)
└─ Risque de changement : Breaking Change(×3) | Considération de compatibilité(×2) | Sûr(×1)

Conversion de coûts :
├─ Coût de temps : Temps de dette × Salaire horaire du développeur
├─ Coût d'opportunité : Jours de retard dans le développement de nouvelles fonctionnalités × Impact quotidien sur les ventes
├─ Coût de qualité : Probabilité d'apparition de bugs × Coût de correction × Fréquence d'apparition
└─ Coût total : Temps + Coût d'opportunité + Coût de qualité

Matrice de priorités

Priorité Degré d'impact Coût de correction Délai de réponse Exemple concret Action recommandée
Critical (Réponse immédiate) Élevé Faible Dans 1 semaine God Object, dépendance circulaire Commencer la refactorisation immédiatement
Important (Réponse planifiée) Élevé Élevé Dans 1 mois Séparation de responsabilités à grande échelle, changement d'architecture Incorporer dans la planification de sprint
Watch (Objet de surveillance) Faible Élevé Dans 3 mois Traitement interne de haute complexité Surveillance des métriques, réponse en cas d'aggravation
Acceptable (Gamme acceptable) Faible Faible Ne nécessite pas de réponse Code smells légers Réponse avec refactorisation normale

Procédure de refactorisation

  1. Analyse et mesure de l'état actuel

    • Mesure de complexité (cyclomatique ・cognitive)
    • Calcul du score SOLID (0-100 points)
    • Quantification de la dette technique (temps/coût)
    • Création de matrice de priorités
  2. Exécution progressive

    • Petites étapes (unités de 15-30 minutes)
    • Exécution de tests après chaque changement
    • Commits fréquents
    • Mesure continue du score SOLID
  3. Confirmation de qualité

    • Maintien de la couverture de tests
    • Mesure de performance
    • Confirmation de réduction de la dette technique
    • Révision de code

Code smells courants et score de dette

Code Smell Critère de détection Score de dette Méthode d'amélioration
God Object Responsabilités >3, méthodes >20 Élevé (15-20h) Extract Class, application SRP
Long Method Lignes >50, complexité >10 Moyen (5-10h) Extract Method
Duplicate Code Taux de duplication >30% Élevé (10-15h) Extract Method/Class
Large Class Lignes >300, méthodes >15 Élevé (10-20h) Extract Class
Long Parameter List Paramètres >4 Faible (2-5h) Parameter Object
Feature Envy Références à autres classes >5 Moyen (5-10h) Move Method
Data Clumps Répétition du même groupe d'arguments Faible (3-5h) Extract Class
Primitive Obsession Utilisation excessive de types primitifs Moyen (5-8h) Replace with Object
Switch Statements case >5 Moyen (5-10h) Strategy Pattern
Shotgun Surgery Zones affectées lors du changement >3 Élevé (10-15h) Move Method/Field

Exemple pratique : Évaluation du score SOLID

// Objet d'évaluation : classe UserService
class UserService {
  constructor(db, cache, logger, emailService) { // 4 dépendances
    this.db = db;
    this.cache = cache;
    this.logger = logger;
    this.emailService = emailService;
  }

  // Responsabilité 1 : authentification
  authenticate(username, password) { /* ... */ }
  refreshToken(token) { /* ... */ }

  // Responsabilité 2 : gestion des utilisateurs
  createUser(data) { /* ... */ }
  updateUser(id, data) { /* ... */ }
  deleteUser(id) { /* ... */ }

  // Responsabilité 3 : notification
  sendWelcomeEmail(user) { /* ... */ }
  sendPasswordReset(email) { /* ... */ }
}

// Résultat d'évaluation du score SOLID
S: 10 points (3 responsabilités : authentification, CRUD, notification)
O: 5 points (sans points d'extension, implémentation directe)
L: 15 points (sans héritage, non applicable)
I: 10 points (interface non ségrégée)
D: 10 points (dépendance de classes concrètes)
Total: 50 points (Acceptable - Refactorisation recommandée)

// Dette technique
Complexité: 15 (7 méthodes, 3 responsabilités)
Portée d'impact: 8 (authentification utilisée dans toutes les fonctions)
Difficulté de correction: 2 (avec tests, documentation insuffisante)
Temps de dette: 15 × 8 × 2 = 240 heures
Priorité: Critical (système d'authentification nécessite réponse immédiate)

Exemple d'implémentation après amélioration

// Après application des principes SOLID (Score : 90 points)

// S : Responsabilité unique (20 points)
class AuthenticationService {
  authenticate(credentials) { /* ... */ }
  refreshToken(token) { /* ... */ }
}

// O : Ouvert/fermé (20 points)
class UserRepository {
  constructor(storage) { // Strategy Pattern
    this.storage = storage;
  }
  save(user) { return this.storage.save(user); }
}

// I : Ségrégation d'interface (20 points)
interface Readable {
  find(id);
  findAll();
}
interface Writable {
  save(entity);
  delete(id);
}

// D : Inversion de dépendance (20 points)
class UserService {
  constructor(
    private auth: IAuthService,
    private repo: IUserRepository,
    private notifier: INotificationService
  ) {}
}

// Réduction de dette : 240 heures → 20 heures (92% de réduction)

Support d'automatisation

# Mesure du score SOLID
npx solid-analyzer src/ --output report.json

# Analyse de complexité
npx complexity-report src/ --format json
sonar-scanner -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info

# Visualisation de la dette technique
npx code-debt-analyzer --config .debt.yml

# Format de code
npm run lint:fix
prettier --write src/

# Exécution de tests et couverture
npm test -- --coverage
npm run test:mutation  # tests de mutation

Précautions

  • Interdiction de changements fonctionnels : Ne pas changer le comportement externe
  • Test first : Ajouter des tests avant la refactorisation
  • Approche progressive : Ne pas faire de gros changements d'un coup
  • Vérification continue : Exécution de tests à chaque étape