Dando seguimento à série de padrão de projeto de software – JavaScript, hoje vamos mostrar o padrão Proxy. A palavra “proxy” pode ser definida como um substituto, e isso essencialmente explica o que é um proxy. Um proxy é um objeto que tem a mesma interface de outro objeto e é usado no lugar dele. A única pergunta é por que usaríamos um proxy em vez do objeto original.
Antes de responder a essa pergunta, eu gostaria de lembrar/informar a todos que isso faz parte de uma longa série de artigos. Você pode acessar a lista deles no final. Pode não ser uma má ideia dar uma conferida antes (ou depois) de ler este aqui. O padrão proxy não depende de conhecimento algum relacionado aos outros padrões, por isso, se preferir, você pode esperar até terminar de ler este aqui.
Por que usar um proxy?
Voltando à questão de por que se usar um proxy, podemos dar alguns cenários diferentes, nos quais o proxy pode vir a calhar: atraso na instanciação de um objeto grande, acesso a um objeto remoto e controle de acesso.
Antes de entrar em cada uma dessas situações, veremos um exemplo de um proxy sem nenhum propósito, apenas para que você obtenha a essência do que é um. Primeiro, precisamos criar uma classe – CarList – e depois faremos a classe proxy que a envolve.
/* For the sake of keeping us on track, we won't
show implementation code in great detail. */
var CarList = function() {
//creation
};
CarList.prototype = {
getCar: function(…) {
// get a vehicle from the list using the
// given parameters
},
search: function(…) {
// search through the cars using the query
},
addCar: function(…) {
// add a car to the database
},
.
.
.
};
var CarListProxy = function() {
this.carList = new CarList();
};
CarListProxy.prototype = {
getCar: function(…) {
return this.carList.getCar(…);
},
search: function(…) {
return this.carList.search(…);
},
addCar: function(…) {
return this.carList.addCar(…);
},
.
.
.
};
O proxy virtual
Tenho certeza de que todos que estão lendo isto possuem um pouco de imaginação, então vamos fazer de conta que CarList tenha 10 vezes mais métodos e que a maioria deles seja muito grande e complicada. Eu sei que essa circunstância pode ser um pouco exagerada, mas eu estou fazendo isso com um objetivo. Temos um grande objeto que usaria um bom número de ciclos de CPU apenas para instanciá-lo. Não faria sentido adiar a instanciação até termos certeza de que ela será usada? Bem, esse é o objetivo do Proxy Virtual. Alguém pode instanciar um proxy e o objeto grande normal não será instanciado até que um método seja chamado, que requer a sua criação. Vamos converter o nosso proxy antigo inútil para um proxy virtual.
var CarListProxy = function() {
// Don't initialize the CarList yet.
this.carList = null;
};
CarListProxy.prototype = {
// this function is called any time any other
// function gets called in order to initialize
// the CarList only when needed.
_init: function() {
if (!this.carList) {
this.carList = new CarList();
}
},
getCar: function(…) {
// every function makes this call to _init()
this._init();
return this.carList.getCar(…);
},
search: function(…) {
this._init();
return this.carList.search(…);
},
addCar: function(…) {
this._init();
return this.carList.addCar(…);
},
.
.
.
}
É claro que esse pode não ser, necessariamente, o melhor método de atrasar a inicialização de um objeto grande. Vamos imaginar (de novo) que CarList não possua incontáveis métodos complexos, mas apenas que os dados nele contidos sejam grandes, como um banco de dados inteiro da marca e do modelo de cada veículo produzido comercialmente na existência. Nesse caso, podemos simplesmente criar um método que inicia todos esses dados, mas apenas usar esse método quando estivermos na necessidade dos dados. Não estou dizendo para você acabar com o poder do padrão proxy, mas sim para te ajudar a ser um programador melhor, ensinando-lhe que um proxy não é a resposta para todos os problemas.
PAREI AQUI!
Proxies remotos em JavaScript
O segundo cenário que mencionei foi acessar um objeto remoto. Esse cenário faz mais sentido quando se usa algo ao longo das linhas de Java e SOAP, principalmente porque quando falamos de algo ser remoto, estamos geralmente falando de algo na outra extremidade de um cabo ethernet, e é altamente improvável que acessaremos objetos JavaScript em um servidor, mas, com a popularização do Node.js, isso está se tornando cada vez mais provável. Para o nosso proxy JavaScript, usaremos um objeto que fornece uma forma simplificada de acessar a API de um serviço web. Isso pode ir um pouco contra a definição do proxy, implementando a mesma interface do objeto que está sendo substituído, mas isso terá que ser feito agora. Eu considero isso mais como um facade, embora outros tenham chamado isso de proxy antes.
Dessa vez, o nosso CarListProxy não está implementando a interface de um objeto, mas apenas apenas extraindo informações do serviço de web fictício
www.WeSeriouslyListEverySingleVehicleModelEver.com.
// The real CarList is contained on the server, so
// CarListProxy gets the information from the server
var CarListProxy = function (){};
CaListProxy.prototype = {
getCar: function(…) {
// Once again we don't show implementation code
// so that you don't get distracted by it
ajax('http://www.weseriouslylisteverysinglevehiclemodelever.com/getCar/'
+ args);
},
search: function(…) {
ajax('http://www.weseriouslylisteverysinglevehiclemodelever.com/search/'
+ args);
},
addCar: function(…) {
ajax('http://www.weseriouslylisteverysinglevehiclemodelever.com/addCar/'
+ args);
},
.
.
.
}
Isso é tudo que eu posso te mostrar para o proxy remoto para JavaScript. Então que tal uma situação final: controlar o acesso ao objeto original?
Controlando o acesso a um objeto JavaScript via proxy
Podem existir várias razões as quais não permitem o acesso ao objeto original, mas, para o bem deste exemplo, estamos à espera de uma data específica, porque o site no qual o script está não é para estar no ar ainda, supostamente. Eu não sei por que os desenvolvedores sentiram que era necessário, para todos os arquivos JavaScript, serem carregados em uma página “Em Construção”, mas quem sou eu para discutir com personagens fictícios?
A fim de realmente controlar o acesso, temos que ter certeza de que não há nenhuma maneira de acessar o objeto original, a não ser através do proxy, por isso vamos fechar tudo em uma função anônima auto-invocativa, mas anexe o proxy como uma propriedade para window, a fim de dar acesso a ele a partir do mundo exterior.
// self invoking anonymous function
(function() {
// We already know what the CarList looks like, so I
// won't rewrite it here
var CarList = …
var CarListProxy = function() {
// Don't initialize the CarList yet.
this.carList = null;
this.date = new Date();
};
CarListProxy.prototype = {
// this function is called any time any other
// function gets called in order to initialize
// the CarList only when needed. The CarList will
// not be initialized if it isn't time to yet.
_initIfTime: function() {
if (this._isTime() && !this.carList) {
this.carList = new CarList();
return true;
}
return false;
},
// Check to see if we've reached the right date yet
_isTime() {
return this.date > plannedReleaseDate;
},
getCar: function(…) {
// if _initIfTime returns a falsey value, getCar will
// return a falsey value, otherwise it will continue
// and return the expression on the right side of the
// && operator
return this._initIfTime() && this.carList.getCar(…);
},
search: function(…) {
return this._initIfTime() && this.carList.search(…);
},
addCar: function(…) {
return this._initIfTime() && this.carList.addCar(…);
},
.
.
.
}
// Make the CarListProxy publicly available
window.CarListProxy = CarListProxy;
// you could also do the below statement so people don't even
// know they're using a proxy:
window.CarList = CarListProxy;
}());
E aí está: os três sabores do padrão de proxy com exemplos. Pessoalmente, não vejo muitas circunstâncias em que um proxy será tão útil, exceto, talvez, como um facade para serviços web, especialmente com a crescente popularidade do uso de serviços web. Espero que todos possam encontrar uma razão para usá-lo, porque conhecimento é poder, mas somente se ele puder ser aplicado.
Até a próxima!
Abaixo, você vai encontrar a lista dos artigos já publicados da série Padrão de projetos de software – JavaScript.
- Padrão de projeto de software – JavaScript: Singleton
- Padrão de projeto de software – JavaScript: Bridge
- Padrão de projeto de software – JavaScript: Composite
- Padrão de projeto de software – JavaScript: Decorator
- Padrão de projeto de software – JavaScript: Facade
?
Texto original disponível em http://www.joezimjs.com/javascript/javascript-design-patterns-proxy/