Aniversário
Alura 12 anos

20% OFF

Falta pouco!

00

DIAS

00

HORAS

00

MIN

00

SEG

Alura > Cursos de Programação > Cursos de .NET > Conteúdos de .NET > Primeiras aulas do curso Praticando C#: orientação a objetos com polimorfismo

Praticando C#: orientação a objetos com polimorfismo

Polimorfismo de sobrecarga e sobrescrita - Apresentação

Apresentação e Audiodescrição

Olá! Meu nome é Yasmin Araújo e faço parte do time da Escola de Programação.

Audiodescrição: Yasmin é uma mulher branca, com cabelo castanho escuro na altura dos ombros. Ela veste uma blusa rosa e, ao fundo, há uma parede iluminada com luz azul.

Introdução ao Curso de Polimorfismo

Neste curso, nós vamos trabalhar com polimorfismo, que é o último pilar da orientação a objetos.

Estrutura do Curso

Para começar, vamos explorar o conceito de polimorfismo, tanto pela definição quanto pela prática, e como podemos utilizá-lo em C#. Em seguida, trabalharemos com os diferentes tipos de polimorfismo. Vamos analisar o polimorfismo de sobrecarga, o polimorfismo de sobrescrita e, por fim, o polimorfismo de subtipos.

Conclusão e Motivação

Aqui, temos uma variedade de termos técnicos, mas, ao longo das nossas práticas, veremos que todos esses conceitos são bastante fáceis de aplicar no dia a dia. Portanto, não há motivo para preocupação. Vamos começar?

Polimorfismo de sobrecarga e sobrescrita - Polimorfismo de sobrecarga

Introdução ao Polimorfismo

Para compreendermos o polimorfismo, precisamos primeiro entender o significado dessa palavra. Podemos dividir "polimorfismo" em duas partes: "poli" e "morfismo". "Poli" significa muitos, e "morfismo" está relacionado a formas. Note que temos a mesma grafia, mas a palavra está um pouco alterada. Ao juntarmos as duas partes, obtemos que polimorfismo significa "muitas formas".

Aplicação do Polimorfismo no Código

Como assim, muitas formas? No mundo real, existem várias situações em que podemos realizar uma determinada ação de maneiras diferentes. O polimorfismo nos ajuda a representar essas diferentes formas em nosso código. Mas como isso funciona? Vamos exemplificar no nosso contexto.

Na classe Produto, estamos trabalhando com o desconto no preço desse produto. Utilizamos a porcentagem, ou seja, passamos uma porcentagem como parâmetro do método, e o preço é multiplicado por essa porcentagem para obter um desconto final.

Preco = Preco * (1 - desconto/100);

No entanto, existe outra forma de aplicar um desconto, que é passando um valor fixo a ser subtraído do preço. Por exemplo, podemos ter R$10 de desconto no produto, realizando a subtração para obter o resultado final. Assim, temos duas formas de fazer a mesma coisa: aplicar o desconto. Queremos representar essa situação no nosso código. Como fazemos isso? Utilizando o polimorfismo.

Implementação do Polimorfismo de Sobrecarga

Para implementar isso, é bastante simples. Criamos um novo método com o mesmo nome, mas com parâmetros diferentes. No Visual Studio, criamos um public void chamado AlterarPrecoComDesconto, passando um valor inteiro, pois geralmente não utilizamos valores fracionados para fornecer esse desconto.

public void AlterarPrecoComDesconto(int desconto)
{
    Preco = Preco - desconto;
}

Observe que o compilador não apresenta problemas, pois estamos realizando ações diferentes, mesmo que tenham o mesmo nome. A ideia é que o método tenha o mesmo nome dentro da mesma classe, mas com parâmetros diferentes. No primeiro caso, usamos um double, e no segundo, um int. Se tivéssemos, por exemplo, dois métodos com double desconto, o compilador reclamaria, pois não conseguiria diferenciar. Se tivéssemos uma quantidade diferente de parâmetros, ele conseguiria fazer essa distinção. Vamos ver um exemplo disso também.

Portanto, a ideia é ter o mesmo método, com o mesmo nome, mas com parâmetros diferentes dentro da mesma classe. Quando trabalhamos com polimorfismo dessa forma, estamos lidando com polimorfismo de sobrecarga.

Polimorfismo de Sobrecarga em Construtores

O polimorfismo de sobrecarga é amplamente aplicado em construtores, pois desejamos inicializar nossas classes de maneiras diferentes. No exemplo de um produto físico, o estoque é inicialmente definido como zero.

public ProdutoFisico(string nome, string descricao,
    decimal preco, string imagem) 
    : base(nome, descricao, preco, imagem)
{
    this.Estoque = 0;
}

No entanto, podemos querer inicializar um produto com um valor específico de estoque. Para isso, podemos criar um novo construtor para diferenciar essas duas inicializações.

public ProdutoFisico(string nome, string descricao,
    decimal preco, string imagem, int estoque)
    : base(nome, descricao, preco, imagem)
{
    this.Estoque = estoque;
}

Dessa forma, o compilador não apresenta mais erros, e estamos utilizando o polimorfismo de sobrecarga.

Exemplos Práticos de Uso

Podemos ver a aplicação dos métodos ao construir um produto físico com estoque zerado. Ao executar, verificamos que o estoque é zero.

ProdutoFisico item1 = new ProdutoFisico("Teclado", "Modelo compacto e silencioso" +
    "perfeito para produtividade diária.",
    80.00m, "Imagem");

Console.WriteLine(@$"Dados do item 1:
    Nome: {item1.Nome};
    Descricao: {item1.Descricao};
    Preco: {item1.Preco};
    Estoque: {item1.Estoque};
");

Em seguida, copiamos o produto físico item1 e criamos item2, inicializando-o com um estoque de 20.

ProdutoFisico item2 = new ProdutoFisico("Teclado", "Modelo compacto e silencioso" +
    "perfeito para produtividade diária.",
    80.00m, "Imagem", 20);

Console.WriteLine(@$"Dados do item 2:
    Nome: {item2.Nome};
    Descricao: {item2.Descricao};
    Preco: {item2.Preco};
    Estoque: {item2.Estoque};
");

Além disso, alteramos o preço utilizando duas formas de desconto. Para o item1, aplicamos um desconto de 10% passando 10.0M e, em seguida, imprimimos o preço atualizado.

item1.AlterarPrecoComDesconto(10.0m);
Console.WriteLine(item1.Preco);

Depois, aplicamos um desconto de 10 unidades inteiras e imprimimos novamente.

item1.AlterarPrecoComDesconto(10);
Console.WriteLine(item1.Preco);

Ao executar o código, observamos que o estoque do item1 é zero e o do item2 é 20, confirmando as duas inicializações. As duas formas de aplicar desconto também funcionam corretamente: o produto inicialmente custava R$ 80,00, foi para R$ 72,00 com 10% de desconto e, depois, para R$ 62,00 com um desconto de R$ 10,00.

Conclusão sobre o Polimorfismo de Sobrecarga

Dessa forma, vemos o polimorfismo de sobrecarga em ação. Existe ainda outra forma de trabalhar com polimorfismo em métodos, que será abordada na sequência.

Polimorfismo de sobrecarga e sobrescrita - Polimorfismo de sobrescrita

Introdução ao Polimorfismo em Métodos

Para discutirmos sobre outro tipo de polimorfismo em métodos, vamos considerar a seguinte situação: temos nossos produtos físicos, que podem ser entregues. Assim, criaremos dentro de produto físico um método chamado entregar, que receberá um endereço. Teremos um public void entregar e, para entregar, passaremos uma string endereço como parâmetro. A ideia da entrega será apenas imprimir que o produto está sendo entregue. Podemos utilizar Console.WriteLine, calculando a rota com base no endereço e enviando o produto. Utilizaremos interpolação para incluir o nome do nosso produto, resultando na entrega do produto físico. Aqui, estamos apenas utilizando implementações de teste.

Implementação do Método Entregar para Produtos Físicos

Vamos começar definindo o método Entregar na classe de produto físico:

public void Entregar(string endereco)
{

}

Agora, vamos adicionar a lógica para imprimir a mensagem de entrega, utilizando interpolação de strings para incluir o nome do produto:

public void Entregar(string endereco)
{
    Console.WriteLine($"Calculando frete com base no {endereco} e enviando {Nome}");
}

Adaptação do Método Entregar para Produtos Digitais

Conseguimos entregar produtos físicos, mas, ao pensarmos em produtos digitais, também é possível realizar entregas. A entrega de um produto digital também requer um endereço, mas, neste caso, será um endereço eletrônico, pois desejamos enviar o produto digital para o e-mail do cliente. Vamos copiar nosso método entregar e aplicá-lo ao produto digital. Logo após o método estáExpirado, colaremos o método copiado e faremos as adaptações necessárias. Teremos uma string endereço e, em vez de calcular uma rota, queremos simplesmente enviar o nome do produto para o e-mail. Colocaremos o endereço de e-mail fornecido pelo usuário. Com isso, conseguimos realizar a entrega de um produto digital.

Para o produto digital, o método Entregar será assim:

public void Entregar(string endereco)
{
    Console.WriteLine($"Enviando {Nome} para o email {endereco}");
}

Generalização do Método Entregar na Classe Produto

Ao observarmos, notamos que temos um método entregar tanto em produto digital quanto em produto físico. Se esse método está presente nas duas classes, podemos afirmar que nossos produtos sempre serão entregues. Assim, podemos querer incluir esse comportamento na nossa classe produto. Sempre que adicionarmos um novo tipo de produto, desejaremos entregá-lo de alguma forma. Para indicar que esse comportamento é genérico, adicionaremos o método entregar na classe produto. Teremos um public void entregar.

Aqui, notamos uma diferença: ao observarmos os métodos avaliar e alterarPreçoComDesconto, percebemos que eles estão na classe produto e são como cópias para as classes produto físico e produto digital, como se fossem replicados diretamente durante a herança. No caso do entregar, não é isso que desejamos. O método entregar existirá na classe produto, mas as implementações serão diferentes dentro de produto físico e produto digital. Portanto, não queremos implementar de fato o método entregar na classe produto, pois um produto genérico não é entregue; apenas produtos físicos e digitais, que são classes concretas, são entregues.

Definição de Método Abstrato para Entregar

Dessa forma, podemos afirmar que o método entregar na classe produto será abstrato, sendo implementado apenas nas classes concretas. É semelhante ao que fazemos com métodos em interfaces, onde apenas declaramos os métodos e os implementamos posteriormente. Para isso, utilizaremos a palavra-chave abstract no nosso método entregar, passando também o parâmetro string endereço. Assim, todas as classes que herdam de produto serão obrigadas a implementar o método entregar.

public abstract void Entregar(string endereco);

Uso da Palavra-Chave Override para Implementação

No momento, o produto físico está apresentando erro, indicando que não implementamos o método entregar, mas ele já está implementado.

Por que estamos enfrentando esse erro? Isso ocorre porque precisamos da palavra-chave override. Toda vez que usamos essa palavra, estamos indicando que nosso método está sendo sobrescrito na classe filha. Ao lado da linha 24, há um "O" e uma seta para cima, semelhante ao que tínhamos nas interfaces, indicando que estamos pegando um método herdado e sobrescrevendo-o na classe filha. No caso de produto digital, também precisamos utilizar a palavra-chave override. Assim, conseguimos ter um método genérico, abstrato, chamado entregar, e implementá-lo em cada uma das classes filhas.

public override void Entregar(string endereco)
{
    Console.WriteLine($"Calculando frete com base no {endereco} e enviando {Nome}");
}
public override void Entregar(string endereco)
{
    Console.WriteLine($"Enviando {Nome} para o email {endereco}");
}

Polimorfismo de Sobrescrita

Quando utilizamos a palavra override, estamos indicando que o método está sendo sobrescrito. Por isso, esse tipo de polimorfismo é chamado de polimorfismo de sobrescrita. A ideia é ter um método declarado na classe genérica e reimplementá-lo, sobrescrevendo-o nas classes filhas, que são produto digital e produto físico, no nosso caso.

Implementações Padrão e Uso da Palavra-Chave Virtual

Além dos métodos abstratos, podemos querer fornecer implementações padrão para nossos métodos nas classes mães. No caso do método entregar, por exemplo, poderíamos querer definir uma entrega padrão. Poderíamos fazer um console.writeLine para uma entrega genérica, apenas para visualização. Fazendo isso, enfrentamos um problema, pois o método não é mais abstrato. Precisamos remover o abstract, e assim temos uma implementação padrão. No entanto, se voltarmos para produto físico ou produto digital, eles começarão a apresentar erros. Isso ocorre porque não está sinalizado de forma alguma, dentro da classe produto, que o método entregar pode ser sobrescrito. Precisamos usar outra palavra-chave, que é virtual, para indicar que podemos sobrescrever o método nas classes filhas. Uma vez que usamos virtual, podemos usar override nas classes produto físico e produto digital sem problemas.

public virtual void Entregar(string endereco)
{
    Console.WriteLine("Entrega genérica");
}

Visualização do Funcionamento no Program.cs

Aqui temos uma diferença: podemos sobrescrever nosso método se ele for do tipo abstrato e não tiver nenhuma implementação. É importante lembrar que só conseguimos trabalhar com métodos abstratos dentro de classes abstratas. Se não temos métodos abstratos, mas apenas métodos já implementados, precisamos marcá-los como virtuais para que possam ser sobrescritos.

Para visualizar como isso está funcionando, vamos para a classe Program.cs. Nessa classe, temos um item 1, que é um produto físico, e um item 2, que é um produto digital. Podemos chamar item 1.Entregar e passar um endereço físico. Podemos chamar o mesmo método em item 2, que também será Entregar, mas passaremos um e-mail, como "iasmin.com". Fazendo isso, temos duas entregas. Estamos chamando o método Entregar, mas são os mesmos métodos com o mesmo tipo de parâmetro, ou seja, têm a mesma assinatura, mas pertencem a classes diferentes. No final, a implementação desses métodos será diferente.

item1.Entregar("endereço físico");
item2.Entregar("iasmin@gmeil.com");

Execução e Resultados

Vamos executar para ver o resultado. A compilação está sendo executada. Ao lado, temos nossos dados. A primeira entrega foi "calculando o frete e enviando o teclado", e agora "enviando o curso para o e-mail iasmin.com". Vamos apenas adicionar um "A" aqui. Feito isso, visualizamos essas duas entregas de formas diferentes, o que é mais um tipo de polimorfismo.

Sobre o curso Praticando C#: orientação a objetos com polimorfismo

O curso Praticando C#: orientação a objetos com polimorfismo possui 29 minutos de vídeos, em um total de 20 atividades. Gostou? Conheça nossos outros cursos de .NET em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Escolha a duração
do seu plano

Conheça os Planos para Empresas