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 herança

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

O que é herança? - Apresentação

Introdução e Audiodescrição

Olá! Meu nome é Yasmin Araújo, sou instrutora na Escola de Programação, e irei me autodescrever para fins de acessibilidade.

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

Objetivos do Curso

Neste curso, vamos trabalhar a coerência, que é um dos pilares da orientação a objetos. Abordaremos diversos tópicos relacionados a isso.

Conteúdo do Curso

Começaremos aprendendo a utilizar a herança, com foco principalmente no uso de herança em C#. Também trabalharemos com classes mães e classes filhas.

Vamos utilizar a herança para definir esses tópicos. Além disso, veremos as classes abstratas e as interfaces, aprendendo a diferenciar essas duas abordagens. Por fim, trabalharemos com o uso de composição e compararemos o uso de composição com o uso de herança.

Importância da Orientação a Objetos

Temos diversos tópicos aqui que são bastante importantes, e todos eles, juntos, nos permitem representar o mundo real e diversas situações complexas do mundo real utilizando a programação. É nesse ponto que reside a magia da orientação a objetos.

Conclusão e Expectativas

Estamos bastante animados para trabalhar com todos esses tópicos e esperamos que vocês nos acompanhem também.

Observações Finais

Infelizmente, não há snippets de código fornecidos para este vídeo, mas a explicação detalhada de Yasmin nos dá uma boa base teórica para entender como a herança, classes abstratas, interfaces e composição são fundamentais na programação orientada a objetos.

O que é herança? - Classes mães e filhas

Introdução ao Sistema de E-commerce

Nós vamos continuar trabalhando neste curso com a classe Produto, para simular um sistema de e-commerce. Ao analisarmos a nossa classe Produto, percebemos que ela contém informações que representam produtos físicos. Por exemplo, um produto só pode ser estocado se for um produto físico. No entanto, queremos representar produtos físicos em nosso site, mas também desejamos vender produtos digitais. Um produto digital não terá estoque, mas, por outro lado, terá um link de download. Assim, um produto digital terá todas as características de um produto físico, exceto o estoque, além de outras informações e métodos que um produto físico não possui.

Criação das Classes ProdutoFisico e ProdutoDigital

Para modelar essa situação, podemos copiar e colar a classe Produto e fazer as alterações necessárias. No gerenciador de soluções, vamos copiar a classe Produto e colar, renomeando a cópia para ProdutoDigital, que será a classe diferente. Na classe Produto original, vamos renomeá-la para ProdutoFisico, para diferenciar bem as duas. O Visual Studio perguntará se desejamos renomear todas as referências, mas não faremos isso, então clicaremos em "não" e renomearemos manualmente. Assim, a classe Produto passará a se chamar ProdutoFisico, e o construtor também será ProdutoFisico.

Implementação das Classes

Vamos começar criando a classe ProdutoFisico:

class ProdutoFisico

E o seu construtor:

public ProdutoFisico(string nome, string descricao,
            decimal preco, string imagem)

Na classe ProdutoDigital, a classe e o construtor serão renomeados para ProdutoDigital.

class ProdutoDigital
public ProdutoDigital(string nome, string descricao,
            decimal preco, string imagem)

Ajustes na Classe ProdutoDigital

Um produto digital não possui estoque, então podemos remover o estoque da classe, do construtor e do método estaDisponivel.

// Deletes the following line from ProdutoDigital.cs
public int Estoque { get; };
// Deletes the following line from the ProdutoDigital constructor
this.Estoque = 0;
// Deletes the following method from ProdutoDigital.cs
public bool EstaDisponivel()
{
    return Estoque > 0;
}

Além disso, um produto digital terá um link de download, que será semelhante à imagem, que era um link usado para exibir no site. Esse link precisa ser validado usando a propriedade. Portanto, vamos duplicar a propriedade da imagem e fazer o mesmo código.

private string linkDownload;
// Duplicates the existing Imagem property block
public string Imagem
{
    get
    {
        return imagem;
    }
    set
    {
        if (value.Length > 0)
        {
            this.imagem = value;
        }
    }
}

Agora, vamos criar a propriedade LinkDownload:

public string LinkDownload
{
    get
    {
        return linkDownload;
    }
    set
    {
        if (value.Length > 0)
        {
            this.linkDownload = value;
        }
    }
}

Problemas de Duplicação de Código

