Exemplos

Notas de alunos

Parte 1.

Roberval, professor de português da rede pública, está aprendendo a programar. Para facilitar o gerenciamento das notas de seus estudantes, ele optou por implementar um programa orientado a objeto, em TypeScript, que possibilita cadastrar três notas para cada aluno, bem como obter a média alcançada por cada um desses estudantes. Apresente um código que poderia ser desenvolvido por Roberval para resolver este problema.

Uma forma de resolver envolve considerar que media é um atributo derivado, ou seja, deve ser computado sempre que for requisitado. Neste caso, uma implementação adequada seria:

export class Aluno{
    private _nome: string;
    private _rga: string;
    private _nota1: number = 0;
    private _nota2: number = 0;
    private _nota3: number = 0;

    constructor(nome: string, rga: string){
        this._nome = nome;
        this._rga = rga;
    }

    get nome(): string{
        return this._nome;
    }
    get rga(): string{
        return this._rga;
    }

    get nota1(): number{
        return this._nota1;
    }

    get nota2(): number{
        return this._nota2;
    }

    get nota3(): number{
        return this._nota3;
    }

    set nota1(nota: number){
        this._nota1 = nota;
    }
    
    set nota2(nota: number){
        this._nota2 = nota;
    }
    
    set nota3(nota: number){
        this._nota3 = nota;
    }

    get media(){
        return (this._nota1 + this._nota2 + this._nota3) / 3; 
    }
}

let pedro:Aluno = new Aluno("Pedro Silva", "1234234332");
pedro.nota1 = 7;
pedro.nota2 = 5;
pedro.nota3 = 7.5;
console.log(`Média de ${pedro.nome}: ${pedro.media}`);

Note que, no caso acima, não implementamos setters para nome e rga. Isso significa que esses dados não são alteráveis após a criação do objeto por elementos externos à classe.

Uma segunda possibilidade, agora considerando o armazenamento de média, seria:

export class Aluno{
    private _nome: string;
    private _rga: string;
    private _nota1: number = 0;
    private _nota2: number = 0;
    private _nota3: number = 0;
    private _media: number = 0;

    constructor(nome: string, rga: string){
        this._nome = nome;
        this._rga = rga;
    }

    get nome(): string{
        return this._nome;
    }
    get rga(): string{
        return this._rga;
    }

    get nota1(): number{
        return this._nota1;
    }

    get nota2(): number{
        return this._nota2;
    }

    get nota3(): number{
        return this._nota3;
    }

    set nota1(nota: number){
        this._nota1 = nota;
    }
    
    set nota2(nota: number){
        this._nota2 = nota;
    }
    
    set nota3(nota: number){
        this._nota3 = nota;
    }

    get media(){
        this._media = (this._nota1 + this._nota2 + this._nota3) / 3; 
        return this._media;
    }
}

let pedro:Aluno = new Aluno("Pedro Silva", "1234234332");
pedro.nota1 = 7;
pedro.nota2 = 5;
pedro.nota3 = 7.5;
console.log(`Média de ${pedro.nome}: ${pedro.media}`);

A implementação acima é bastante interessante porque, no método get media(), atualiza-se o valor do atributo privado _media e, depois, esse atributo é retornado. Consegue observar alguma limitação com relação à essa estratégia?

🧠 Teste seus conhecimentos

Qual afirmação corretamente descreve um aspecto inerente à implementação anterior?

  • A

    O valor de média deve ser sempre acessado pelo método get, não pelo atributo em si, para garantir a validade do código

  • B

    O programa funcionará adequadamente independentemente da abordagem usada para acessar o valor da média, mesmo com futuras implementações de novos métodos.

  • C

    Os atributos deveriam ser públicos.

  • D

    A implementação apresentada não cumpre o critério de encapsulamento.

  • E

    Nenhuma das alternativas anteriores está correta.

Se você respondeu à pergunta anterior, já deve ter observado que, se um novo método for implementado nesta classe, existe a possibilidade de um equívoco ser ocasionado. Veja, por exemplo, o seguinte método:

export class Aluno{
    /* todos os outros atributos e métodos apresentados anteriormente */

    public consultarAprovacao(): boolean{
        return this._media >= 5 ? true : false;
    }
}

