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 el encapsulamiento a su extremo lógico: una vez que se crea un objeto, 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 ningún setter:

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, ya que ningún código puede modificarlos después de su creación. También es más fácil razonar sobre ellos 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 se pueden diseñar objetos para que nunca cambien después de su creación. Crearás una clase Money donde 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 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 mientras mantienes 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 sumar (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.

Hoja de referencia

La inmutabilidad significa que una vez que se crea un objeto, este nunca puede cambiar. Esto elimina errores relacionados con modificaciones de datos inesperadas.

Cree una clase inmutable utilizando campos readonly con un constructor y sin setters:

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

Para modificar un objeto inmutable, devuelva una nueva instancia en lugar de cambiar la existente:

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

Beneficios de los objetos inmutables:

  • Seguros para hilos (thread-safe) por diseño, ya que ningún código puede modificarlos después de su creación
  • Más fáciles de razonar porque su estado nunca cambia inesperadamente
  • Utilizados en todo .NET (por ejemplo, las cadenas son inmutables)

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: Crear un objeto Money inicial con el monto y la moneda iniciales

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

        // TODO: Llamar a Add() en el original y almacenar el resultado en una nueva variable

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

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

        // TODO: Convertir el resultado sumado a EUR usando la tasa de conversión

        // TODO: Imprimir 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