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.
Neste curso, vamos trabalhar a coerência, que é um dos pilares da orientação a objetos. Abordaremos diversos tópicos relacionados a isso.
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.
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.
Estamos bastante animados para trabalhar com todos esses tópicos e esperamos que vocês nos acompanhem também.
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.
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.
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
.
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)
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;
}
}
}
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.
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.
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
.
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;
}
}
}
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.
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;
}
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.
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;
}
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;
}
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.
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};
");
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:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS (1 ANO) e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS (1 ANO) e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO (1 ANO) e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Brinde aniversário Alura
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.
Enquanto durarem os estoques