Design Pattern
SOLID

Open/Closed Principle

Ouvert à l'extension, fermé à la modification

Principe

Un module doit être ouvert à l'extension (on peut ajouter des comportements) mais fermé à la modification (on ne touche pas au code existant pour le faire).

En pratique : ajouter une fonctionnalité ne doit pas nécessiter de modifier du code existant qui fonctionne.

Violation classique

type DiscountType = 'vip' | 'student' | 'employee';

function calculateDiscount(price: number, type: DiscountType): number {
  if (type === 'vip') return price * 0.8;
  if (type === 'student') return price * 0.85;
  if (type === 'employee') return price * 0.7;
  return price;
}

Chaque nouvelle réduction nécessite de modifier calculateDiscount. Si cette fonction est dans un package externe ou partagé entre équipes, c'est un problème.

Application de l'OCP

interface DiscountStrategy {
  apply(price: number): number;
}

const vipDiscount: DiscountStrategy = {
  apply: (price) => price * 0.8,
};

const studentDiscount: DiscountStrategy = {
  apply: (price) => price * 0.85,
};

const employeeDiscount: DiscountStrategy = {
  apply: (price) => price * 0.7,
};

// calculateDiscount ne change plus
function calculateDiscount(price: number, strategy: DiscountStrategy): number {
  return strategy.apply(price);
}

// Ajouter une réduction = ajouter un objet, pas modifier calculateDiscount
const seasonalDiscount: DiscountStrategy = {
  apply: (price) => price * 0.9,
};

En React : les render props et les slots

OCP se retrouve naturellement dans la conception de composants React réutilisables.

// Fermé : le composant Card impose son rendu interne
function Card({ title, content }: { title: string; content: string }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <p>{content}</p>
    </div>
  );
}

// Ouvert : le parent contrôle ce qui est rendu
function Card({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div className="card">
      <h2>{title}</h2>
      {children}
    </div>
  );
}

// Le composant Card ne change pas quand on veut afficher autre chose
<Card title="Produit">
  <ProductDetails product={product} />
  <AddToCartButton productId={product.id} />
</Card>

Nuance

OCP ne signifie pas qu'on ne modifie jamais rien. Si une règle métier change fondamentalement, il faut modifier le code. L'idée est d'anticiper les points de variation connus et de les rendre extensibles, sans suringénierie pour des cas hypothétiques.