Encapsulamento de dados em JavaScript: getters e setters

Encapsulamento de dados em JavaScript: getters e setters

Ao construir aplicações JavaScript maiores, há logo a necessidade de dividi-las em módulos ligados por contratos claros. Quando se trata de manutenção de longo prazo, uma forma de manter a compatibilidade após alterações na interface precisa ser fornecida. Para fazer isso, linguagens orientadas a objetos dependem de encapsulamento ou informações ocultando detalhes de implementação dos usuários de um pedaço de código, para que ele possa mudar sem impactar os clientes.

Neste post vamos falar sobre encapsulamento de dados em JavaScript. Mais especificamente, vamos falar sobre encapsulamento de propriedades por trás de getters e setters.

alguns conceitos

JavaScript foi projetado tendo como base o princípio da digitação de pato:

“Quando eu vejo um pássaro que anda como um pato e nada como um pato e grasna como um pato, eu chamo aquele pássaro de pato.”

“Em outras palavras, no seu núcleo, JavaScript é uma linguagem orientada a objetos, mas diferentemente de outras linguagens orientadas a objetos como C# e Java, nas classes JavaScript são cidadãos de segunda classe e as interfaces não existem. Os objectos não têm tipos e, portanto, o código precisa de ser escrito para interagir com objectos que têm determinadas propriedades e métodos. Contratos entre módulos são baseados em um conjunto de métodos e propriedades assumidas como presentes nos objetos trocados.

Ancapsulação é fundamental na programação orientada a objetos. Isso significa que um determinado objeto deve ser capaz de esconder detalhes de seu funcionamento interno, de modo que outros objetos que o interagem não dependam de detalhes de sua implementação, mas de um alto nível acordado na interface. Isto torna a vida dos desenvolvedores mais simples, tanto do lado do provedor quanto do lado dos usuários, pois ambos conhecem que os detalhes de implementação podem mudar sem quebrar objetos clientes.

Por que expor dados em uma interface é uma má idéia

Em JavaScript um objeto é simplesmente um dicionário de nomes de propriedades mapeadas em valores, quando o valor de uma propriedade é uma função, nós o chamamos de um método. A interface entre métodos é o conjunto de propriedades que o código cliente espera encontrar em um objeto.

Se um objeto expõe apenas métodos, é fácil fazer a interface evoluir com o tempo. Um método pode verificar seus parâmetros e reagir de acordo. Também é possível criar novos métodos fornecendo novas funcionalidades. E, suportando os antigos que adaptam o comportamento antigo sobre o novo. Com propriedades não é possível fazer isso.

No caso das propriedades, manter uma interface estável não é fácil. Pois, por padrão, o código do cliente pode se referir a propriedades inexistentes de um objeto. Escrever para uma propriedade inexistente irá criá-la. A sua leitura irá retornar indefinida. O código do cliente não será capaz de detectar rapidamente se ele estiver usando propriedades depreciadas. Pior, o erro pode se propagar para outras partes do código, tornando a detecção do problema mais difícil.

A API JavaScript padrão tem métodos que podem ser úteis para evitar tais problemas: é possível congelar ou selar um objeto, de modo que nomes de propriedades não suportadas não possam ser usados.

Getter/Setters para salvar o dia

Esconder os detalhes da implementação do método é fácil.

Getters/setters foram oficialmente introduzidos à linguagem no ECMAScript 5.1 (ECMA-262). Eles são atualmente suportados em todos os principais navegadores desktop e móveis.

A idéia básica é que ele adiciona sintaxe para definir propriedades de acessor como métodos, ao invés de simples propriedades de dados. Um getter é definido pela palavra-chave getter seguida por uma função com o nome da propriedade, não tomando argumentos e retornando o valor da propriedade. Um setter é definido pelo conjunto de palavras-chave seguido por uma função com o nome da propriedade, tomando como parâmetro o novo valor da propriedade.

O exemplo seguinte ilustra um getter e um setter usado para definir uma propriedade acessor chamada prop:

var obj = { v: 0, get prop() { return this.v; }, set prop(newValue) { this.v = newValue; }};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Output:

042

Um também pode criar uma propriedade read-only definindo um getter sem setter:

var obj = { get prop() { return -1; },};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Output:

-1-1

Uma propriedade pode então ser substituída por um par de métodos que, caso a implementação de uma classe seja alterada, podem reagir a ela, e adaptar o comportamento da classe. Na pior das hipóteses, pode levantar uma exceção para dizer ao usuário que a propriedade está depreciada e não deve mais ser usada.

Outras leituras

Espero que você tenha aprendido algo novo sobre o design de aplicações complexas evolutivas em JavaScript neste post. Nesta seção, vou dar-lhe alguns links para links contendo mais informações.

Artigos de produto

Esta seção traz alguns artigos curtos apresentando os conceitos e fornecendo uma visão geral das APIs relacionadas:

  1. Obtentores de propriedades e setters.
  2. JavaScript Getters and Setters

Artigos mais avançados

Estes artigos falam sobre tópicos relacionados mais avançados como os problemas trazidos pelos getters e setters, e como resolvê-los.

  1. Porque getters/setters é uma má idéia em JavaScript
  2. Encapsulamento de dados em JavaScript

Documentação de referência

Finalmente, para realmente dominar o tópico, os links para a documentação mozilla das APIs relacionadas:

  1. get and set permitem definir getters e setters. Além do que explicamos aqui. Existem outras características interessantes como o suporte para nomes de propriedades geradas dinamicamente, e usar getters e setters para simular valores de configuração/estilo em um array
  2. seal and freeze permite controlar quais propriedades de um objeto são modificáveis ou não e como. Ele pode ser usado para evitar ter clientes usando partes depreciadas da API de um objeto.
  3. defineProperty permite definir getters e setters e ao mesmo tempo ter mais controle sobre como essas propriedades são vistas pelos clientes e como são modificáveis.

Deixe uma resposta

O seu endereço de email não será publicado.