Suportando Method Chaining em uma API já existente
Neste artigo aprendemos a suportar Method Chaining em uma API sem suporte a esse recurso.
O Problema
O IndexedDB disponibiliza uma API para que possamos persistir objetos. Vejamos um trecho desta API em ação:
// conexão criada com o banco antes
const store = 'books';
object = { name: 'Cangaceiro JavaScript', isbn: 9788594188014 };
const request = connection
.transaction([store],"readwrite")
.objectStore(store)
.add(object);
request.onsuccess = () => console.log('persistiu com sucesso');
request.onerror = e => console.log(e.target.error);
Queremos encadear as chamadas de onsuccess
e onerror
. Em outras palavras, queremos o suporte à Method Chaining. Mas para que isso seja possível, precisamos criar uma requisição encadeável. Por padrão, o IndexedDB não suporta requisições que suportem chaining, no entanto, podemos criar um wrapper que encapsule a requisição e permita utilizar o recurso desejado:
// conexão criada com o banco antes
const store = 'books';
object = { name: 'Cangaceiro JavaScript', isbn: 9788594188014 };
// cria um wrapper a partir da requisição
ChainableRequest.of(connection
.transaction([store],"readwrite")
.objectStore(store)
.add(object)
).success(() => console.log('persistiu com sucesso'))
).error(e => console.log(e.target.error))
No código anterior temos a chamada de ChainableRequest.of()
, mas a classe ChainableRequest
não existe. Precisamos implementá-la.
Implementando a classe ChainableRequest.
A classe ChainableRequest
receberá em seu constructor
o objeto request que encapsulará:
class ChainableRequest {
constructor(request) {
this._request = request;
}
}
A classe ChainableRequest é um wrapper que encapsula o objeto original.
Excelente, mas do jeito que construímos nossa classe, precisaremos utilizar o operador new
para criar instâncias de ChainableRequest
. Podemos evitar o uso do operador através do método estático of
:
class ChainableRequest {
constructor(request) {
this._request = request;
}
static of(request) {
return new ChainableRequest(request);
}
}
Da maneira que organizamos o código é o próprio método of()
que se encarregará de criar a instância de ChainableRequest
lidando com o operador new
. Vamos continuar nossa implementação.
Criando métodos que retornam a própria instância
Vamos criar um método para cada propriedade de uma requisição do IndexedDB:
class ChainableRequest {
constructor(request) {
this._request = request;
}
static of(request) {
return new ChainableRequest(request);
},
success(fn) {
this._request.onsuccess = fn;
return this;
}
error(fn) {
this._request.onerror = fn;
return this;
}
}
Vejam que os métodos success
e error
recebem a lógica que será aplicada nas respectivas propriedades _request.onsuccess
e _request.onerror
. Todavia, o retorno de ambos é a própria instância de ChainableRequest
. É esse retorno que permitirá as chamadas encadeadas. Com a classe completa, o código proposto no início deste artigo funcionará:
// conexão criada com o banco antes
const store = 'books';
object = { name: 'Cangaceiro JavaScript', isbn: 9788594188014 };
ChainableRequest.of(connection
.transaction([store],"readwrite")
.objectStore(store)
.add(object)
).success(() => console.log('persistiu com sucesso'))
).error(e => console.log(e.target.error))
O ganho na legibilidade pode ser ainda maior dependendo da quantidade de métodos encadeáveis utilizados para resolver o problema em questão.
Conclusão
Não é pelo fato de uma API não ser encadeável que ela nos privará desse recurso. Com um pouco de código podemos criar uma pequena camada sob a API já existente possibilitando chamadas encadeáveis.
E você? Já aplicou essa estratégia antes? Tem algum exemplo de outra API no qual essa estratégia se torna ainda mais interessante. Deixe seu comentário.