18
Framework: você REALMENTE sabe o que é isto?
Recentemente retomei meu contato com o Angular: está sendo uma experiência muito enriquecedora pois a imagem negativa que tinha da ferramenta recebeu um significativo upgrade. Li uns 90% de toda a sua documentação oficial (incluindo este livro incrível) e o que posso dizer é: que framework maravilhoso! Mas o que é um framework?
Um post atrasado no mínimo uma década
Meu primeiro contato com o termo "framework" foi quando comecei a aprender Java. Naquela época (1996, 97, bem mais em 2001, 2002) muito se falava a respeito de frameworks, especialmente após o lançamento do Struts. Confesso que não conseguia entender direito o significado do termo, e não é pra menos. Vejam o que me diziam na época a respeito:
- "Framework é uma aplicação pré-pronta".
- "É um conjunto de códigos que você pode reaproveitar em seus projetos e que te poupa muito tempo".
- "É uma solução pré-pronta para problemas recorrentes".
Mas e uma "biblioteca"? Ela também não é um conjunto de códigos pré-prontos que eu posso reutilizar?
Se existem estas duas palavras: biblioteca e framework, seriam estas sinônimos? Se sim, por que veio depois este termo, framework? Será que eu o descobri posteriormente e que o mesmo sempre existiu (sim)?
Não são sinônimos e hoje, após trabalhar com diversos sistemas que não foram escritos por mim posso dizer algo com plena segurança: boa parte do software que temos dificuldade em manter hoje é mal escrito devido à má compreensão do termo "framework".
O que é uma biblioteca?
Primeiro é importante saber aquilo com o qual as pessoas costumam confundir o framework: a biblioteca. Esta sim é um conjunto de códigos reutilizáveis que vão se apresentar sob a forma de funções, classes, módulos...
Seu código invoca sua execução e como consequência o trabalho pesado é realizado para você. Vamos a alguns exemplos práticos. O código a seguir, escrito em Java (extraído daqui) usa as bibliotecas de compressão da linguagem para gerar um arquivo zip.
package com.mkyong.zip;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class App
{
public static void main( String[] args )
{
byte[] buffer = new byte[1024];
try{
FileOutputStream fos = new FileOutputStream("C:\\MyFile.zip");
ZipOutputStream zos = new ZipOutputStream(fos);
ZipEntry ze= new ZipEntry("spy.log");
zos.putNextEntry(ze);
FileInputStream in = new FileInputStream("C:\\spy.log");
int len;
while ((len = in.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
in.close();
zos.closeEntry();
//remember close it
zos.close();
System.out.println("Done");
}catch(IOException ex){
ex.printStackTrace();
}
}
}
FileOutputStream, ZipOutputStream, ZipEntry, FIleInputStream são classes disponibilizadas pela biblioteca padrão do Java SE. Observe algo interessante: eu simplesmente as importo e, na sequência, meu código define quando estas serão executadas e como.
É possível criar novas sub-classes baseadas nestas que acabei de mencionar, entretanto alguns aspectos se mantém:
- Estou usando código pré-existente que não foi escrito por mim.
- Meu código está definindo quando usarei estas classes/funções.
Outro exemplo bem simples podemos ver no código C escrito a seguir:
#include <stdio.h>
int main(void) {
printf("Aqui de buenas usando a função printf");
return 0;
}
Agora ao invés de classes estou importando a biblioteca de I/O clássica da linguagem C e, na sequência, reaproveito a função printf, disponibilizada por esta. E novamente o fluxo do meu programa é definido inteiramente por mim: imprima este texto inútil e na sequência retorne 0.
Então vamos à definição Kiconiana de uma biblioteca: código escrito por terceiros focando o reuso e cuja ordem de execução é definida inteiramente por você.
E o framework, o que é?
É uma aplicação semi pronta? Sim, mas esta é uma definição muito vazia (bibliotecas também podem ser consideradas como tal). É um conjunto de código que posso reaproveitar? Óbvio, mas também é uma descrição incompleta e ainda gera confusão em relação ao termo biblioteca. É uma solução para um problema recorrente? Também. Então, qual a diferença?
O significado fica mais claro quando pensamos na tradução do termo para o português: framework pode ser traduzido como moldura, armação. Imagens surgem em minha mente:
Armação, vigamento e moldura: o que tem em comum? Me lembro de ficar um bom tempo me questionando a respeito. Framework: por que este termo? O que isto tem a ver com moldura???
A moldura existe para suportar a gravura/imagem, o vigamento para que as telhas ou piso possam ser incluídos acima de si e a armação dos óculos fornece a estrutura necessária para que as lentes possam estar bem posicionadas em relação à nossa face. E o framework?
Enquanto no caso da biblioteca o ciclo de vida do nosso código é de nossa responsabilidade, ao lidamos com o framework o contrário ocorre: nós fornecemos a gravura para a moldura, o piso para o vigamento e as lentes para a armação.
Não somos nós que definimos quando nosso código executa: quem define isto é o framework. Se você já trabalhou com Spring ou Angular talvez isto lhe soe familiar. Lá vai: frameworks nada mais são que a aplicação mais básica do conceito de inversão de controle. A propósito, o Martin Fowler tem um texto muito bom sobre isto.
Vamos a um exemplo bem simples baseado em Angular? Imagine que eu queira escrever um componente: então escrevo algo similar ao código a seguir usando TypeScript:
@Component({
selector: 'login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
credentials: Credentials;
constructor(private usuarioService: UsuarioService) {
this.credentials = new Credentials(null,null);
}
ngOnInit() {
}
entrar() {
this.usuarioService.auth(this.credentials);
}
}
Aquela função ali, ngOnInit, não sou eu quem a invoco: é o Angular. Repare: apenas forneço os músculos para este esqueleto. Eu sei que aquele método será invocado, mas não por mim. Em momento algum escrevo código como o exemplo a seguir:
usuarioService = new UsuarioService();
login = new LoginComponent(usuarioService);
login.ngOnInit();
O código acima é aquele que tipicamente escrevemos ao lidar com bibliotecas. Mas o que ganhamos com esta delegação? Será que minha função realmente será chamada?
Lembra quando mencionei que sim, frameworks são soluções para problemas que ocorrem com frequência e que também são aplicações pré-prontas, mas que vão além? Pois bem: o problema que todo framework resolve é essencialmente o ciclo de vida do nosso código.
Autores de frameworks se preocupam com a vida de nossos objetos/funções: quando devem ser criados, usados e destruídos. E isto de uma forma ótima, garantindo que nosso trabalho consista apenas em fornecer o conteúdo necessário para que nossas necessidades de negócio sejam atendidas.
Vamos a mais um exemplo: desta vez usando a API Servlet do Java. Um Servlet pode ter diversas formas, a mais comum é o HTTP que tem como objetivo receber requisições pelo protocolo (HTTP), executar nossas regras e, finalmente, retornar conteúdo ao usuário final da aplicação. Vamos implementar um servlet?
package br.com.kicosoft;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo1 extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException{
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("
<h1>Sou um servlet feliz!</h1>
");
out.println("</body>");
out.println("</html>");
}
}
Primeiro eu escrevo a minha classe, que extenderá uma outra chamada HttpServlet. Meu papel consiste apenas em sobrescrever os métodos HTTP que me interessam, no caso, doGet, que corresponde ao método GET do protocolo. Minha função define o que deverá ser retornado ao usuário: o conteúdo HTML que você vê em meu código.
Repare na riqueza: não preciso me preocupar em implementar o protocolo HTTP, ou mesmo um algoritmo de espera (ocupada ou não) de requisições que chegam ao meu servidor. Apenas forneço ao meu framework o código que desejo ser executado.
Então, como posso definir um framework à moda Kiconiana? É o maestro responsável por orquestrar seu código visando resolver um ou mais problemas específicos .
O problema específico pode ser a construção de uma aplicação web, uma integração (pense em Apache Camel implementando os Enterprise Integration Patterns), um jogo (pense em um engine como o Unity). Seu papel é portanto apenas fornecer o código que será executado pelo framework. O que nos leva à grande questão.
Frameworks são burocráticos
Lembra no início deste post quando disse que boa parte do código mal escrito que vemos hoje tem como principal problema o fato de haver uma má compreensão a respeito do termo "framework"? O problema nasce quando ignora-se este fato: frameworks são burocráticos.
O termo burocracia tem uma conotação bastante negativa mas é um fato necessário para que haja ordem, para que se consiga formalizar a identificação de elementos.
(Sabia que a escrita nasce de uma necessidade burocrática? Dica: pesquise sobre a origem da escrita cuneiforme e os sumérios.
Lembre: burocracia é uma coisa, burrocracia, outra.)
Imagine que você irá escrever um framework: seu papel é orquestrar a execução de código de terceiros, certo? Como você o identifica este código e o papel a ser desempenhado por este? Alguma formalização precisa ser posta em prática, o que ocorrerá através da definição de padrões, tais como:
- Presença de arquivos de configuração que identifique os artefatos. Lembra do XML no Java ou Spring?
- Inclusão de anotações em nosso código. Olha ali o meu exemplo usando Angular e a anotação @Component.
- A implementação de uma interface ou extensão de classes (lembra do meu Servlet?).
- Padronizações no código como, por exemplo, a definição de nomes padrão para eventos de ciclo de vida dos objetos (já viu Tapestry?).
- Qualquer outro dispositivo que sirva para marcar o papel desempenhado pelo código a ser orquestrado.
O principal problema que encontro em código legado é justamente este: quem o evoluiu não se esforçou o suficiente para conhecer o framework adotado na escrita do projeto. E ainda pior: usou esta "moldura" como se fosse uma biblioteca, desrespeitando completamente o ciclo de vida proposto por esta.
Sabe aquele sujeito que sempre "dá um jeitinho" na burocracia cortando caminho? É o programador que usa o framework como biblioteca e ferra a gente no futuro.
Frameworks são limitados
Assim como o termo burocracia, a palavra limitada também tem uma conotação negativa em nossa sociedade, mas infelizmente é verdade: frameworks são limitados (e tem de ser). Aqui nasce outro problema que encontro em muito código legado.
Lembra que na definição Kiconiana menciono o fato do framework visar resolver um ou mais problemas específicos? Pois é: meu framework web favorito é o Grails, mas não o uso para implementar integrações.
Apesar de 90% do código que vejo ser escrito hoje visar resolver o problema da construção de aplicações web (ou web services, micro-serviços), este não é o único problema computacional a ser resolvido.
Talvez você precise escrever uma automação residencial (HomeKit da Apple), ou quem sabe uma integração (Apache Camel), talvez seja a implementação de um jogo de tiro em primeira pessoa (Cry Engine). Posso ficar um bom tempo aqui com exemplos.
O importante é: muitos dos problemas que encontro são resultantes da má escolha do framework para aplicações distintas do seu propósito original. Isto deve ser levado em consideração. Nem tudo é uma aplicação web, e desconfio de gente que diz ser capaz de resolver qualquer problema usando um único framework.
O que a armação, a moldura e o vigamento tem em comum? Eles limitam a forma do que irão orquestrar.
Concluindo
Estudando o Angular (6) fiquei maravilhado com o modo como ele organiza meu código fonte e orquestra a sua execução, o que me fez voltar ao conceito de framework e, na sequência, finalmente liberar este post.
Resumindo, digo que você deve evitar as seguintes armadilhas ao escrever seu código:
- Saber se precisa realmente de um framework ou de uma biblioteca (em 95% das vezes usará os dois no mesmo projeto).
- Saber como usar uma biblioteca e, principalmente, um framework.
- Buscar entender as formalizações definidas pelos autores do seu framework (poderíamos falar o mesmo a respeito de bibliotecas: pense em OpenGL).
- Entender para qual fim o framework foi escrito e usá-lo apenas para este fim.
- Resumindo o resumo: você deve respeitar o framework.
Infelizmente (ou felizmente?) não se discute com tanta ênfase hoje o significado dos termos biblioteca e framework. Talvez pelo fato de terem se tornado ubíquos para nós com o passar do tempo, o que mostra termos evoluído (e muito) de lá pra cá.
Mais uma vez o Wittgenstein estava certo: a maior parte dos problemas surge da má compreensão da linguagem.
18