Com essas alterações, temos a nossa classe ProdutoDigital e também o nosso ProdutoFisico. No entanto, se observarmos bem, temos vários códigos duplicados. Para visualizar isso melhor, vamos para o nosso slide. Atualmente, temos uma classe ProdutoFisico com um código verde e outro transparente, e uma classe ProdutoDigital com o mesmo código verde, que está duplicado, e abaixo, a parte transparente, onde estão as especificidades do produto digital. Se quisermos construir um novo produto, criaríamos uma nova classe que teria toda essa parte verde, comum a todos, e uma parte específica. A cada nova classe criada, duplicamos mais e mais nossos códigos. Essa duplicação dificulta a manutenção das classes. Não queremos trabalhar com duplicação de código, e é por isso que a orientação a objetos nos propõe o conceito de herança.

Introdução ao Conceito de Herança

Com a herança, vamos separar as informações que aparecem em comum em uma outra classe. Teremos uma classe Produto, onde estará tudo o que é genérico. Faremos com que as outras classes herdem dessa classe. Assim, ProdutoFisico herdará de Produto, e ProdutoDigital também herdará de Produto. Com isso, eles herdarão esses atributos, mas não necessariamente precisaremos escrever esse código dentro dessas classes mais específicas. Dessa forma, evitamos a duplicação de código, erros na manutenção e outros problemas que podem surgir dessa duplicação.

Implementação da Herança

Como vamos modelar a herança dentro das nossas classes no Visual Studio? Vamos voltar para lá. No Visual Studio, precisaremos criar uma nova classe Produto, conforme vimos no slide.

class Produto

Dentro de "Praticando C Sharp", vamos pegar o ProdutoFisico novamente e usar Ctrl C, Ctrl V para facilitar nosso trabalho. Vamos renomear essa classe de ProdutoFisico para Produto. Entraremos no arquivo e identificaremos o que Produto tem em comum em todas as classes: imagem, nome, descrição e preço. O estoque é específico do ProdutoFisico, então vamos apagá-lo.

// Deletes the following line from Produto.cs
public int Estoque { get; };

O construtor também pode ser apagado por enquanto.

// Deletes the following constructor from Produto.cs
public Produto(string nome, string descricao,
            decimal preco, string imagem)
{
    this.Nome = nome;
    this.Descricao = descricao;
    this.Preco = preco;
    this.Estoque = 0;
    this.Imagem = imagem;
}

Alterar preço com desconto é geral, assim como a imagem. Dessa forma, já definimos nossa classe Produto.

Aplicação da Herança nas Classes Filhas

Agora, vamos modelar a herança. Salvamos a classe Produto, vamos para ProdutoDigital e adicionamos dois pontos, Produto. Ao fazer isso, estamos dizendo que ProdutoDigital herda de Produto.

class ProdutoDigital : Produto

Assim, tudo o que existe dentro de Produto estará dentro de ProdutoDigital também. Podemos apagar o nome, a descrição, o preço e a imagem.

// Deletes the following lines from ProdutoDigital.cs
public string Nome { get; };
public string Descricao { get; };
public decimal Preco { get; private set; }

Também podemos apagar o método de alterar preço com desconto e a propriedade da imagem.

// Deletes the following method and property from ProdutoDigital.cs
public void AlterarPrecoComDesconto(decimal desconto)
{
    Preco = Preco * (1 - desconto/100);
}

public string Imagem
{
    get
    {
        return imagem;
    }
    set
    {
        if (value.Length > 0)
        {
            this.imagem = value;
        }
    }
}

Vamos fazer o mesmo para ProdutoFisico.

class ProdutoFisico : Produto

No ProdutoFisico, apagamos tudo o que está em Produto, que são o nome, a descrição, o preço e a imagem.

// Deletes the following lines from ProdutoFisico.cs
public string Nome { get; };
public string Descricao { get; };
public decimal Preco { get; private set; }

Podemos apagar também o método de alterar preço com desconto e a imagem.

// Deletes the following method and property from ProdutoFisico.cs
public void AlterarPrecoComDesconto(decimal desconto)
{
    Preco = Preco * (1 - desconto/100);
}

public string Imagem
{
    get
    {
        return imagem;
    }
    set
    {
        if (value.Length > 0)
        {
            this.imagem = value;
        }
    }
}

Conclusão e Próximos Passos

Dessa forma, temos uma nova classe para ProdutoFisico e uma nova classe para ProdutoDigital. O ProdutoFisico deve herdar a classe de fato, para que possamos utilizar os atributos. Então, adicionamos dois pontos, Produto.

