Patterns creationnels
Singleton
class Singleton {
constructor() {
if (!Singleton.instance) {
Singleton.instance = this;
}
return Singleton.instance;
}
doSomething() {}
}
const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // true
Utilisez lorsque vous avez besoin d'une seule occurrence d'un objet pour coordonner actions globales.
Factory Method
class Creator {
factoryMethod() {}
}
class ConcreteCreator extends Creator {
factoryMethod() {
return new Product();
}
}
class Product {}
const creator = new ConcreteCreator();
const product = creator.factoryMethod();
Utilisez pour créer des objets sans spécifier la classe exacte de l'objet à créer.
Abstract Factory
class CreatorA {
factoryMethod() {}
}
class CreatorB {
factoryMethod() {}
}
class ProductA {}
class ProductB {}
const creatorA = new CreatorA();
const productA = creatorA.factoryMethod(); // instanceof ProductA
const creatorB = new CreatorB();
const productB = creatorB.factoryMethod(); // instanceof ProductB
Utilisez pour créer familles de objets liés ou dépendants entre eux sans spécifier leurs classes concrètes.
Patterns structurels
Adapter
class Target {
request() {}
}
class Adaptee {
specificRequest() {}
}
class Adapter extends Target {
constructor(adaptee) {
super();
this.adaptee = adaptee;
}
request() {
return this.adaptee.specificRequest().split('').reverse().join('');
}
}
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
adapter.request(); // 'olleh'
Utilisez lorsque vous avez besoin de utiliser une interface existante mais avec une API différente.
Decorator
class Component {
operation() {}
}
class ConcreteComponent extends Component {
operation() {
return 'ConcreteComponent';
}
}
class Decorator extends Component {
constructor(component) {
super();
this.component = component;
}
operation() {
return `Decorator(${this.component.operation()})`;
}
}
const concreteComponent = new ConcreteComponent();
const decorator = new Decorator(concreteComponent);
decorator.operation(); // 'Decorator(ConcreteComponent)'
Utilisez pour ajouter des responsabilités à un objet dynamiquement sans affecter les objets existants.
Proxy
class Subject {
request() {}
}
class RealSubject extends Subject {
request() {
return 'RealSubject';
}
}
class Proxy extends Subject {
constructor(realSubject) {
super();
this.realSubject = realSubject;
}
request() {
if (this.checkAccess()) {
const result = this.realSubject.request();
this.logAccess();
return result;
} else {
throw new Error('Access denied');
}
}
checkAccess() {
// Check access logic
return true;
}
logAccess() {
console.log('Access logged');
}
}
const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);
proxy.request(); // 'RealSubject'
Utilisez pour contrôler l'accès à un objet.
Patterns comportementaux
Observer
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
class Observer {
update(data) {}
}
const subject = new Subject();
const observer1 = { update: data => console.log('Observer1:', data) };
const observer2 = { update: data => console.log('Observer2:', data) };
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify('Hello'); // 'Observer1: Hello', 'Observer2: Hello'
Utilisez pour mettre en place un système de publication/abonnement.
Strategy
class Context {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
execute() {
return this.strategy.execute();
}
}
class StrategyA {
execute() {
return 'Strategy A';
}
}
class StrategyB {
execute() {
return 'Strategy B';
}
}
const context = new Context(new StrategyA());
console.log(context.execute()); // 'Strategy A'
context.setStrategy(new StrategyB());
console.log(context.execute()); // 'Strategy B'
Utilisez pour définir une famille de algorithmes, encapsuler chaque un et les rendre interchangeables.
Command
class Receiver {
action() {}
}
class Command {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.action();
}
}
class Invoker {
constructor(command) {
this.command = command;
}
runCommand() {
this.command.execute();
}
}
const receiver = new Receiver();
const command = new Command(receiver);
const invoker = new Invoker(command);
invoker.runCommand(); // 'Action performed'
Utilisez pour encapsuler une requête en un objet, permettant de paramétrer les clients avec différents requêtes, et séparer l'objet d'un exécuteur de la requête.
State
class Context {
constructor() {
this.state = null;
}
setState(state) {
this.state = state;
this.state.context = this;
}
request() {
if (this.state) {
return this.state.handle();
}
}
}
class StateA {
handle() {
console.log('State A handling');
}
}
class StateB {
handle() {
console.log('State B handling');
}
}
const context = new Context();
context.setState(new StateA());
context.request(); // 'State A handling'
context.setState(new StateB());
context.request(); // 'State B handling'
Utilisez pour permettre à un objet de changer son comportement en fonction de son état interne.