Explorando o Composite Pattern JavaScript

Imagem ilustrativa

Imagem ilustrativa

Design Patterns é um assunto bem comum em todas as linguagens de programação, e o mais importante quando se trata em manutenção de código. Um pattern é uma solução reutilizável que pode ser aplicada em um projeto de desenvolvimento de software. Hoje, vamos explorar o Composite pattern com implementação JavaScript.

O composite pattern diz que um grupo de objetos pode ser tratado da mesma maneira que um objeto individual desse grupo.

Um uso bem comum de Composite Pattern que você provavelmente já tenha visto é o sistema de diretórios de um Sistema Operacional que utiliza estrutura de dados em árvore (considere que temos uma pasta e dentro dessa pasta temos várias outras pastas, e que dentro de cada uma dessas outras pastas temos mais algumas pastas, e assim por diante).

Nesse modelo, temos “N” objetos que possui “N” filhos que também pode ter mais “N” filhos. Entretanto, a quantidade de objetos não importa, porque a implementação é a mesma para todos eles. Ok? Vamos ver isso melhor na prática.

Exemplo: Construindo uma cidade com Composite Pattern

Vamos colocar a mão na massa e criar nosso próprio exemplo modificando um pouco o modelo apresentado acima, mas ainda seguindo a estrutura em árvore. No nosso exemplo vamos construir duas cidades que possuem bairros que, por sua vez, possuem algumas casas (lembre-se da estrutura em árvore). Algo bem simples e que vai ficar parecido com isto:

| cities
|
|– São Paulo/
| |– Liberdade
| |– Casa 1
| |– Casa 2
| |– Ipiranga
| |– Casa 3
|
|– Rio de Janeiro/
| |– Leblon
| |– Casa 4
| |– Lapa
| |– Casa 5
| |– Casa 6
|

O objeto possuirá os seguintes métodos específicos do nosso composite:

add: adiciona um novo filho para o objeto;
remove: remove um determinado filho do objeto;
getChild: retorna um filho do objeto;

Método auxiliar:

getElement: retorna o elemento HTML do objeto específico;

Criamos o objeto “Cidade” que terá o formato composite:


1 var City = function (title, id, className) {
2     this.children = [];
3     this.element = document.createElement('div');
4     var h2 = document.createElement('h2');
5
6     this.element.id = id;
7     if (className) this.element.className = className;
8
9     h2.textContent = title;
10     this.element.appendChild(h2);
11
12 }
13
14 City.prototype = {
15     add: function (child) {
16         this.children.push(child);
17         this.element.appendChild(child.getElement());
18     },
19
20     remove: function (child) {   
21         for (var node, i = 0; node = this.getChild(i); i++) {
22             if (node == child) {
23                 this.children.splice(i, 1);
24                 this.element.removeChild(child.getElement());
25                 return true;
26             }
27         }
28
29         return false;
30     },
31
32     getChild: function (i) {
33         return this.children[i];
34     },
35
36     getElement: function () {
37         return this.element;
38     }
39 }

Instanciamos os objetos e montamos a estrutura


1 var cities = new City('', 'cities');
2 var saoPaulo = new City('São Paulo', 'sao-paulo');
3 var rioDeJaneiro = new City('Rio de Janeiro', 'rio-de-janeiro');
4 var liberdade = new City('Liberdade', 'liberdade');
5 var ipiranga = new City('Ipiranga', 'ipiranga');
6 var lapa = new City('Lapa', 'lapa');
7 var leblon = new City('Leblon', 'leblon');
8 var casa1 = new City('Casa 1', 'casa-1', 'composite-house');
9 var casa2 = new City('Casa 2', 'casa-2', 'composite-house');
10 var casa3 = new City('Casa 3', 'casa-3', 'composite-house');
11 var casa4 = new City('Casa 4', 'casa-4', 'composite-house');
12 var casa5 = new City('Casa 5', 'casa-5', 'composite-house');
13 var casa6 = new City('Casa 6', 'casa-6', 'composite-house');
14 var casaRemover7 = new City('Casa remover 7', 'casa-remover-7', 'composite-house');
15
16 liberdade.add(casa1);
17 liberdade.add(casa2);
18
19 ipiranga.add(casa3);
20 ipiranga.add(casaRemover7);
21
22 ipiranga.remove(casaRemover7); // Removido
23
24 lapa.add(casa4);
25
26 leblon.add(casa5);
27 leblon.add(casa6);
28
29 saoPaulo.add(liberdade);
30 saoPaulo.add(ipiranga);
31
32 rioDeJaneiro.add(lapa);
33 rioDeJaneiro.add(leblon);
34
35 cities.add(saoPaulo);
36 cities.add(rioDeJaneiro);
37
38 document.body.appendChild(cities.getElement());

