Menu
Coddy logo textTech

Padrões de Imutabilidade

Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 34 de 70.

A imutabilidade leva o encapsulamento ao seu extremo lógico - uma vez que um objeto é criado, ele nunca pode mudar. Isso elimina categorias inteiras de bugs porque você nunca precisa se preocupar com algo modificando seus dados inesperadamente.

A maneira mais simples de criar uma classe imutável é usar campos readonly com um construtor que define todos os valores e não fornecer nenhum setter:

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

Uma vez que um Point é criado, suas coordenadas não podem mudar. Mas e se você precisar de uma versão modificada? Em vez de alterar o objeto existente, você retorna um novo:

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

O ponto original permanece inalterado enquanto você obtém um novo ponto com valores atualizados. Este padrão é usado em todo o .NET - strings, por exemplo, são imutáveis. Quando você chama ToUpper(), você obtém uma nova string em vez de modificar a original.

Objetos imutáveis são inerentemente seguros para threads, já que nenhum código pode modificá-los após a criação. Eles também são mais fáceis de raciocinar sobre, pois você sabe que seu estado nunca mudará inesperadamente.

challenge icon

Desafio

Fácil

Vamos construir um sistema monetário imutável que demonstra como objetos podem ser projetados para nunca mudarem após a criação. Você criará uma classe Money onde cada operação retorna um novo objeto em vez de modificar o original - um padrão que torna seu código mais seguro e fácil de raciocinar.

Você organizará seu código em dois arquivos:

  • Money.cs: Crie uma classe Money imutável no namespace Finance. Seu dinheiro deve ter:
    • Dois campos readonly: Amount (decimal) e Currency (string)
    • Um construtor que define ambos os valores - uma vez criado, um objeto Money nunca pode mudar
    • Um método Add(decimal value) que retorna um novo objeto Money com o valor aumentado (mesma moeda)
    • Um método Subtract(decimal value) que retorna um novo objeto Money com o valor diminuído (mesma moeda)
    • Um método Convert(string newCurrency, decimal rate) que retorna um novo objeto Money com o valor multiplicado pela taxa e a nova moeda
    • Um método GetDisplay() que retorna uma string no formato "{Amount} {Currency}"
    Lembre-se: nenhum desses métodos deve modificar o objeto original - todos eles retornam instâncias de Money novinhas em folha!
  • Program.cs: No seu arquivo principal, demonstre a imutabilidade em ação. Crie um objeto Money inicial e, em seguida, realize operações nele mantendo referências tanto para o original quanto para os resultados. Isso provará que o original nunca muda, mesmo após múltiplas operações.

Você receberá quatro entradas:

  • Valor inicial (decimal)
  • Moeda inicial (string)
  • Valor para adicionar (decimal)
  • Taxa de conversão para EUR (decimal)

Imprima a saída neste formato:

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

Por exemplo, se as entradas forem 100.00, USD, 50.00, e 0.85, a saída deve ser:

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

Observe como o objeto Money original ainda mostra 100.00 USD mesmo após chamar Add() - isso é imutabilidade! O método Add() criou um objeto completamente novo com 150.00 USD, deixando o original intocado. A conversão é realizada no resultado da operação de adição.

Folha de consulta

Imutabilidade significa que, uma vez que um objeto é criado, ele nunca pode mudar. Isso elimina bugs relacionados a modificações inesperadas de dados.

Crie uma classe imutável usando campos readonly com um construtor e sem setters:

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

Para modificar um objeto imutável, retorne uma nova instância em vez de alterar a existente:

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

Benefícios de objetos imutáveis:

  • Seguros para threads (thread-safe) por design, já que nenhum código pode modificá-los após a criação
  • Mais fáceis de raciocinar sobre, porque seu estado nunca muda inesperadamente
  • Usados em todo o .NET (por exemplo, strings são imutáveis)

Experimente você mesmo

using System;
using Finance;

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

        // TODO: Criar um objeto Money inicial com o valor e a moeda iniciais

        // TODO: Imprimir o dinheiro original usando GetDisplay()

        // TODO: Chamar Add() no original e armazenar o resultado em uma nova variável

        // TODO: Imprimir o resultado após a adição usando GetDisplay()

        // TODO: Imprimir o original novamente para provar que ele não foi alterado

        // TODO: Converter o resultado somado para EUR usando a taxa de conversão

        // TODO: Imprimir o dinheiro convertido usando GetDisplay()
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos