Outro artigo, outro padrão de software JavaScript. Hoje, falaremos sobre o padrão Factory. Ele é um dos meus padrões preferidos, especialmente o Simple Factory, que irei explicar mais tarde. Factories – na vida real, bem como dentro do mundo de programação – criam objetos. Elas ajudam a limpar o seu código por meio da remoção de todos os operadores new.
Simple Factory
Existem dois tipos de factories: o Simple Factory e o Normal Factory. Começaremos com o primeiro, porque é… bem… mais simples. Hoje, em vez de simplesmente evocarmos um exemplo, iremos realmente usar nosso do artigo do padrão Decorator e o corrigiremos para torná-lo simplesmente impressionante. Caso não entenda o padrão Decorator então deveria voltar e ler sobre ele antes de continuar, caso queira que isso tenha algum sentido.
Então, o que o padrão Factory fará para tornar o exemplo Decorator melhor? Se você se lembra do código de implementação final, se você quisesse um veículo com todos os três recursos que foram codificados atualmente, você precisava criar quatro objetos, todos com o operador new. Isso é cansativo e tedioso, por isso vamos usar uma única chamada de função para criar um “car” com todas as características que desejamos.
O Simple Factory é apenas um Singleton (ou apenas uma classe estática na maioria das linguagens de programação, mas, em JavaScript, eles são essencialmente os mesmos) que tem uma ou mais funções para criação e retorno de objetos. Se você olhar para o código abaixo, verá o objeto escrito e como usá-lo.
var CarFactory = {
// One function to make a car with any combo of features
makeCar: function (features) {
var car = new Car();
// If they specified some features then add them
if (features && features.length) {
var i = 0,
l = features.length;
// iterate over all the features and add them
for (; i < l; i++) {
var feature = features[i];
switch(feature) {
case 'powerwindows':
car = new PowerWindowsDecorator(car);
break;
case 'powerlocks':
car = new PowerLocksDecorator(car);
break;
case 'ac':
car = new ACDecorator(car);
break;
}
}
}
return car;
}
}
// Call the factory method. Send in an array of strings
// representing the features you want your car to have
var myCar = CarFactory.makeCar(['powerwindows', 'ac']);
// If you want a plain old car, just don't send in an array
var myCar = CarFactory.makeCar();
Tornando o Factory melhor
Nosso exemplo Decorator teve alguns problemas adicionais que não foram resolvidos no trecho de código anterior, embora o Factory possa facilitar a solução deles. O problema número 1 era que não havia nenhuma maneira para garantir que uma característica não tivesse sido adicionada mais de uma vez. Por exemplo, você poderia ter vários PowerWindowDecorators envolvendo o mesmo “car”, o que não faria muito sentido. O outro era que se os recursos tivessem que ser adicionados em qualquer ordem específica, não haveria, novamente, nenhuma maneira de fazer cumprir essa regra.
Podemos corrigir ambos os problemas usando o padrão Factory. A melhor parte é que nada dessa lógica precisa ser contida no objeto Car ou nos objetos Decorator. Está tudo num lugar só: o factory, que em uma perspectiva do mundo real faz sentido. Você vê o “car” ou as suas características sabendo como adicionar recursos ou em que ordem instalá-los? Não, isso é feito no factory.
var CarFactory = {
makeCar: function (features) {
var car = new Car(),
// create a list of all the possible features and set them to 0,
// which means it won't be included
featureList = {
powerwindows: 0,
powerLocks: 0,
ac: 0
};
// If they specified some features then add them
if (features && features.length) {
var i = 0,
l = features.length;
// iterate over all the features and add them
for (; i < l; i++) {
// mark the feature in the featureList as a feature
// that will be included. This way we only get one of
// each feature.
featureList[features[i]] = 1;
}
// Now we add the features on in a specific order
if (featureList.powerwindows) {
car = new PowerWindowsDecorator(car);
}
if (featureList.powerlocks) {
car = new PowerLocksDecorator(car);
}
if (featureList.ac) {
car = new ACDecorator(car);
}
}
return car;
}
}
// Now you can have some careless programmer call this
var myCar = CarFactory.makeCar(['ac', 'ac', 'powerlocks', 'powerwindows', 'ac']);
// and it will still give you a car with only 1 ACDecorator and it will be built
// in the correct order.
Agora você consegue ver por que o Simple Factory é um dos meus modelos preferidos? Ele leva embora todos os bits tediosos de criação do objeto. Levando em conta que usar um factory para criar um único objeto que não tenha aderência a nenhuma interface a não ser a sua própria é algo meio idiota.
Outra forma
O poder do Factory não se limita aos Decorators. Basicamente, todos os objetos que compartilham uma interface podem ser criados utilizando um Factory, que ajuda a dissociar os objetos individuais a partir de seu código. Tudo o que você sabe, com certeza, é o tipo de objeto que você receberá do Factory. Assim, as únicas coisas das quais você vai depender são o Factory e uma interface, não importa quantos objetos diferentes implementem essa interface.
E se eu mostrar um exemplo de uso do Factory para fins não-decorator? O próximo Factory será parte de um framework MVC imaginário. O Factory pode obter um modelo de objeto do tipo especificado e enviá-lo de volta para o controlador.
Diferentes controladores usam modelos diferentes e, até dentro do mesmo controlador, podem usar um modelo diferente para métodos diferentes. Em vez de codificar os nomes específicos do modelo “classe” para o controlador, usamos o Factory para buscar o modelo para nós. Dessa forma, se mudarmos para um novo modelo de classe (talvez a gente tenha decidido usar um tipo diferente de banco de dados), o único lugar onde nós precisamos fazer mudanças é no Factory. Não vou entrar em detalhes de como implementar isso, pois seria um desperdício de tempo. Vamos apenas mostrar como ele é usado, e eu deixarei você usar sua imaginação para o código de implementação. Abaixo você verá o código para um controlador que usa o Factory.
// This controller uses two different models: car and cars
var CarController = {
getCars: function () {
var model = ModelFactory.getModel('cars');
return model.get('all');
},
getCar: function (id) {
var model = ModelFactory.getModel('car');
return model.get(id);
},
createCar: function () {
var model = ModelFactory.getModel('car');
model.create();
return model.getId();
},
deleteCars: function (carIds) {
var model = ModelFactory.getModel('cars');
model.delete(carIds);
},
.
.
.
}
Dando fim a essa loucura
Terminando já? E o Normal Factory? Acredito que este artigo tornou-se longo o suficiente, não? Que tal nos reunir novamente para outro artigo dedicado exclusivamente ao padrão Normal Factory? Deve haver tanto código para escrever, que o próximo artigo não deverá ser muito pequeno. Além disso, isso lhe dará algum tempo para absorver o conhecimento que ganhou aqui antes de eu encher sua cabeça com mais um disparate.
Então, se você já se deparou com o padrão Decorator ou com qualquer grupo de objetos usando a mesma interface, certifique-se de considerar o uso do padrão Factory para realizar a criação desses objetos, a fim de remover as dependências neles.
?
Texto original disponível em http://www.joezimjs.com/javascript/javascript-design-patterns-factory/