Introdução ao Polimorfismo
Tags: beginners, oop, typescript, braziliandevs
Published: 2022-05-19 21:21:03

## Tabela de Conteúdo
- [Polimorfismo](#polimorfismo)
- [Sobrescrita de Métodos](#sobrescrita-de-métodos)
- [Classes Abstratas](#classes-abstratas)
- [Métodos Estáticos](#métodos-estáticos)
- [Polimorfismo em Interfaces](#polimorfismo-em-interfaces)
- [Links Úteis](#links-úteis)

---

## Polimorfismo

### O que é?

Na programação, o Polimorfismo remete a capacidade de alterar o comportamento de determinado método de uma Super Classe em qualquer uma das Sub Classes que à herdem.

### O que faz?

Como dito em sua definição, permite a alteração de comportamentos de um determinado método sem alterar sua assinatura, dessa forma podemos ter o mesmo método com diferentes comportamentos a depender da classe que o implementa.

Um exemplo disso seria os métodos de ciclo de vida no React, ao trabalharmos o React com classes definimos os métodos de ciclo de vida com diferentes comportamentos dependendo do componente, porém esses métodos continuam a ter o mesmo nome, parâmetros e tipo de retorno.

### Tipos

Existem diferentes tipos de Polimorfismo dentro da POO _(Programação Orientada a Objetos)_, porém nesse artigo iremos abordar apenas a Sobrescrita de Métodos com TypeScript.

<br />

<small>[Voltar ao topo](#tabela-de-conteúdo)</small>

---

## Sobrescrita de Métodos

### O que é?
É a alteração do comportamento padrão de um método da Super Classe para um comportamento mais específico de uma Sub Classe, sendo que, se não o sobrescrevermos o método manterá o comportamento definido na Super Classe.

### Sintaxe
Para sobrescrevermos um determinado método, tudo que precisamos fazer é instanciá-lo na Sub Classe e definirmos uma nova lógica.

Uma obervação, os parâmetros e o tipo de retorno do método a ser sobrescrito podem ser mantidos, no intuito de padronizar o método, sendo a única alteração, a lógica interna.

``` ts
class Person {
  constructor(public name: string) {}

  move(): void {
    console.log(`${this.name} se move`);
  }
}
```

``` ts
class Runner extends Person {
  move() {
    console.log(`${this.name} corre`);
  }
}

class Lazy extends Person {
  move() {
    console.log(`${this.name} anda devagar`);
  }
}
```

``` ts
const john = new Person('John');
const runnerJohn = new Runner('John');
const lazyJohn = new Lazy('John');

john.move();  // John se move
runnerJohn.move();  // John corre
lazyJohn.move();  // John anda devagar
```

<br />

<small>[Voltar ao topo](#tabela-de-conteúdo)</small>

---

## Classes Abstratas

### O que são?

São classes que atuam de forma similar a Interfaces, isso porque elas não podem ser instanciadas, apenas herdadas, possibilitando ainda a criação de métodos abstratos, esses que por sua vez só podem existir em Classes Abstratas e __devem ser implementados__ em qualquer Sub Classe de uma Super Classe Abstrata.

A diferença de Classes Abstratas e Interfaces, é que nas classes podemos definir outros métodos e atributos que não são abstratos, ou seja, podem receber funcionalidade e valores.

### Sintaxe

Para definirmos Classes/Métodos Abstratos utilizamos a palavra reservada `abstract` antes de suas definições.

Lembrando que tudo que for abstrato, não pode ser implementado, apenas definido, e nada nos impede de criarmos métodos comuns dentro de Classes Abstratas.

``` ts
abstract class Person {
  constructor(public name: string) {}

  /*
  mesmo estando em uma classe abstrata, ainda podemos criar métodos comuns
  que não precisam ser obrigatoriamente implementados nas Sub Classes.
  */
  greetings() {
    console.log(`Eu me chamo ${this.name}`);
  }

  abstract move(): void;
}
```

``` ts
class Runner extends Person {
  move() {  // o método abstrato precisa ser implementado nas Sub Classes
    console.log(`${this.name} corre`);
  }
}

class Lazy extends Person {
  move() {
    console.log(`${this.name} anda devagar`);
  }
}
```

``` ts
const runnerJohn = new Runner('John');
const lazyJohn = new Lazy('John');

runnerJohn.move();  // John corre
runnerJohn.greetings();  // Eu me chamo John

lazyJohn.move();  // John anda devagar
lazyJohn.greetings();  // Eu me chamo John
```

<br />

<small>[Voltar ao topo](#tabela-de-conteúdo)</small>

---

## Métodos Estáticos

### O que são?

São métodos que não interagem com as instancias da classe, ou seja, manipulam as informações ligadas a classe em si, não a instância.

Também existem os atributos estáticos que assim como os métodos estão vinculados a classe e não as instâncias.

### Sintaxe

Para definirmos Métodos/Atributos Estáticos utilizamos a palavra reservada `static` antes de suas definições.

``` ts
class GroupMember {
  private static groupSize = 0;

  constructor(public newPerson: string) {
    Group.addMember();
  }

  private static addMember() {
    this.groupSize += 1;
  }

  static get size() {
    return this.groupSize;
  }
}
```

``` ts
console.log(GroupMember.size);  // 0
const member1 = new GroupMember('John');

console.log(GroupMember.size);  // 1
const member2 = new GroupMember('John');

console.log(GroupMember.size);  // 2
```

<br />

<small>[Voltar ao topo](#tabela-de-conteúdo)</small>

---

## Polimorfismo em Interfaces

### O que é?

Assim como nas classes, as Interfaces também podem ser polimorfas, ou seja, ter diferentes comportamentos a depender da implementação. Esse é o comportamento padrão de toda a Interface, afinal só definimos a assinatura, parâmetros e retorno do método, enquanto a lógica interna fica por nossa conta.

Porém podemos ainda utilizar das Generics criar classes e até funções ainda mais dinâmicas, dessa forma o tipo será determinado no momento de sua criação e não na definição.

### Sintaxe

Como dito acima, o comportamento padrão de métodos definidos na Interface é serem polimórficos, logo sua definição continua a mesma.

Já para utilizarmos Generics, tudo que precisamos fazer é nós utilizar dos símbolos de maior e menor `<>`, colocando entre eles uma letra _~~por convenção~~_ em maiúsculo.

``` ts
interface IGame<T> {
  title: string;
  platform: string;
  controls: T;
}
```

``` ts
type FPSControls = {
  W: 'up',
  A: 'left',
  S: 'down',
  D: 'right',
}

class FPSGame extends IGame<FPSControls> {
  constructor(
    public title: string,
    public platform: string,
    public controls: FPSControls
  ) {}
}
```

``` ts
type MOBAControls = {
  Q: 'firstAbility',
  W: 'secondAbility',
  E: 'thirdAbility',
  R: 'ultimate',
}

class MOBAGame extends IGame<MOBAControls> {
  constructor(
    public title: string,
    public platform: string,
    public controls: MOBAControls
  ) {}
}
```

<br />

<small>[Voltar ao topo](#tabela-de-conteúdo)</small>

---

## Links Úteis

- [TypeScript docs - Abstract Classes](https://www.typescriptlang.org/docs/handbook/2/classes.html#abstract-classes-and-members)
- [TypeScript docs - Static Members](https://www.typescriptlang.org/docs/handbook/2/classes.html#static-members)
- [TypeScript docs - Generic Classes](https://www.typescriptlang.org/docs/handbook/2/classes.html#generic-classes)
- [DevMedia - Polimorfismo](https://www.devmedia.com.br/conceitos-e-exemplos-polimorfismo-programacao-orientada-a-objetos/18701)

<br />

<small>[Voltar ao topo](#tabela-de-conteúdo)</small>