Se preferir, aqui está o demo de como ficou o exemplo funcionando. Composite pattern é interessante quando temos uma coleção de objetos que possuem o mesmo tratamento, é um ótimo pattern e bastante usado pelos desenvolvedores.

Espero que tenham gostado. Qualquer dúvida, sugestão ou crítica, fique à vontade para deixar seu comentário. Até a próxima!

—-

Artigo de Pedro Araújo, publicado no iMasters.

DialHost
DialHost

Contamos com nosso DataCenter no Brasil de alta qualidade, estabilidade e confiança para hospedar seu site. Utilize poderosas ferramentas para otimizar o uso do seu serviço e seja sempre atendido de forma rápida e clara pelo nosso atendimento 24h.

CSS vs Animação JS: qual é o mais rápido?

Animação

Imagem ilustrativa

Como pode a animação baseada em JavaScript ser secretamente sempre tão rápida – ou mais rápida – do que as transições CSS? E como é possível que Adobe e Google lancem, consistentemente, sites mobile com rich-media que rivalizam com o desempenho dos apps nativos?

Este artigo serve como um passo a passo de como as bibliotecas de animação DOM baseadas em JavaScript, como Velocity.js e GSAP, são mais eficazes do que jQuery e bibliotecas de animação baseadas em CSS.

jQuery

Vamos começar com o básico: JavaScript e jQuery são falsamente confundidos. A animação de JavaScript é rápida. A do jQuery é mais lenta. Por quê? Porque nunca fez parte dos objetivos de design do jQuery ser um motor performático para animação:

 

Note-se que layout thrashing é o que causa travamento (stuttering) no início das animações, a coleta de lixo é o que causa stuttering durante as animações, e a ausência de RAF é o que geralmente produz baixa taxas de frames.

Exemplos de implementação

Evitar o layout thrashing consiste simplesmente em juntar as consultas e as atualizações DOM:


1 var currentTop,
2     currentLeft;
3
4 /* With layout thrashing. */
5 currentTop = element.style.top; /* QUERY */
6 element.style.top = currentTop + 1; /* UPDATE */
7
8 currentLeft = element.style.left; /* QUERY */
9 element.style.left = currentLeft + 1; /* UPDATE */
10
11 /* Without layout thrashing. */
12 currentTop = element.style.top; /* QUERY */
13 currentLeft = element.style.left; /* QUERY */
14
15 element.style.top = currentTop + 1; /* UPDATE */
16 element.style.left = currentLeft + 1; /* UPDATE */

As consultas que ocorrem após uma atualização forçam o navegador a recalcular os dados de estilo computadorizado da página (enquanto levam efeitos da nova atualização em consideração). Isso produz uma sobrecarga significativa para animações que estão sendo executadas ao longo de pequenos intervalos de apenas 16ms.

Da mesma forma, implementar RAF não precisa de uma reformulação significativa de sua base de código existente. Vamos comparar a implementação básica de RAF contra a de setInterval:


1 var startingTop = 0;
2
3 /* setInterval: Runs every 16ms to achieve 60fps (1000ms/60 ~= 16ms). */
4 setInterval(function() {
5     /* Since this ticks 60 times a second, we divide the top property's increment of 1 unit per 1 second by 60. */
6     element.style.top = (startingTop += 1/60);
7 }, 16);
8
9 /* requestAnimationFrame: Attempts to run at 60fps based on whether the browser is in an optimal state. */
10 function tick () {
11     element.style.top = (startingTop += 1/60);
12 }
13
14 window.requestAnimationFrame(tick);

 

