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 proporcione 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

Vamos a construir un sistema de dinero inmutable que demuestre cómo los objetos pueden ser diseñados para nunca cambiar después de su creación. Crearás una clase Money donde cada operación devuelve un nuevo objeto 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 Money inmutable en el espacio de nombres Finance. Tu dinero debe tener:
    • Dos campos readonly: Amount (decimal) y Currency (string)
    • Un constructor que establece ambos valores: una vez creado, un objeto Money nunca puede cambiar
    • Un método Add(decimal value) que devuelve un nuevo objeto Money con el monto incrementado (misma moneda)
    • Un método Subtract(decimal value) que devuelve un nuevo objeto Money con el monto disminuido (misma moneda)
    • Un método Convert(string newCurrency, decimal rate) que devuelve un nuevo objeto Money con el monto multiplicado por la tasa y la nueva moneda
    • Un método GetDisplay() que devuelve una cadena en 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, luego realiza operaciones sobre él manteniendo referencias tanto al original como a los resultados. Esto demostrará que el original nunca cambia incluso después de múltiples operaciones.

Recibirás cuatro entradas:

  • Monto inicial (decimal)
  • Moneda inicial (string)
  • Monto a añadir (decimal)
  • Tasa de conversión a EUR (decimal)

Imprime la salida en 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 todavía muestra 100.00 USD incluso después de llamar a Add(): ¡eso es inmutabilidad! El método Add() creó un objeto completamente nuevo con 150.00 USD, dejando el original intacto. 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