Aller au contenu principal

Pattern Prototype

Créez de nouveaux objets en copiant un objet existant (le prototype) plutôt qu'en les construisant de zéro.


Le Problème

Vous avez un objet de configuration complexe. Vous avez besoin de variantes, mais le construire de zéro est fastidieux et source d'erreurs.

L'approche naïve :

const baseConfig = {
server: { host: "localhost", port: 3000, ssl: { enabled: true, cert: "..." } },
database: { host: "localhost", pool: { min: 5, max: 20 } },
logging: { level: "info", format: "json" },
};

// Créer une config de test - la copie manuelle est pénible
const testConfig = {
server: { ...baseConfig.server, port: 3001 },
database: { ...baseConfig.database, pool: { ...baseConfig.database.pool } },
logging: { ...baseConfig.logging, level: "debug" },
};
// Oublié de deep-copy ssl ? Maintenant ils partagent le même objet !

Le spread superficiel ne fait pas de deep clone. Les objets imbriqués sont partagés. Les mutations fuient.


La Solution

Clonez le prototype, puis modifiez :

import { deepClone } from "@pithos/core/arkhe";

const baseConfig = {
server: { host: "localhost", port: 3000, ssl: { enabled: true, cert: "..." } },
database: { host: "localhost", pool: { min: 5, max: 20 } },
logging: { level: "info", format: "json" },
};

// Deep clone, puis modifiez en toute sécurité
const testConfig = deepClone(baseConfig);
testConfig.server.port = 3001;
testConfig.logging.level = "debug";

// baseConfig est intact - aucune référence partagée

Une vraie copie profonde. Modifiez librement sans affecter l'original.


Démo

Clonez une config, modifiez des champs dans le clone, observez le diff. Basculez entre "Shallow Copy" et "Deep Clone" — la copie superficielle laisse fuiter les mutations vers l'original sur les objets imbriqués. Le deep clone les garde isolés.

Code
/**
* Prototype pattern: deep clone vs shallow copy.
*
* deepClone from Arkhe creates a true deep copy.
* Spread only copies the top level — nested objects are shared references.
*/

import { deepClone } from "@pithos/core/eidos/prototype/prototype";
import type { AppConfig, ConfigPath, CloneMode, RefCheck } from "./types";

export function cloneConfig(config: AppConfig, mode: CloneMode): AppConfig {
if (mode === "deep") return deepClone(config);
return { ...config };
}

export function getField(config: AppConfig, path: ConfigPath): string | number | boolean {
switch (path) {
case "server.port": return config.server.port;
case "server.ssl.enabled": return config.server.ssl.enabled;
case "database.pool.max": return config.database.pool.max;
case "logging.level": return config.logging.level;
}
}

export function setField(config: AppConfig, path: ConfigPath, value: string | number | boolean): void {
switch (path) {
case "server.port": config.server.port = value as number; break;
case "server.ssl.enabled": config.server.ssl.enabled = value as boolean; break;
case "database.pool.max": config.database.pool.max = value as number; break;
case "logging.level": config.logging.level = value as string; break;
}
}

export function checkReferences(original: AppConfig, clone: AppConfig): RefCheck[] {
return [
{ label: "server", shared: original.server === clone.server },
{ label: "server.ssl", shared: original.server.ssl === clone.server.ssl },
{ label: "database", shared: original.database === clone.database },
{ label: "database.pool", shared: original.database.pool === clone.database.pool },
{ label: "logging", shared: original.logging === clone.logging },
];
}
Result

Analogie

Un modèle de document. Vous n'écrivez pas chaque lettre de zéro — vous copiez un modèle et remplissez les détails. Le modèle est le prototype.


Quand l'Utiliser

Chaque fois que vous avez besoin de variantes d'un objet imbriqué complexe — surcharges de config, fixtures de test, spawn d'entités de jeu, presets de formulaire. Si l'objet a des références imbriquées, deepClone garantit l'isolation. Le spread superficiel non.


Quand NE PAS l'Utiliser

Si votre objet est plat (pas d'imbrication), l'opérateur spread { ...obj } est plus simple et plus rapide. Ne faites pas de deep clone quand une copie superficielle suffit.


API

Ces fonctions viennent d'Arkhe et sont ré-exportées par Eidos :

  • deepClone — Deep clone avec les types courants (objets, tableaux, dates, maps, sets)
  • deepCloneFull — Deep clone incluant les données binaires (TypedArrays, ArrayBuffer, Blob, File)