RAF produz o maior boost possível para o desempenho de animação que você pode fazer com uma única alteração em seu código.

Transições CSS

As transições CSS superam jQuery ao descarregar a lógica de animação para o próprio navegador, que é eficiente em 1) otimizar a interação DOM e o consumo de memória para evitar stuttering, 2) alavancar os princípios de RAF nos bastidores e 3) forçar a aceleração de hardware (aproveitando o poder da GPU para melhorar o desempenho da animação).

A realidade, contudo, é que essas otimizações também podem ser realizadas diretamente no JavaScript. GSAP vem fazendo isso há anos. Velocity.js, um novo mecanismo de animação, não só alavanca essas mesmas técnicas, mas também vai vários passos além – como veremos em breve.

Chegar a um acordo com o fato de que a animação JavaScript pode rivalizar com bibliotecas de animação CSS é só o primeiro passo em nosso programa de reabilitação. O segundo passo é perceber que a animação JavaScript pode realmente ser mais rápida do que eles.

Vamos começar analisando os pontos fracos das bibliotecas CSS de animação:

  • As transições forçam as taxas de aceleração do hardware do GPU, resultando em stuttering e banding em situações de alto estresse. Esses efeitos são agravados em dispositivos móveis (em específico, o sttutering é um resultado da sobrecarga que ocorre quando os dados são transferidos entre a thread principal do navegador e a thread do compositor. Algumas propriedades CSS, como transforms e opacidade, são imunes a essa sobrecarga). A Adobe fala sobre esse assunto aqui.
  • As transições não funcionam para versões abaixo do Internet Explorer 10, causando problemas de acessibilidade para sites de desktop, já que IE8 e IE9 continuam sendo muito populares.
  • Pelo fato de as transições não serem nativamente controladas pelo JavaScript (elas só são desencadeadas por JavaScript), o navegador não sabe como otimizar em sincronia as transições com o código JavaScript que as manipula.

 

Em contrapartida, as bibliotecas de animação baseadas em JavaScript podem decidir por si mesmas quando permitir a aceleração de hardware, elas funcionam inerentemente em todas as versões do IE, e são perfeitamente adequadas para otimizações de animação em lote.

Minha recomendação é usar as transições brutas de CSS quando você estiver desenvolvendo exclusivamente para celular e suas animações forem compostas exclusivamente de mudanças de estado simples. Em tais circunstâncias, as transições são uma solução nativa de alto desempenho e que lhe permitem manter toda a lógica de animação dentro de suas folhas de estilo e evitar inchaço da sua página com bibliotecas JavaScript. Entretanto, se você estiver projetando uma interface de usuário complexas, cheia de floreios, ou se estiver desenvolvendo uma aplicação com informações de estado, sempre utilize uma biblioteca de animação para que as suas animações continuem com bom desempenho e seu fluxo de trabalho permaneça controlável. A Transit é uma biblioteca que faz um trabalho fantástico na gestão das transições brutas de CSS.

Animação em JavaScript

Ok, então o JavaScript pode levar vantagem quando se trata de desempenho. Mas quão mais rápido exatamente ele pode JavaScript ser? Bem, para começar, rápido o suficiente para construir uma intensa demonstração de animação em 3D que você normalmente só vê construída com WebGL. E rápido o suficiente para construir um teaser multimídia que você costuma ver construído com Flash ou After Effects. E também rápido o suficiente para construir um mundo virtual que você normalmente só ver construído com canvas.

Para comparar diretamente o desempenho das principais bibliotecas de animação, incluindo Transit (que usa transições CSS), tenha em mente a documentação do Velocity em VelocityJS.org.

A dúvida continua: como exatamente o JavaScript atinge seus altos níveis de desempenho? Abaixo está uma pequena lista do que as otimizações baseadas em animação JavaScript são capazes de realizar:

  • Sincronizar o DOM entre a pilha de toda a cadeia de animação para minimizar o layout thrashing.
  • Fazer cache nos valores de propriedade entre as chamadas encadeadas, a fim de minimizar a ocorrência de consulta de DOM (que é o calcanhar de Aquiles do desempenho de animação DOM).
  • Fazer cache de taxas de conversão de unidades  (por exemplo, px a%, em, etc.) por meio de elementos irmãos na mesma chamada.
  • Ignorar a atualização do estilo quando as atualizações seriam imperceptíveis visualmente.

 

