Class Factory ou Factory Function?
JavaScript é uma linguagem multi paradigma suportando a programação orientada a objetos e a funcional. Contudo, grandes poderes trazem grandes responsabilidades e muitas vezes ficamos na dúvida qual deles aplicar para resolver determinado problema.
Neste post teremos um problema e utilizaremos diferentes paradigmas para resolvê-lo. Vamos ao problema.
O problema
Precisamos isolar a complexidade de criação de proxies em nossa aplicação para facilitar sua construção em diversas partes do sistema.
Dentro do paradigma orientado a objetos, há o padrão de projeto Factory para lidar com problemas como o citado. Já no paradigma funcional podemos trabalhar com Factory Functions. Veremos a solução com os dois paradigmas para que o leitor possa avaliar cada um deles. Iniciaremos pelo orientado a objetos.
Class Factory
Vejamos um exemplo utilizado o paradigma da orientação a objetos com a classe ProxyFactory
. Seu método createProxy
recebe como parâmetro um objeto e retorna um proxy deste mesmo objeto que ao ter suas propriedade lidas exibe no console o nome da propriedade acessada:
// paradigma orientado à objetos
class ProxyFactory {
createProxy(object) {
return new Proxy(object, {
get(target, property, receiver) {
console.log(`A propriedade "${property}" foi lida.`);
return target[property];
}
});
}
}
let person = { name: 'Flávio Almeida' };
// cria um instância do ProxyFactory
const factory = new ProxyFactory();
// cria o proxy
person = factory.createProxy(person);
console.log(person.name); // exibe também a mensagem do proxy
No entanto, como a classe ProxyFactory
não possui estado interno, podemos converter seu método createProxy
para um método estático permitindo acessá-lo diretamente da classe sem a necessidade de uma instância:
// paradigma orientado à objetos
class ProxyFactory {
static createProxy(object) {
return new Proxy(object, {
get(target, property, receiver) {
console.log(`A propriedade "${property}" foi lida.`);
return target[property];
}
});
}
}
let person = { name: 'Flávio Almeida' };
// não precisou criar a instância
person = ProxyFactory.createProxy(person);
console.log(person.name); // exibe também a mensagem do proxy
Com essa modificação, não faz sentido o programador criar instâncias de ProxyFactory
, pois o método createProxy
só pode ser acessado diretamente pela classe. Para garantirmos isso, podemos lançar uma exeção caso o operador new
seja utilizado:
// paradigma orientado à objetos
class ProxyFactory {
// blidando o uso do operador new
constructor() {
throw new Error('Não é permitida a criação de instâncias desta classe');
}
static createProxy(object) {
return new Proxy(object, {
get(target, property, receiver) {
console.log(`A propriedade "${property}" foi lida.`);
return target[property];
}
});
}
}
// vai resultar em um erro em tempo de execução
const factory = new ProxyFactory();
Por outro lado, podemos rescrever nossa classe através do paradigma funcional.
Factory Function
Vamos declarar apenas a função createProxy
. Diferente de outras linguagens como Java ou C#, funções podem existir sem pertencerem a uma classe:
// paradigma funcional
const createProxy = object => new Proxy(object, {
get(target, property, receiver) {
console.log(`A propriedade "${property}" foi lida.`);
return target[property];
}
});
let person = { name: 'Flávio Almeida' };
person = createProxy(person);
console.log(person.name);
Fica evidente que temos um código mais limpo. Primeiro, declaramos createProxy
como uma function expression
com auxílio de uma arrow function
sem bloco e que assume automaticamente como retorno o proxy criado. Não gastamos linhas de código declarando uma classe e por conseguinte não foi necessário blindar seu constructor
para evitar a criação de instâncias.
Conclusão
No contexto do problema que vimos, utilizar uma factory function
mostrou-se uma solução menos verbosa e fácil de manter do que a sua versão baseada em classe. Contudo, a aplicação de um paradigma depende muito do problema a ser resolvido, além da bagagem do próprio programador.
E você? Consegue dar um exemplo no qual o paradigma da orientação a objetos seja mais elegante que o funcional?