Agora, imagine que esse método seja chamado para o objeto pedro. Qual será a saída do método na chamada a seguir?

let pedro:Aluno = new Aluno("Pedro Silva", "1234234332");
pedro.nota1 = 7;
pedro.nota2 = 5;
pedro.nota3 = 7.5;
console.log(pedro.consultarAprovacao());

Se você respondeu que a saída será false, você acertou. Agora, vamos consultar uma segunda implementação possível para o método:

export class Aluno{
    /* todos os outros atributos e métodos apresentados anteriormente */

    public consultarAprovacao(): boolean{
        return this.media >= 5 ? true : false;
    }
}

let pedro:Aluno = new Aluno("Pedro Silva", "1234234332");
pedro.nota1 = 7;
pedro.nota2 = 5;
pedro.nota3 = 7.5;

E neste caso, a saída será false também? Na verdade, não.

Isso ocorre porque, embora os dois trechos pareçam conter a mesma implementação, há uma sutil diferença: no primeiro caso, acessamos diretamente o valor do atributo media. Se o método get media() nunca foi chamado, o valor o atributo _media nunca foi alterado (lembre-se que ele foi inicializado com 0).

No segundo caso, o resultado é true porque acessamos o valor da média não diretamente pelo atributo (_media), mas sim pelo método get media(). Ou seja: na nossa implementação, teríamos sempre que nos lembramos de acessar o valor pelo método, não pelo atributo.

Apesar de parecer uma desvantagem, essa é a abordagem mais interessante: mesmo na própria classe, é fortemente recomendado que acessemos os valores dos atributos não diretamente, mas sempre pelo intermédio do get equivalente. O mesmo se aplica ao acesso aos valores que, idealmente, deve ser feito pelo set do atributo.

De outro modo, poderíamos seguir por uma terceira abordagem:

export class Aluno {

  private _nome: string;
  private _rga: string;
  private _nota1: number = 0;
  private _nota2: number = 0;
  private _nota3: number = 0;
  private _media: number = 0;

  constructor(nome: string, rga: string) {
    this._nome = nome;
    this._rga = rga;
  }

  get nome() : string {
    return this._nome;
  }

  get rga() : string {
    return this._rga;
  }

  get nota1() : number {
    return this._nota1;
  }

  get nota2() : number {
    return this._nota2;
  }

  get nota3() : number {
    return this._nota3;
  }

  set nota1(nota: number) {
    this._nota1 = nota;
    this.calcularMedia();
  }

  set nota2(nota: number) {
    this._nota2 = nota;
    this.calcularMedia();

  }

  set nota3(nota: number) {
    this._nota3 = nota;
    this.calcularMedia();
  }

  private calcularMedia() : void {
    this._media = (this._nota1 + this._nota2 + this._nota3) / 3;
  }

  get media() {
    return this._media;
  }

  public consultarAprovacao() : boolean {
    return this._media >= 5 ? true: false;
  }
}

let pedro: Aluno = new Aluno("Pedro Silva", "1234234332");
pedro.nota1 = 7;
pedro.nota2 = 5;
pedro.nota3 = 7;
console.log(pedro.consultarAprovacao());

Agora, sempre que alteramos o valor de uma nota, atualizamos também o valor da média. Observe que:

Os métodos setters não devem incorporar muitas tarefas. Nesse caso específico, a atualização da média é admissível, já que este é um atributo computado, que deve ser atualizado sempre a partir dos valores de nota1, nota2 e nota3.

Apesar disso, essa abordagem de implementação ainda é menos vantajosa que a anterior.

Enunciado 2

Roberval, da atividade anterior, agora deseja também encontrar o aluno que obteve a menor média. Ele quer saber o nome e o número de matrícula desse estudante. Como ele pode implementar essa funcionalidade?

let AlunoComMenorMedia = alunos.reduce((menor, atual) => {
    return atual.media < menor.media ? atual : menor;
});

console.log(AlunoComMenorMedia)

Enunciado 3

Agora, o incansável professor Roberval deseja listar todos os alunos que estão reprovados, considerando como critério para reprovação a média inferior a 5,0.

let alunosMediaMenor5 = alunos.filter((aluno) => aluno.media < 5);
console.log(alunosMediaMenor5);