Estamos trabalhando com a herança tanto em ProdutoFisico quanto em ProdutoDigital. Nosso construtor está com alguns problemas associados a propriedades, que vamos comentar por enquanto, e depois entenderemos melhor.

// Comments out the constructor in ProdutoFisico.cs
/*public ProdutoFisico(string nome, string descricao,
            decimal preco, string imagem)
{
    this.Nome = nome;
    this.Descricao = descricao;
    this.Preco = preco;
    this.Estoque = 0;
    this.Imagem = imagem;
}*/
// Comments out the constructor in ProdutoDigital.cs
/*public ProdutoDigital(string nome, string descricao,
            decimal preco, string imagem)
{
    this.Nome = nome;
    this.Descricao = descricao;
    this.Preco = preco;
    this.Imagem = imagem;
}*/

Comentando esses códigos dos construtores, nosso código está compilando. Note que ProdutoDigital agora é uma classe muito menor do que estava antes, e o mesmo ocorre com ProdutoFisico. Se quisermos criar um novo tipo de produto, não precisaremos declarar novamente as propriedades nome, descrição, preço e imagem. Basta herdar da classe Produto, evitando toda essa duplicação de código.

Uma nomenclatura formal é que todas as classes que herdam de outras são classes filhas, e as classes das quais herdamos são as classes mães. Nesse caso, Produto é a classe de quem herdamos, então é uma classe mãe, e as classes ProdutoDigital e ProdutoFisico são classes filhas. Na sequência, veremos como resolver o problema do construtor para conseguir executar nosso código de fato, pois ele está com alguns problemas.

O que é herança? - Herança

Problemas com o Construtor ao Implementar Herança

Nós observamos que, ao implementar a herança, o nosso construtor deixou de funcionar, tanto para o caso de produto digital quanto para o caso de produto físico. Vamos descomentar o nosso construtor de produto digital para entender o que ocorreu e quais são os erros. Dentro do nosso construtor, notamos que surgiram erros relacionados a nome, descrição e preço. O erro que aparece é que a propriedade ou indexador não podem ser atribuídos, pois esse indexador é somente leitura.

Para ilustrar, aqui está o construtor original do ProdutoDigital que causou o problema:

public ProdutoDigital(string nome, string descricao,
    decimal preco, string imagem)
{
    this.Nome = nome;
    this.Descricao = descricao;
    this.Preco = preco;
    this.Imagem = imagem;
}

Análise dos Erros e Solução com protected set

O que isso significa? Se voltarmos ao nosso arquivo produto.cs, veremos que as propriedades nome, descrição e preço possuem vários GETs, e no caso do preço, há um private set. Portanto, não conseguimos alterar a propriedade fora da classe. Se tentarmos utilizar o construtor para acessar atributos dessa classe, não conseguiremos alterá-los, justamente porque não existe um set ou porque o set é privado.

Como podemos resolver isso? Podemos utilizar um modificador de acesso que permita que nossas propriedades sejam acessadas e alteradas dentro das classes filhas. Para isso, usaremos o modificador protected. No lugar do private set do preço, colocaremos um protected set. Faremos o mesmo para a descrição, adicionando um protected set, e novamente para o nome, colocando um protected set.

Aqui estão as alterações nas propriedades:

public decimal Preco { get; protected set; }
public string Descricao { get; protected set; }
public string Nome { get; protected set; }

Se voltarmos à nossa classe de produto digital e salvarmos, notaremos que agora tudo está compilando corretamente, pois indicamos que em classes filhas conseguimos alterar esses valores. Essa é uma das soluções. Se formos ao produto físico e descomentarmos, ele funcionará normalmente, pois alteramos tudo para todas as classes.

Considerações sobre Encapsulamento e Alternativa com Construtor Base

No entanto, essa solução de usar protected set pode gerar outros problemas. Pode ser que desejemos que nome, descrição e preço sejam realmente alteráveis apenas dentro da classe de produto. Ou seja, se quisermos alterar o preço com desconto em outra classe filha, não conseguiremos, pois toda essa lógica está encapsulada apenas dentro de produto. Essa é uma boa prática.

Em vez de ter um protected set e permitir alterações dentro das outras classes, podemos manter nossa propriedade oculta e permitir que apenas no construtor ela seja alterada. Como faremos isso? Primeiro, apagaremos o protected set. Vamos reverter o código para o estado anterior. Em seguida, podemos criar um construtor com todos os elementos gerais de produto. Copiaremos o construtor de produto digital e colaremos na classe produto, renomeando para produto. Esse construtor contém exatamente o que desejamos: elementos gerais de produto e atribuições conforme criamos anteriormente.

