Javascript Class Patterns

Tweet Like

Quando eu comecei a usar o C++, ele era essencialmente um pré processador que produzia código em C. Eu gostava disso, pois quando eu ficava realmente confuso com uma característica em particular, podia examinar o código em C para descobrir como funcionava. As funcionalidades que eu achava mais interessantes em C++ podem ser divididas em duas categorias: Polimorfismo e Encapsulamento. Polimorfismo é só uma maneira bonita de dizer que uma determinada função ou operação pode trabalhar com tipos diferentes, da mesma forma que a adição pode ser feita com tipos de números inteiros ou em ponto flutuante. Usando Sobrecarga e Templates, você pode escrever funções em C++ que tratam todos os tipos diferentes que você quer operar.

Encapsulamento significa que alguma coisa está organizada de forma isolada do resto do código, com uma interface bem definida para acesso ou modificação. É como uma caixa preta com um conjunto de interfaces. Você só precisa saber o que a caixa preta deveria fazer, e não precisa se envolver com o que tem dentro dela. Quando programas ficam complicados, é bom saber que vários pedaços executam suas atividades sem interferências. O C++ introduziu o encapsulamento na forma de Classes.

O C não tinha classes, mas tinha 'structs' (estruturas), que eram formas simples de dados organizados. As estruturas tinham uma coleção de campos, com identificadores para cada campo:

struct point {
  int x;
  int y;
  int z;
};

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

Isto é uma melhoria em relação ao Fortran, digamos, onde você precisaria de uma matriz de 3 números para representar um ponto. Ao menos deste jeito, é mais claro o que cada campo significa. É uma boa forma de organizar os dados, mas não fornece nenhuma interface de programação, ele apenas mantém os campos juntos em um objeto. O C++, especialmente em sua forma original, usa o conceito de estruturas em C para organizar os dados, mas adiciona interfaces na forma de funções membro (métodos). Ele também adicionou a habilidade de ocultar dados em uma secção privada, acessível somente pelas funções membro, e adicionou construtores, métodos especiais chamados para inicializar os dados quando o objeto é criado. Continuando com o exemplo acima, suponha que você queira prover uma função para mover um ponto de forma relativa a onde ele se encontra, ou de ir para um determinado local no espaço. Em C++, você poderia criar uma classe como esta:

class point {
  point() {x=0; y=0; z=0;}
  point(int i, int j, int k) {x=i; y=j; z=k;}
  void moveBy(int i, int j, int k) {x+=i; y+=j; z+=k;}
  void moveTo(int i, int j, int k) {x=i; y=j; z=k;}
private:
  int x;
  int y;
  int z;
}

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

O código C++ poderia ser convertido em C, com funções especiais para os métodos um ma função construtor especial que fosse automaticamente chamada quando o objeto fosse criado, no início do programa ou de forma mais dinâmica, durante a sua execução. Então era mais ou menos possível de se fazer isso usando C, porém com C++ tudo ficava mais fácil, e garantia que a seção privada não fosse acessível de fora das funções membro.

Agora, o Javascript não possui classes, mas possui Objetos que se parecem um pouco com as estruturas do C. Eles possuem campos nomeados e dados associados a estes campos:

point = {
  x: 0,
  y: 0,
  z: 0
}

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

É bom e organizado, mas sem interfaces para funções membro. Nos podemos facilmente adicionar funções ao objeto:

point = {
  x: 0,
  y: 0,
  z: 0,

  moveBy: function(i, j, k) {x+=i; y+=j; z+=k;},
  moveTo: function(i, j, k) {x=i; y=j; z=k;}
}

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

mas isso não faz o que queremos. Quando ela tenta acessar 'x' (ou y ou z), ela não acessa point.x, mas olha no ambiente por uma variável chamada 'x'. Ou ela não encontra nenhuma, e lança um erro, ou ela encontra uma e altera a variável errada.

Por sorte existe uma correção fácil. 'This' é a correção fácil, ou seja, a variável especial em javascript 'this'. Se você nunca a usou antes, os detalhes podem ser um tanto confusos, mas para nosso propósito, eu acho que é seguro assumir que a variável 'this' sempre indica a instância que inclui o objeto, então substituimos 'x' por 'this.x':

point = {
  x: 0,
  y: 0,
  z: 0,

  moveBy: function(i, j, k) {this.x+=i; this.y+=j; this.z+=k;},
  moveTo: function(i, j, k) {this.x=i; this.y=j; this.z=k;}
}

See the Pen %= penName %> by Intel IDZ (@intelidz) on CodePen

Agora quando dizemos point.moveTo(3,5,0), as coordenadas (x,y,z) se tornam (3,5,0). Se nós chamarmos point.moveBy(10,10,10), tornam-se (13,15,10). A variável 'this' garante que quando chamamos point.moveBy() e point.moveTo(), this.x corresponde a point.x e por aí vai. Então temos os dados e temos as funções membro, mas mesmo assim isso não nos dá tudo o que a classe em C++ nos dava. Por isso precisamos de construtores e ocultação de dados. Usando as técnicas que usamos para variáveis estáticas, nós conseguimos resolver isso, mas vamos falar mais sobre isso na próxima.