Modern Design Patterns
Chapitre 3
Chapitre 03 — Patterns Factory (approfondi)
Chapitre 03 — Patterns Factory (approfondi)
Cours 03 — Patterns Factory (approfondi)
Partie 1 : Simple Factory
1.1 Définition
La Simple Factory (ou Static Factory) est un idiome, pas un pattern GoF : une classe statique qui crée des objets selon un paramètre.
Diagramme en cours de génération...
1.2 Exemple
interface PaymentProvider {
process(amount: number): void;
}
class StripeProvider implements PaymentProvider {
process(amount: number): void {
console.log(`Stripe: $${amount}`);
}
}
class PayPalProvider implements PaymentProvider {
process(amount: number): void {
console.log(`PayPal: $${amount}`);
}
}
// Simple Factory
class PaymentProviderFactory {
static create(type: "stripe" | "paypal"): PaymentProvider {
switch (type) {
case "stripe": return new StripeProvider();
case "paypal": return new PayPalProvider();
}
}
}
// Usage
const provider = PaymentProviderFactory.create("stripe");
provider.process(100);
1.3 Limites
- Violation OCP : ajouter un provider = modifier la factory
- Pas d'héritage : pas de personnalisation possible
- Testabilité : difficile à mocker
Partie 2 : Factory Method
2.1 Définition (GoF)
"Définir une interface pour créer un objet, mais laisser les sous-classes décider quelle classe instancier. La Factory Method permet à une classe de reporter l'instanciation à ses sous-classes."
2.2 Structure détaillée
Diagramme en cours de génération...
2.3 Exemple : Framework de parsing
interface Document {
title: string;
content: string;
}
interface Parser {
parse(data: string): Document;
}
class JSONParser implements Parser {
parse(data: string): Document {
return JSON.parse(data);
}
}
class XMLParser implements Parser {
parse(data: string): Document {
// Simulation XML parsing
return { title: "XML Doc", content: data };
}
}
abstract class DocumentProcessor {
// Template Method
process(data: string): void {
const parser = this.createParser();
const doc = parser.parse(data);
this.validate(doc);
this.save(doc);
}
// Factory Method
protected abstract createParser(): Parser;
private validate(doc: Document): void {
if (!doc.title) throw new Error("Title required");
}
private save(doc: Document): void {
console.log(`Saving document: ${doc.title}`);
}
}
class JSONProcessor extends DocumentProcessor {
protected createParser(): Parser {
return new JSONParser();
}
}
class XMLProcessor extends DocumentProcessor {
protected createParser(): Parser {
return new XMLParser();
}
}
// Usage
const processor: DocumentProcessor = new JSONProcessor();
processor.process('{"title": "Hello", "content": "World"}');
2.4 Factory Method + Template Method (lien fort)
La Factory Method est presque toujours appelée dans une Template Method :
abstract class Application {
// Template Method
run(): void {
this.init();
const doc = this.createDocument(); // Factory Method
doc.open();
doc.save();
this.cleanup();
}
protected abstract createDocument(): Document;
protected init(): void { /* default */ }
protected cleanup(): void { /* default */ }
}
2.5 Parametric Factory Method
Version où la factory method prend un paramètre :
abstract class PaymentService {
protected abstract createPayment(type: string): Payment;
pay(type: string, amount: number): void {
const payment = this.createPayment(type);
payment.process(amount);
}
}
class DefaultPaymentService extends PaymentService {
protected createPayment(type: string): Payment {
switch (type) {
case "credit": return new CreditCardPayment();
case "paypal": return new PayPalPayment();
default: throw new Error(`Unknown: ${type}`);
}
}
}
Partie 3 : Abstract Factory
3.1 Définition (GoF)
"Fournir une interface pour créer des familles d'objets apparentés ou interdépendants sans spécifier leurs classes concrètes."
3.2 Structure détaillée
Diagramme en cours de génération...
3.3 Exemple : Cross-platform UI (Java)
// Abstract Factory
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// Concrete Factory 1
class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// Concrete Factory 2
class MacOSFactory implements GUIFactory {
public Button createButton() {
return new MacOSButton();
}
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}
// Abstract Products
interface Button {
void paint();
}
interface Checkbox {
void paint();
}
// Concrete Products
class WindowsButton implements Button {
public void paint() {
System.out.println("Windows button");
}
}
class MacOSButton implements Button {
public void paint() {
System.out.println("macOS button");
}
}
// Client
class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void paint() {
button.paint();
checkbox.paint();
}
}
// Usage
public class Main {
public static void main(String[] args) {
GUIFactory factory = new WindowsFactory();
Application app = new Application(factory);
app.paint();
}
}
Partie 4 : Comparaison détaillée
Tableau comparatif
| Critère | Simple Factory | Factory Method | Abstract Factory |
|---|---|---|---|
| Nature | Idiome | Pattern GoF | Pattern GoF |
| Mécanisme | Classe statique | Héritage | Composition |
| Produits | Un seul type | Un seul produit | Famille de produits |
| Extension | Modifier la factory | Nouvelle sous-classe | Nouvelle factory |
| OCP | Violé | Respecté | Respecté |
| Testabilité | Faible | Bonne | Bonne |
| Complexité | Très faible | Moyenne | Élevée |
Quand utiliser quoi ?
Diagramme en cours de génération...
Anti-patterns associés
- Factory Class : créer une factory pour chaque classe, même simple
- Abstract Factory Overkill : utiliser Abstract Factory pour un seul produit
- Static Factory as Service : utiliser Simple Factory pour des objets complexes avec dépendances
Partie 5 : Cas d'usage concrets
5.1 Système de documents
Diagramme en cours de génération...
5.2 Providers (cloud, paiement, auth)
interface CloudProvider {
createCompute(): ComputeService;
createStorage(): StorageService;
createDatabase(): DatabaseService;
}
class AWSProvider implements CloudProvider {
createCompute(): ComputeService { return new EC2(); }
createStorage(): StorageService { return new S3(); }
createDatabase(): DatabaseService { return new RDS(); }
}
class AzureProvider implements CloudProvider {
createCompute(): ComputeService { return new VMs(); }
createStorage(): StorageService { return new BlobStorage(); }
createDatabase(): DatabaseService { return new SQLDatabase(); }
}
5.3 Query Builders
interface QueryBuilder {
select(...fields: string[]): QueryBuilder;
from(table: string): QueryBuilder;
where(condition: string): QueryBuilder;
getQuery(): string;
}
class MySQLQueryBuilder implements QueryBuilder {
private query = "";
select(...fields: string[]): QueryBuilder {
this.query = `SELECT ${fields.join(", ")}`;
return this;
}
from(table: string): QueryBuilder {
this.query += ` FROM ${table}`;
return this;
}
where(condition: string): QueryBuilder {
this.query += ` WHERE ${condition}`;
return this;
}
getQuery(): string {
return this.query + ";";
}
}
class PostgreSQLQueryBuilder implements QueryBuilder {
// Similar with PostgreSQL-specific syntax
}
Résumé
- Simple Factory : rapide, pratique, mais viole OCP
- Factory Method : flexible via héritage, idéal pour frameworks
- Abstract Factory : cohérence entre familles, idéal pour cross-platform
- Le choix dépend du nombre de produits et du besoin d'extension