POO: Encapsulamento e Polimorfismo

Fala Brab#s! Essa é a segunda parte do post sobre programação orientada a objetos em C#, vamos falar sobre encapsulamento e polimorfismo, que são dois conceitos fundamentais na orientação a objetos. Entretanto, se você ainda não viu o primeiro post, corre lá e dar uma conferida: Programação Orientada a Objetos com C#: Abstração e Herança.

Encapsulamento

O encapsulamento está relacionado à segurança da aplicação. Ele consiste em controlar o acesso às propriedades e métodos de um objeto, definindo quem pode visualizá-los ou alterá-los. Isso é feito utilizando modificadores de acesso, como public e private, que restringem a visibilidade de atributos e métodos. Vamos entender o encapsulamento com um exemplo prático.

Exemplo prático: Sistema de Conta Bancária

No post anterior, criamos uma classe Conta para um sistema bancário, com atributos como proprietario e saldo. Todos esses atributos estavam públicos, permitindo que fossem alterados de qualquer parte do código, o que pode comprometer a segurança e consistência dos dados.

Ao aplicar o encapsulamento, podemos tornar esses atributos privados e criar métodos para controlar como os dados são acessados e modificados. Por exemplo, definimos o atributo saldo como privado e criamos métodos públicos para realizar depósitos e saques, garantindo que essas operações respeitem regras de negócio.

 public class Conta
 {
     public string NumeroConta { get; set; }
     public string Proprietario { get; set; }
     public decimal Saldo { get; private set; } // Definindo a propriedade Saldo como privada para adicionar valores

     public Conta(string proprietario, decimal saldo)
     {
         Proprietario = proprietario;
         Saldo = saldo;
     }

     // Método para realizar depósito

     public void Deposito(decimal valor) 
     {
         Saldo += valor; 
     }

     // Método para realizar saque
     public void Saque(decimal valor) 
     { 
         if (valor > Saldo) 
         { 
             throw new Exception("Saldo insuficiente"); 
         }
         Saldo -= valor; 
     }
 }

Dessa forma, o saldo da conta só pode ser alterado através dos métodos de Deposito e Saque, o que garante que o saldo nunca seja alterado diretamente de forma incorreta.

Vamos testar esse código, incluindo os métodos dentro do nosso arquivo Program.cs

class Program
{
    static void Main(string[] args)
    {
        var conta = new Conta("Filipe Brito", 100m);
        
        Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo.");

        conta.Deposito(2000);

        Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo.");

        conta.Saque(200);

        Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo.");

        conta.Saque(3000);

    }
}

Após a execução ele apresentará os seguintes resultados:

Filipe Brito tem 100 de saldo.
Filipe Brito tem 2100 de saldo.
Filipe Brito tem 1900 de saldo.
Unhandled exception. System.Exception: Saldo insuficiente

Perceba que os métodos de Deposito e Saque funcionarão corretamente, bem como nossa validação de “Saldo insuficiente”. Também podemos tratar esse erro para que o nosso código fique com um retorno melhor, dessa forma:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var conta = new Conta("Filipe Brito", 100m);

            Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo."); // Filipe Brito tem 100 de saldo.

            conta.Deposito(2000);

            Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo."); // Filipe Brito tem 2100 de saldo.

            conta.Saque(200);

            Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo."); // Filipe Brito tem 1900 de saldo.

            conta.Saque(3000);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message); // Saldo insuficiente
        }
    }
}

Polimorfismo

Agora, vamos falar sobre polimorfismo, que é um conceito ligado à capacidade de um método assumir diferentes formas. Isso ocorre principalmente quando uma classe herda outra, mas precisa modificar o comportamento de algum método herdado.

Exemplo prático: Herança e Sobrescrita de Métodos

Vamos adicionar uma classe Poupanca que herda da classe Conta. No caso da Poupanca, queremos que, ao realizar um depósito, o saldo receba um rendimento extra automaticamente. Isso exemplifica o polimorfismo, pois o método de depósito será diferente na classe Poupanca, mesmo que ela herde o método da classe Conta.

Para fazermos essa implementação primeiro precisamos adaptar nossa classe Contapara que o método de depósito seja do tipo virtual, assim conseguimos sobrescrever esse método em qualquer classe que herde a classe Conta.

 public class Conta
 {
     public string NumeroConta { get; set; }
     public string Proprietario { get; set; }
     public decimal Saldo { get; private set; }

     public Conta(string proprietario, decimal saldo)
     {
         Proprietario = proprietario;
         Saldo = saldo;
     }

     // Método para realizar depósito

     public virtual void Deposito(decimal valor) 
     {
         Saldo += valor; 
     }

     // Método para realizar saque
     public void Saque(decimal valor) 
     { 
         if (valor > Saldo) 
         { 
             throw new Exception("Saldo insuficiente"); 
         }
         Saldo -= valor; 
     }
 }

Agora só precisamos criar a nossa classe Poupanca:

  public class Poupanca : Conta
  {
      public Poupanca(string proprietario, decimal saldo) : base(proprietario, saldo)
      {
      }

      public override void Deposito(decimal valor)
      {
          base.Deposito(valor + 5); // Adiciona 5 BRL de rendimento
      }
  }

No exemplo acima, o método Deposito na classe Poupanca utiliza o conceito de sobrescrita (override), o que permite modificar o comportamento do método herdado de Conta.

Quando fazemos um depósito na conta poupança, além de adicionar o valor depositado, ele acrescenta automaticamente 5 BRL de rendimento, criando um comportamento diferenciado em relação à classe Conta.

Para testar essa nova classe, basta chamar a classe Poupança ao invés da Conta.

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var conta = new Poupanca("Filipe Brito", 100m); // Incluir nova classe

            Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo."); // Filipe Brito tem 100 de saldo.

            conta.Deposito(2000);

            Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo."); // Filipe Brito tem 2105 de saldo.

            conta.Saque(200);

            Console.WriteLine($"{conta.Proprietario} tem {conta.Saldo} de saldo."); // Filipe Brito tem 1905 de saldo.

            conta.Saque(3000);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message); // Saldo insuficiente
        }
    }
}

Conclusão

Neste post, vimos dois conceitos importantes da Programação Orientada a Objetos:

  • Encapsulamento: Como esconder os atributos de um objeto e oferecer métodos para manipulá-los de forma controlada.
  • Polimorfismo: A habilidade de sobrescrever métodos de uma classe base, permitindo comportamentos específicos em classes derivadas.

Esses conceitos são fundamentais para garantir a segurança, flexibilidade e manutenção do código em sistemas orientados a objetos.

Não perca tempo, se você quiser acompanhar o tutorial completo, acesse o vídeo e inscreva-se no canal do BraboDev.

“Desde o princípio, a Palavra estava com Deus. Por meio da Palavra, Deus fez todas as coisas, e nada do que existe foi feito sem ela”

João 1:2

Rolar para cima