Revendo o que aprendemos anteriormente sobre o layout thrashing, o Velocity.js aproveita essas melhores práticas para armazenar em cache os valores finais de uma animação para serem reutilizados como os valores de início da animação subsequente, evitando assim fazer um novo query do DOM para os valores iniciais do elemento:


1 $element
2     /* Slide the element down into view. */
3     .velocity({ opacity: 1, top: "50%" })
4     /* After a delay of 1000ms, slide the element out of view. */
5     .velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });

 

No exemplo acima, a segunda chamada do Velocity sabe que deve iniciar automaticamente com um valor de opacidade de 1 e um valor superior a 50%.

O navegador, em última instância, poderia realizar ele mesmo muitas dessas otimizações, mas fazer isso implicaria agressivamente em reduzir as maneiras pelas quais o código de animação poderia ser trabalhado pelo desenvolvedor. Assim, pela mesma razão que jQuery não usa RAF (veja acima), os navegadores nunca impõem otimizações que têm uma pequena chance de quebrar a especificação ou de se desviar do comportamento esperado.

Finalmente, vamos comparar as duas bibliotecas de animação JavaScript (Velocity.js e GSAP) uma contra a outra.

  • GSAP é uma plataforma de animação rápida e rica em recursos. A velocidade é uma ferramenta leve para melhorar drasticamente o desempenho de animação UI e o fluxo de trabalho.
  • GSAP exige uma taxa de licenciamento para vários tipos de empresas. Velocity é gratuito e open source por meio da licença MIT ultrapermissiva.
  • Em termos de performance, GSAP e Velocity são indistinguíveis em projetos do mundo real.

 

Minha recomendação é usar GSAP quando você necessitar de um controle preciso sobre over timing (por exemplo remapeamento, pausa/resumo/procurar), movimento (por exemplo, caminhos da Curva de Bezier), ou agrupamento/sequenciamento complexos. Essas características são fundamentais para o desenvolvimento do jogo e certos aplicativos de nicho, mas são menos comuns nas UI de web apps.

Velocity.js

Fazer referência ao rico conjunto de recursos do GSAP não quer dizer que o Velocity é fraca em recursos. Pelo contrário. Em apenas 7Kb quando compactados, Velocity não só reproduz todas as funcionalidades do $.animate() do jQuery, mas também empacota em animação colorida, transforms, loops, easings, animação classe e rolagem.

Em suma, Velocity é o melhor do que jQuery, jQuery UI e as transições CSS combinadas.

Além disso, do ponto de vista de conveniência, Velocity usa internamente o método do jQuery $.queue(), e assim interage perfeitamente com as funções $.animate(), $.fade() e $.delay do jQuery. E, uma vez que a sintaxe do Velocity é idêntica à do $.animate(), nenhum código da sua página precisa mudar.

Vamos dar uma rápida olhada em Velocity.js. Em um nível básico, Velocity funciona de forma idêntica ao $.animate():


1 $element
2     .delay(1000)
3     /* Use Velocity to animate the element's top property over a duration of 2000ms. */
4     .velocity({ top: "50%" }, 2000)
5     /* Use a standard jQuery method to fade the element out once Velocity is done animating top. */
6     .fadeOut(1000);

 

Em seu nível mais avançado, podem ser criados cenários de rolagem complexos com animações 3D – com apenas duas linhas simples de código:

1 $element
2     /* Scroll the browser to the top of this element over a duration of 1000ms. */
3     .velocity("scroll", 1000)
4     /* Then rotate the element around its Y axis by 360 degrees. */
5     .velocity({ rotateY: "360deg" }, 1000);

 

Conclusão

O objetivo do Velocity é permanecer líder no desempenho de animação do DOM. Dê uma olhada em VelocityJS.org para saber mais sobre o último.

