Menu
Coddy logo textTech

Patterns d'immuabilité

Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 34 sur 70.

L'immuabilité pousse l'encapsulation à son extrême logique : une fois qu'un objet est créé, il ne peut plus jamais changer. Cela élimine des catégories entières de bogues, car vous n'avez jamais à vous soucier de voir quelque chose modifier vos données de manière inattendue.

La manière la plus simple de créer une classe immuable est d'utiliser des champs readonly avec un constructeur qui définit toutes les valeurs, et de ne fournir aucun setter :

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Une fois qu'un Point est créé, ses coordonnées ne peuvent pas changer. Mais que se passe-t-il si vous avez besoin d'une version modifiée ? Au lieu de modifier l'objet existant, vous en retournez un nouveau :

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

Le point d'origine reste inchangé tandis que vous obtenez un nouveau point avec des valeurs mises à jour. Ce modèle est utilisé partout dans .NET - les chaînes de caractères, par exemple, sont immuables. Lorsque vous appelez ToUpper(), vous obtenez une nouvelle chaîne plutôt que de modifier l'originale.

Les objets immuables sont intrinsèquement sûrs pour les threads, car aucun code ne peut les modifier après leur création. Ils sont également plus faciles à appréhender, car vous savez que leur état ne changera jamais de manière inattendue.

challenge icon

Défi

Facile

Construisons un système monétaire immuable qui démontre comment les objets peuvent être conçus pour ne jamais changer après leur création. Vous allez créer une classe Money où chaque opération retourne un nouvel objet au lieu de modifier l'original - un modèle qui rend votre code plus sûr et plus facile à raisonner.

Vous organiserez votre code sur deux fichiers :

  • Money.cs : Créez une classe Money immuable dans l'espace de noms Finance. Votre argent doit avoir :
    • Deux champs readonly : Amount (decimal) et Currency (string)
    • Un constructeur qui définit les deux valeurs - une fois créé, un objet Money ne peut jamais changer
    • Une méthode Add(decimal value) qui retourne un nouvel objet Money avec le montant augmenté (même devise)
    • Une méthode Subtract(decimal value) qui retourne un nouvel objet Money avec le montant diminué (même devise)
    • Une méthode Convert(string newCurrency, decimal rate) qui retourne un nouvel objet Money avec le montant multiplié par le taux et la nouvelle devise
    • Une méthode GetDisplay() qui retourne une chaîne de caractères au format "{Amount} {Currency}"
    Rappelez-vous : aucune de ces méthodes ne doit modifier l'objet original - elles retournent toutes de toutes nouvelles instances de Money !
  • Program.cs : Dans votre fichier principal, démontrez l'immuabilité en action. Créez un objet Money initial, puis effectuez des opérations sur celui-ci tout en conservant des références à l'original et aux résultats. Cela prouvera que l'original ne change jamais, même après plusieurs opérations.

Vous recevrez quatre entrées :

  • Montant initial (decimal)
  • Devise initiale (string)
  • Montant à ajouter (decimal)
  • Taux de conversion vers EUR (decimal)

Affichez la sortie dans ce format :

Original: {GetDisplay()}
After add: {GetDisplay()}
Original unchanged: {GetDisplay()}
Converted: {GetDisplay()}

Par exemple, si les entrées sont 100.00, USD, 50.00, et 0.85, la sortie devrait être :

Original: 100.00 USD
After add: 150.00 USD
Original unchanged: 100.00 USD
Converted: 127.50 EUR

Remarquez comment l'objet Money original affiche toujours 100.00 USD même après l'appel de Add() - c'est l'immuabilité ! La méthode Add() a créé un objet complètement nouveau avec 150.00 USD, laissant l'original intact. La conversion est effectuée sur le résultat de l'opération d'ajout.

Aide-mémoire

L'immuabilité signifie qu'une fois qu'un objet est créé, il ne peut plus jamais changer. Cela élimine les bogues liés aux modifications de données inattendues.

Créez une classe immuable en utilisant des champs readonly avec un constructeur et sans setters :

public class Point
{
    public readonly int X;
    public readonly int Y;
    
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Pour modifier un objet immuable, retournez une nouvelle instance au lieu de modifier l'existante :

public Point Move(int deltaX, int deltaY)
{
    return new Point(X + deltaX, Y + deltaY);
}

Avantages des objets immuables :

  • Sûrs pour les threads (thread-safe) par conception, car aucun code ne peut les modifier après leur création
  • Plus faciles à raisonner car leur état ne change jamais de manière inattendue
  • Utilisés partout dans .NET (par exemple, les chaînes de caractères sont immuables)

Essayez vous-même

using System;
using Finance;

class Program
{
    public static void Main(string[] args)
    {
        // Lire les entrées
        decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
        string initialCurrency = Console.ReadLine();
        decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
        decimal conversionRate = Convert.ToDecimal(Console.ReadLine());

        // TODO: Créer un objet Money initial avec le montant et la devise initiaux

        // TODO: Afficher l'argent d'origine en utilisant GetDisplay()

        // TODO: Appeler Add() sur l'original et stocker le résultat dans une nouvelle variable

        // TODO: Afficher le résultat après l'ajout en utilisant GetDisplay()

        // TODO: Afficher à nouveau l'original pour prouver qu'il est inchangé

        // TODO: Convertir le résultat ajouté en EUR en utilisant le taux de conversion

        // TODO: Afficher l'argent converti en utilisant GetDisplay()
    }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet