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)