Antes de terminarmos, lembre-se de que uma interface performática vai além de escolher a biblioteca de animação certa. O resto da sua página também deve ser otimizado. Saiba mais a partir destas palestras fantásticas do Google:

—–

Artigo de Julian Shapiro, publicado originalmente no iMasters.

 

DialHost
DialHost

Contamos com nosso DataCenter no Brasil de alta qualidade, estabilidade e confiança para hospedar seu site. Utilize poderosas ferramentas para otimizar o uso do seu serviço e seja sempre atendido de forma rápida e clara pelo nosso atendimento 24h.

ElasticSearch com PHP não é magia; é tecnologia!

ElasticSearch

ElasticSearch

Já não é de hoje que a ciência e a tecnologia desmistificaram a magia, então vamos entender um pouco o nosso assunto.

Há alguns anos começaram a aparecer em diversos sites grandes a famosa “busca viva”. De início, parecia ser algo mágico, mas depois foi descoberto que na maioria das vezes o cara por trás disso era o nosso querido ElasticSearch. Ele nada mais é do que um servidor de buscas escrito em Java e baseado no Apache Lucene. Hoje ele e usado por empresas como GitHub, Facebook, TheGuardian, Globo.com e outras…

Quando pensamos no uso do ElasticSearch com PHP não pensamos na busca viva, pois essa fica por conta do Javascript. No PHP nós trabalhamos com a indexação de dados. Uma vez que o ElasticSearch basicamente é um banco de dados, então nossos dados de busca precisam ser indexados nele de alguma maneira, e a maneira mais fácil de fazer isso é com um backend que “escute” suas ações e salve as no ElasticSearch.

Vamos usar o client oficial do ElasticSearch para PHP, o elasticsearch-php. Ele é bem completo, mas tem uma documentação bem incompleta (coisa comum no mundo de hoje).

Para nosso artigo, eu criei uma Factory simples e também um exemplo. Vamos por a mão na massa então.

Instalando o ElasticSearch

A primeira coisa a fazer e instalar o ElasticSearch. Depois, o elasticsearch-php usando o composer. Aqui esta a dependência:

1 {
2     "require": {
3         "elasticsearch/elasticsearch": "~1.0"
4     }
5 }

Após instalado, podem criar uma factory como a minha que está aqui.

Vamos entender essa Factory, então… Basicamente ela vai criar uma instância do elasticsearch client php para que possamos indexar, buscar e fazer update nos documents dentro do elasticsearch.

O elasticsearch se divide em index (índice), type (tipo) e depois document. O índice é usado, por exemplo, como o nome do meu banco de dados. Já o tipo seria minha tabela e o document são nossos dados.

Bom, a nossa factory se inicia com métodos privados que vamos usar logo e também um construtor na linha :16.

1 public function __construct($index){
2         $this->index = $index;
3         $this->client = new Elasticsearch\Client();
4
5         return $this;
6     }

Nosso construtor recebe um index obrigatoriamente, pois independente da nossa ação precisamos de um índice setado. Ela também inicia uma instância do nosso elasticsearch client. Também nessa factory, temos os getters e os setters para acessarmos nossas variáveis privadas.

Bom, então nos já temos o construtor, os getters e os setters. Agora precisamos criar os métodos para indexar e buscar dados do nosso elasticsearch.

Na linha :25 temos o método create:

1 public function create()
2     {
3         $params['index'] = $this->index;
4         $params['type'] = $this->type;
5         $params['body'] = $this->data;
6         
7         return $this->client->index($params);
8     }

Nosso método create tem como um índice um tipo e um body. Com esses dados setados, nós vamos chamar o index do elasticsearch cliente e passar esses parâmetros pra ele indexar – vejam que eu não passei um “id”, então ele vai criar um id automaticamente para nós.

Na pratica ficaria assim então:

1 $ef = new ElasticFactory('my_project');
2 $ef->setType('users');
3
4 $data = [
5     'name'=>'Waldemar Neto',
6     'age'=>24,
7     'email'=>'waldemarnt@outlook.com',
8     'born'=>'1990/01/23'