Aqui está o construtor da classe Produto:

public Produto(string nome, string descricao,
    decimal preco, string imagem)
{
    this.Nome = nome;
    this.Descricao = descricao;
    this.Preco = preco;
    this.Imagem = imagem;
}

Implementação de Herança de Construtores

Uma vez que temos esse construtor, podemos usá-lo nas classes filhas. Como faremos isso? Utilizando uma herança de construtores. No produto digital, logo após a declaração dos parâmetros, podemos usar dois pontos e a palavra-chave base.

Usando essa palavra base, nós vamos abrir e fechar os parênteses e passar todos os atributos de produto digital. Vamos passar nome, descrição, preço e imagem. Ao fazer isso, estamos chamando o construtor da nossa classe base, a classe mãe, que é o construtor de produto. Assim, toda vez que quisermos inicializar um produto digital, faremos primeiro uma inicialização de produto da parte mais genérica e depois a parte mais específica.

Aqui está como o construtor do ProdutoDigital foi modificado:

public ProdutoDigital(string nome, string descricao,
    decimal preco, string imagem, string linkDownload)
    : base(nome, descricao, preco, imagem)
{
    this.LinkDownload = linkDownload;
}

Nesse caso, toda a inicialização de nome, descrição, preço e imagem já é feita em produto, então podemos apagar essas linhas. Podemos deixar apenas as partes mais específicas de um produto digital, como, por exemplo, o linkDownload. Vamos adicionar no produto digital uma string linkDownload. Podemos dizer que as partes iniciais vão usar o nosso construtor base e a parte específica, que é o this.linkDownload, vai receber o linkDownload do parâmetro.

Podemos fazer exatamente a mesma coisa na nossa classe produto físico. Como faremos isso? Vamos copiar o que está em produto digital e colar. Agora, todas as nossas inicializações gerais não precisam mais ficar no construtor de produto físico; elas ficam no construtor de produto genérico. Podemos apagar essas outras linhas e deixar apenas o this.stock. Dessa forma, reaproveitamos o código do construtor e garantimos melhor o nosso encapsulamento, tanto de produto quanto de produto físico e produto digital.

Aqui está o construtor do ProdutoFisico:

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

Criação de Produtos no program.cs

Vamos salvar as nossas classes. Podemos utilizar essas classes agora no nosso program.cs. Se formos para o program.cs, estamos criando produtos genéricos, mas podemos criar produtos específicos. Vamos criar um produto físico, que é um new ProdutoFisico. Aqui, o nosso código não altera nada, mas vamos criar também um produto digital. Vamos copiar todo esse código de cima, colar e alterar para produto digital. Então, produtoDigital item2 será um new ProdutoDigital.

Aqui está como criamos os produtos no program.cs:

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

ProdutoDigital item2 = new ProdutoDigital("Curso", "OO em C#",
    100.00m, "Imagem ilustrativa", "Link");

Um produto digital pode ser, por exemplo, um curso. Vamos criar os nossos valores. Ele será um curso e a descrição dele será "OO em C-sharp". O preço será de 100 reais, então colocaremos 100.00m. Além disso, temos também o link de download. Vamos colocar um link. Além disso, temos a imagem também antes. Vamos colocar "imagem ilustrativa", só para ficar diferente. Temos um curso de "OO em C-sharp", que custa 100 reais, uma imagem ilustrativa e um link de download.

Exibição dos Dados dos Produtos

Aqui embaixo, podemos passar também dados do item2, que serão item2.nome, item2.descrição, item2.preço. E ali, ao invés de ser estoque, colocamos link. Colocamos item2.link. Assim, criamos um produto físico e um produto digital. Aparentemente, está tudo compilando. Vamos executar o nosso código para ver se realmente está funcionando. O código está sendo executado e temos os nossos dados. Os dados do item1, assim como estavam antes, e as alterações aqui também. Os dados do item2 também foram construídos com sucesso. Conseguimos criar produtos do tipo produto físico e produto digital, utilizando a herança.

Aqui está o código para exibir os dados do item2:

Console.WriteLine(@$"Dados do item 2:
    Nome: {item2.Nome};
    Descrição: {item2.Descricao};
    Preço: {item2.Preco};
    Link: {item2.LinkDownload};
");

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

O curso Praticando C#: orientação a objetos com herança possui 38 minutos de vídeos, em um total de 21 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