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.
Défi
FacileConstruisons 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 classeMoneyimmuable dans l'espace de nomsFinance. Votre argent doit avoir :- Deux champs
readonly:Amount(decimal) etCurrency(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 objetMoneyavec le montant augmenté (même devise) - Une méthode
Subtract(decimal value)qui retourne un nouvel objetMoneyavec le montant diminué (même devise) - Une méthode
Convert(string newCurrency, decimal rate)qui retourne un nouvel objetMoneyavec 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}"
Money!- Deux champs
Program.cs: Dans votre fichier principal, démontrez l'immuabilité en action. Créez un objetMoneyinitial, 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 EURRemarquez 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()
}
}
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
1Fondamentaux de la POO
Fichiers externesEspaces de noms et directivesIntro aux classes et objetsLe mot-clé 'this'Méthodes et paramètresChamps vs PropriétésConstructeursInitialiseurs d'objetsRécapitulatif - Calculatrice simple4Héritage
Syntaxe de base de l'héritage (:)Le mot-clé 'base'Mots-clés Virtual & OverrideClasses scelléesLa classe de base 'object'Récapitulatif - Hiérarchie des employés7Fonctionnalités avancées
Surcharge d'opérateursIndexeurs (this[])Redéfinition de ToString()Méthodes d'extensionRécapitulatif - Liste personnalisée10Patrons de conception - Partie 1
Introduction aux patrons de conceptionSingleton Thread-SafePatron FabriquePatron Observateur (Événements)Patron Stratégie2Propriétés et membres statiques
Propriétés auto-implémentéesPropriétés en lecture/écriture seuleChamps et méthodes statiquesClasses statiquesMembres à corps d'expression5Polymorphisme & Interfaces
Polymorphisme : Compilation vs ExécutionInterface vs Classe AbstraiteInterfaces MultiplesInterfaces ExplicitesUpcasting & DowncastingRécapitulatif - Calculateur de Formes8Concepts avancés de la POO
Composition plutôt qu'héritageGénériques (Classes et Méthodes)Délégués et ÉvénementsAttributs et RéflexionIDisposable et l'instruction usingBases de l'Injection de Dépendances11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Architecture des classes
Données d'instance vs statiquesMots-clés 'readonly' & 'const'Champs de supportRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Modificateurs d'accèsPropriétés pour l'encapsulationImplémentation du masquage de donnéesPatterns d'immuabilitéRécapitulatif - Dossiers d'étudiants12Projet : Système de gestion de bibliothèque
Structure du projetModèles Livre et Utilisateur