Aller au contenu principal

Eidos

εἶδος - "forme, pattern"

Les 23 design patterns du Gang of Four, réimaginés pour TypeScript fonctionnel. Pas de classes, pas d'héritage, juste des fonctions, des unions discriminées et de la composition.

info

Eidos traduit les patterns OOP classiques en code fonctionnel idiomatique. Certains patterns deviennent de simples fonctions. D'autres deviennent des alias de types. Certains sont marqués deprecated car le système de types de TypeScript les rend inutiles. Chaque module documente la version OOP, explique l'équivalent fonctionnel et fournit du code fonctionnel.


Philosophie

Les patterns GoF ont été conçus pour des langages sans fonctions first-class, sans types algébriques, et sans inférence de types puissante. En TypeScript fonctionnel :

  • Strategy est juste un Record<string, Function>
  • Visitor est juste un switch sur une union discriminée
  • Factory Method est juste de l'injection de dépendances
  • Decorator est juste de la composition de fonctions

Eidos ne force pas les patterns OOP dans du code fonctionnel. Il montre la façon idiomatique de résoudre les mêmes problèmes. Quand un pattern est absorbé par le langage, on le documente avec une fonction @deprecated qui explique quoi faire à la place, le tout directement accessible depuis son IDE.


Catégories de Patterns

Patterns avec Vraie Valeur Ajoutée

Ces patterns fournissent des fonctions utilitaires que vous pourrez utiliser dans votre code :

PatternFonctionsDescription
StrategycreateStrategies, safeStrategy, withFallback, withValidationAlgorithmes interchangeables avec lookup typé
ObservercreateObservablePub/sub avec événements typés
Decoratordecorate, before, after, aroundWrapping et composition de fonctions
Adapteradapt, createAdapterTransformation de signatures de fonctions
Commandundoable, createCommandStack, undoableState, createReactiveCommandStackUndo/redo avec historique d'actions
Chain of ResponsibilitycreateChain, safeChainPipelines de handlers séquentiels
StatecreateMachineMachines à états finis
IteratorcreateIterable, lazyRange, iterateSéquences lazy avec opérateurs
Compositeleaf, branch, fold, map, flatten, findStructures arborescentes avec traversée
Abstract FactorycreateAbstractFactoryFamilles de factories avec lookup typé
MediatorcreateMediatorHub d'événements typé pour communication découplée
MementocreateHistorySnapshots d'état avec undo/redo
BuildercreateBuilder, createValidatedBuilderBuilders fluent immutables
Template MethodtemplateWithDefaultsSquelettes d'algorithmes avec étapes surchargeables

Patterns Couverts par Arkhe

Ces patterns sont déjà implémentés dans Arkhe. Eidos les ré-exporte pour la découvrabilité :

PatternFonctions ArkheDescription
Proxymemoize, once, throttle, debounce, lazy, guardedInterception et cache de fonctions
PrototypedeepClone, deepCloneFullClonage d'objets
Singletononce (alias singleton)Création d'instance unique
FlyweightmemoizePooling d'objets via cache

Patterns Absorbés par le Langage

Ces patterns sont inutiles en TypeScript fonctionnel. Les fonctions existent uniquement pour la documentation et sont marquées @deprecated pour guider vers du code idiomatique :

PatternPourquoi Absorbé
BridgeUn bridge est juste (impl) => abstraction, une fonction
Factory MethodPassez la factory en paramètre (injection de dépendances)
FacadeUne facade est juste une fonction qui appelle d'autres fonctions
VisitorUtilisez switch sur une union discriminée
InterpreterUnions discriminées + évaluation récursive

Exemples Rapides

Strategy : Algorithmes Interchangeables

import { createStrategies } from "@pithos/core/eidos/strategy/strategy";

const pricing = createStrategies({
regular: (price: number) => price,
vip: (price: number) => price * 0.8,
premium: (price: number) => price * 0.7,
});

pricing.execute("vip", 100); // 80

State : Machine à États Finis

import { createMachine } from "@pithos/core/eidos/state/state";

const light = createMachine({
green: { timer: "yellow" },
yellow: { timer: "red" },
red: { timer: "green" },
}, "green");

light.send("timer"); // "yellow"
light.send("timer"); // "red"

Composite : Structures Arborescentes

import { leaf, branch, fold } from "@pithos/core/eidos/composite/composite";

const tree = branch({ name: "root", size: 0 }, [
leaf({ name: "file1.txt", size: 100 }),
branch({ name: "docs", size: 0 }, [
leaf({ name: "readme.md", size: 50 }),
]),
]);

const totalSize = fold(tree, {
leaf: (data) => data.size,
branch: (_, children) => children.reduce((a, b) => a + b, 0),
}); // 150

Mediator : Communication Découplée

import { createMediator } from "@pithos/core/eidos/mediator/mediator";

type Events = {
userLoggedIn: { userId: string };
orderPlaced: { orderId: string };
};

const mediator = createMediator<Events>();

mediator.on("userLoggedIn", ({ userId }) => {
console.log(`Welcome ${userId}`);
});

mediator.emit("userLoggedIn", { userId: "alice" });

Builder : Construction Fluent

import { createBuilder } from "@pithos/core/eidos/builder/builder";

const queryBuilder = createBuilder({ table: "", where: [] as string[], limit: 100 })
.step("from", (s, table: string) => ({ ...s, table }))
.step("where", (s, clause: string) => ({ ...s, where: [...s.where, clause] }))
.step("limit", (s, n: number) => ({ ...s, limit: n }))
.done();

const query = queryBuilder()
.from("users")
.where("active = true")
.limit(10)
.build();

Memento vs Command pour Undo/Redo

Les deux patterns supportent undo/redo, mais fonctionnent différemment :

ApprocheModuleFonctionnementQuand l'utiliser
Snapshots d'étatMemento (createHistory)Stocke des copies de l'état, restaure sur undoL'état est peu coûteux à copier
Historique d'actionsCommand (createCommandStack)Stocke des paires execute/undoVous avez des opérations réversibles
// Memento : stocker des états
const history = createHistory({ count: 0 });
history.push({ count: 1 });
history.undo(); // restaure { count: 0 }

// Command : stocker des actions
const stack = createCommandStack();
stack.execute(undoable(() => count++, () => count--));
stack.undo(); // appelle la fonction undo

Quand Utiliser

Eidos est utile quand :

  • Vous venez de l'OOP et voulez comprendre les équivalents fonctionnels
  • Vous avez besoin d'un pattern spécifique (machine à états, builder, observer, etc.)
  • Vous voulez des implémentations typées et testées plutôt que de les écrire vous-même

Quand NE PAS Utiliser

BesoinUtilisez Plutôt
Transformation de donnéesArkhe
Gestion d'erreursZygos
Validation de schémasKanon

Ressources Liées