Herança
Durante a modelagem de sistemas computacionais, observaremos várias situações em que diferentes classes compartilham mesmo atributos e métodos. Imagine, por exemplo, um sistema que envolve o cadastro de cursos de uma instituição de ensino. Um classe que representa um curso de graduação terá muitos membros (atributos e métodos) em comum com a classe que representa o curso de pós-graduação.
Há, no entanto, algumas particularidades. A classe CursoGraduacao
, por exemplo, pode ter um indicador do conceito ENADE (Exame Nacional de Avaliação de Egressos) do curso. A classe CursoPosGraduacao
, por sua vez, pode ter um atributo para identificar qual é a área associada junto à CAPES (Coordenação de Aperfeiçoamento de Pessoal de Ensino Superior).
Além dos atributos e métodos, um fator importante a se observar quando se fala de herança é as associações que cada classe terá. Uma tese de doutorado, por exemplo, deve ser vinculada a um curso de pós-graduação. Qualquer vinculação desta classe com curso de pós-graduação seria incorreta. Da mesma forma, uma disciplina de curso de graduação não pode ser vinculada a um curso de graduação.
Uma solução intermediária
Até aqui, já devemos concordar que a existência de três classes em nosso programa.
export class Curso{
private _nome: string;
constructor(nome: string){
this._nome = nome;
}
get nome(){
return this._nome;
}
set nome(nome: string){
this._nome = nome;
}
}
export class CursoGraduacao{
private _curso: Curso;
private _conceitoENADE: number;
constructor(curso: Curso, conceitoENADE: number){
this._curso = curso;
this._conceitoENADE = enade;
}
get conceitoENADE(){
return this._conceitoENADE;
}
set conceitoENADE(conceitoENADE: number){
this._conceitoENADE = conceitoENADE;
}
}
Para acessar o nome do curso, teríamos a seguinte operação:
let engcomp = new CursoGraduacao(new Curso("Engenharia de Computação"), 5);
console.log(engcomp.curso.nome);
Observe que uma leitura desta implementação seria: "curso de graduação tem curso e tem ENADE". De fato, o curso de graduação tem conceito ENADE, mas não soa correto dizer que o curso de graduação tem um curso. Ele é um curso.
Aplicando a herança
Nesse tipo de situação, usamos o conceito de herança, que faz justamente essa mudança de "tem" para "é". Na implementação de Curso
, por enquanto, nada muda. Diremos, nas classes filhas, que elas "herdam" as propriedades da classe Curso
.
export class CursoGraduacao extends Curso{
private _conceitoENADE: number;
constructor(nome: string, conceitoENADE: number){
super(nome);
this._conceitoENADE = enade;
}
get conceitoENADE(){
return this._conceitoENADE;
}
set conceitoENADE(conceitoENADE: number){
this._conceitoENADE = conceitoENADE;
}
}
export class CursoPosGraduacao extends Curso{
private _areaCAPES: number;
constructor(nome: string, areaCAPES: number){
super(nome);
this._areaCAPES = areaCAPES;
}
get areaCAPES(){
return this_.areaCAPES;
}
set areaCAPES(areaCAPES: number){
this._areaCAPES = areaCAPES;
}
}
Agora, a criação de um objeto para o curso de Engenharia de Computação ficaria da seguinte forma:
let engcomp = new CursoGraduacao("Engenharia da Computação", 5);
console.log(engcomp.nome);
Limitando a herança
Em alguns linguagens de programação, como é o caso de Java, pode-se impedir que uma classe tenha herdeiros, usando a palavra reservada final
. Esse recurso não está disponível em TypeScript.
Herança múltipla
Algumas linguagens de programação permitem que uma classe herde membros de mais uma classe. Isso não é possível em TypeScript
ou em Java
.