Menu
Coddy logo textTech

Patrones de inmutabilidad

Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy. Lección 34 de 70.

La inmutabilidad lleva la encapsulación a su extremo lógico: una vez que se crea un objeto, este nunca puede cambiar. Esto elimina categorías enteras de errores porque nunca tienes que preocuparte de que algo modifique tus datos de forma inesperada.

La forma más sencilla de crear una clase inmutable es usar campos readonly con un constructor que establezca todos los valores y no proporcionar setters:

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

Una vez que se crea un Point, sus coordenadas no pueden cambiar. ¿Pero qué pasa si necesitas una versión modificada? En lugar de cambiar el objeto existente, devuelves uno nuevo:

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

El punto original permanece sin cambios mientras obtienes un nuevo punto con valores actualizados. Este patrón se utiliza en todo .NET; las cadenas, por ejemplo, son inmutables. Cuando llamas a ToUpper(), obtienes una nueva cadena en lugar de modificar la original.

Los objetos inmutables son intrínsecamente seguros para subprocesos (thread-safe), ya que ningún código puede modificarlos después de su creación. También son más fáciles de razonar porque sabes que su estado nunca cambiará de forma inesperada.

challenge icon

Desafío

Fácil

Construyamos un sistema de dinero inmutable que demuestre cómo se pueden diseñar objetos para que nunca cambien después de su creación. Crearás una clase Money en la que cada operación devuelve un objeto nuevo en lugar de modificar el original, un patrón que hace que tu código sea más seguro y fácil de razonar.

Organizarás tu código en dos archivos:

  • Money.cs: Crea una clase inmutable Money en el espacio de nombres Finance. Tu sistema monetario debe tener:
    • Dos campos readonly: Amount (decimal) y Currency (string)
    • Un constructor que establezca ambos valores; una vez creado, un objeto Money nunca puede cambiar
    • Un método Add(decimal value) que devuelva un objeto Money nuevo con el importe aumentado (la misma moneda)
    • Un método Subtract(decimal value) que devuelva un objeto Money nuevo con el importe reducido (la misma moneda)
    • Un método Convert(string newCurrency, decimal rate) que devuelva un objeto Money nuevo con el importe multiplicado por la tasa y la nueva moneda
    • Un método GetDisplay() que devuelva una cadena con el formato "{Amount} {Currency}"
    Recuerda: ninguno de estos métodos debe modificar el objeto original; ¡todos devuelven instancias de Money completamente nuevas!
  • Program.cs: En tu archivo principal, demuestra la inmutabilidad en acción. Crea un objeto Money inicial y después realiza operaciones sobre él mientras mantienes referencias tanto al original como a los resultados. Esto demostrará que el original nunca cambia, incluso después de varias operaciones.

Recibirás cuatro entradas:

  • Importe inicial (decimal)
  • Moneda inicial (string)
  • Importe que se sumará (decimal)
  • Tasa de conversión a EUR (decimal)

Imprime la salida con este formato:

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

Por ejemplo, si las entradas son 100.00, USD, 50.00 y 0.85, la salida debería ser:

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

Observa cómo el objeto Money original sigue mostrando 100.00 USD incluso después de llamar a Add(); ¡eso es la inmutabilidad! El método Add() creó un objeto completamente nuevo con 150.00 USD, dejando intacto el original. La conversión se realiza sobre el resultado de la operación de suma.

Pruébalo tú mismo

using System;
using Finance;

class Program
{
    public static void Main(string[] args)
    {
        // Leer entradas
        decimal initialAmount = Convert.ToDecimal(Console.ReadLine());
        string initialCurrency = Console.ReadLine();
        decimal amountToAdd = Convert.ToDecimal(Console.ReadLine());
        decimal conversionRate = Convert.ToDecimal(Console.ReadLine());

        // TODO: Create un objeto Money inicial con el monto y la moneda iniciales

        // TODO: Imprime el dinero original usando GetDisplay()

        // TODO: Llama a Add() en el original y almacena el resultado en una nueva variable

        // TODO: Imprime el resultado después de sumar usando GetDisplay()

        // TODO: Imprime el original de nuevo para demostrar que no ha cambiado

        // TODO: Convierte el resultado sumado a EUR usando el conversion rate

        // TODO: Imprime el dinero convertido usando GetDisplay()
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de C# online