Aqui está o quarto artigo sobre as abstrações da computação, sob a perspectiva do desenvolvimento de sistemas.
As linguagens de desenvolvimento atendem as necessidades para construção de aplicações desde sistemas operacionais e jogos até complexos sistemas corporativos. Mas o poder delas tem o custo de aumentar a capacitação do desenvolvedor para que consiga explorar todos os recursos que oferecem.
Alguém que precise construir uma típica aplicação LoB necessita trabalhar com diferentes aspectos do sistema: ler e gravar dados, trabalhar com esses dados e construir formas do usuário interagir com ele. Para realizar esses aspectos, que são parte da arquitetura do sistema, uma linguagem como Java disponibiliza APIs (Application Program Interfaces) que são conjuntos de instruções para realizar tarefas específicas como manipular arquivos, enviar dados, etc.
Ter um grande número de APIs dá flexibilidade, o que seria um benefício pois aumenta as chances de atender por completo os requisitos necessários para as aplicações. Mas há um risco de se ter uma falta de padronização, tornando mais complexa a manutenção do código. Outro ponto é que pode-se adotar uma codificação que não seja a mais adequada para o cenário de uso da aplicação. Um exemplo é construir uma aplicação web sem se preocupar com a concorrência no uso de recursos, decorrente do acesso simultaneo por vários usuários.
Com frameworks de desenvolvimento, o que se busca é abstrair a complexidade do uso de linguagens de programação (com suas respectivas APIs) em uma forma onde o desenvolvedor não precise lidar com um número muito grande de instruções à sua disposição. Esses frameworks buscam diminuir a quantidade de linhas de código escritas, reduzindo o tempo de codificação e reduzindo a quantidade de instruções que podem falhar.
Desenvolver frameworks é por si só uma tarefa complexa, comparável à de construir sistemas em si. Como eles tem a característica do reuso, que é o aproveitamento deles em diferentes projetos de construção de sistemas, a confiabilidade deles é indispensável porque senão todas as aplicações que utilizaram esses frameworks podem falhar caso tenham algum erro de programação.
A disseminação de uso de códigos baseados em software livre trouxe como benefício o aparecimento de inúmeros frameworks. A colaboração inerente a essa prática de desenvolvimento faz com que centenas ou mesmo milhares de desenvolvedores testem na prática os frameworks e indiquem as melhorias a serem feitas, até mesmo corrigindo os eventuais bugs que descubram.
Há inúmeros exemplos de frameworks que seguem o modelo de software livre, como o Hibernate. Até mesmo para os desenvolvedores que trabalham com a plataforma .NET da Microsoft contam com o Codeplex para usar frameworks prontos para uso em seus projetos.
Este é o terceiro artigo da série sobre as abstrações encontradas na computação.
O artigo anterior apresentou um resumo sucinto do que é um sistema operacional, observando que ele oferece uma camada de abstração a ser utilizada por programas escritos via uso de linguagens de programação.
O conceito por trás de uma linguagem de programação está em expressar através de sentenças próximas da linguagem humana (mais precisamente a língua inglesa, adotada nos países pioneiros da computação – Inglaterra e EUA).
Através do trabalho pioneiro de Grace Hopper, criou-se o conceito de compilação, que resumidamente é transformar um conjunto de sentenças escritas dentro de regras específicas em uma codificação que computadores reconhecem. Com isso surgiram as primeiras linguagens de alto nível, como COBOL e FORTRAN.
A principal contribuição das linguagens de programação está no desenvolvimento da computação empresarial. Computadores deixaram de ser usados apenas para fins militares e começaram a entrar nas corporações, para resolver questões financeiras e de engenharia.
Com a evolução dos equipamentos, principalmente através da miniaturização dos componentes usados para monta-los (ver Tudo começa no processador), surgiu uma grande demanda por programadores e isso levou à busca de linguagens com um aprendizado mais rápido, como o BASIC). Também houve um desenvolvimento acadêmico, que levou a linguagens mais rigorosas na implementação de algoritmos, como o Pascal.
Uma linguagem que teve enorme´sucesso é a C. Ela foi usada no desenvolvimento do Unix, provavelmente o sistema operacional mais bem sucedido da história, considerando que é a base para modernos SO como o Linux e sua vertente móvel Android. Ela possui características que facilitam a codificação de instruções para usar diretamente recursos do hardware de um computador e a portabilidade, que é rodar um programa em diferentes modelos de processadores.
A programação orientada a objetos foi uma evolução notável no mundo das linguagens de programação. As sentenças que fazem parte de qualquer linguagem são usadas para descrever os chamados “objetos”, incluindo as possíveis ações que eles podem realizar. A analogia de objetos com entidades do mundo real é algo perceptível, conforme visto em diversos tutoriais sobre o assunto que se encontram na Internet.
Atualmente as principais linguagens de programação, como Java e as voltadas para a plataforma .NET são baseadas em orientação a objetos. Especificamente para esses casos, os compiladores não geram diretamente código de máquina e sim instruções que são executadas por máquinas virtuais. Isso é feito para garantir um maior independência entre programas e o ambiente onde são executados, visando maior segurança e independência do processador utilizado.
Linguagens de programação podem ser declarativas, quando possuem a qualidade de transparência referencial. Em um código que use expressões com essa característica, estas são trocadas por seu valor sem mudar o sentido do programa. Uma explicação mais detalhada para isso está além da proposta destes artigos, mas vale observar que a computação quântica eventualmente se baseará em linguagens dessa natureza.
Para se trabalhar com as modernas linguagens de programação, surgiram os frameworks de desenvolvimento, cuja finalidade é descrita no próximo artigo.
Este é o segundo artigo da série que discute as abstrações encontradas na computação.
No primeiro artigo foi dada uma pequena explicação sobre o uso do assembly para programar diretamente instruções em processadores. O uso dessa técnica é complexo, pois as instruções dessa linguagem não possuem uma analogia com aquelas que usamos para nos comunicarmos.
Antes de chegarmos às linguagens de programação de mais alto nível, precisamos entender o papel dos sistemas operacionais e o porquê de constituírem mais uma das abstrações com que lidamos na computação.
Bem resumidamente, um sistema operacional provê um conjunto de serviços para os programas executados em um computador. Consequentemente ao escrever programas, tarefas como leitura e gravação de dados, interação do usuário, exibição de informações entre outras são delegadas ao sistema operacional.
O sistema operacional é inicializado após o carregamento das instruções BIOS (Basic Input Output System). Esse sistema usa as funções BIOS para comunicar-se com as diferentes partes que compõe o computador (processador, memória, discos de armazenamento, dispositivos de entrada de dados, monitores, etc.) e controla a execução dos programas.
Todo sistema operacional, possui um mecanismo para carregar em memória programas a executar no computador. A ação para carregar programas normalmente é via solicitação de quem está usando o computador, mas eventualmente é feita via agendamento de tarefas a executar.
Quem utiliza um computador tem à disposição comandos que configuram a execução do sistema operacional e solicitam informações sobre o estado do computador. Por exemplo, ao listar os arquivos que se encontram em um dado local do computador, o que se tem em ultima instancia é o disparo de um comando do sistema operacional que faz essa tarefa.
Assim como o assembly varia de acordo com o processador ao qual se refere, existem diferentes sistemas operacionais e de maneira análoga eles também variam de acordo com o processador. A diferença é que pode haver mais de um sistema operacional para um mesmo tipo de processador.
A escolha de um sistema operacional é basicamente ligada aos gostos e preferências de quem usa o computador. Atualmente isso está mais claro nos dispositivos móveis (tablets e smartphones) onde ao escolher uma marca e modelo torna-se implícito o sistema operacional adotado.
Graças ao nível de abstração proporcionado pelo sistema operacional, o desenvolvimento de programas deixou de ser algo possível de ser feito apenas com assembly. Um próximo artigo tratará de linguagens de programação e o que faz com que o código delas seja executado por sistemas operacionais.
OBSERVAÇÃO: Este é o primeiro artigo de uma série que discute as diferentes abstrações que fazem parte da computação.
O uso da computação se tornou ubíquo graças à evolução da eletrônica, simbolizada pelos microprocessadores. Estes dispositivos, construídos a partir da combinação da química do silício com as chamadas terras raras, são circuitos eletrônicos miniaturizados que combinam um número muito grande de transistores para realizar operações binárias.
Transístores nada mais são que espécies de chaves que permitem ou não a passagem de sinais elétricos. Essa característica os torna bastante adequados para o desenvolvimento de dispositivos que trabalham com lógica binária. Nela as operações são baseadas através da combinação de dois estados - ligado e desligado – daí a praticidade de se usar algo que permita controlar mudanças nesses estados.
Os microprocessadores mais simples e especializados são desenhados para atender objetivos específicos. Assim não é necessário considerar a necessidade de programá-los, pois sua construção embute as funcionalidades que atenderão.
No entanto a maioria dos usos de microprocessadores está na construção de sistemas, quer seja uma central de controle de um veículo automotor da atualidade ou encapsulado num chip que vai num smartphone. Nesse caso um sistema é um conjunto de instruções para realização de diversas atividades, por exemplo, controlar a injeção de combustível ou apresentar informações numa rede social, nos respectivos exemplos de uso citados acima.
A forma primordial para programar um microprocessador, i.e., fazer com que realize tarefas específicas, está no uso do assembly. Este é um tipo de programação voltado especificamente para as instruções de um determinado processador. De qualquer forma, ele já é considerado como uma linguagem de programação, mas com propósito bastante específico e requerendo o conhecimento de como o processador funciona.
O assembly é um primeiro nível de abstração, pois cria instruções que possibilitam um processador realizar operações com finalidades por vezes bastante distintas. Seu uso não é fácil e para se ter uma analogia próxima a como usá-lo, pode-se voltar no tempo das calculadoras de bolso programáveis, onde uma combinação de sequencias de teclas criavam instruções próximas às que se encontram no assembly (por exemplo, o famoso “goto”).
As dificuldades para se programar com assembly remetem ao uso de uma camada de abstração presente em todo equipamento que roda algum tipo de programa: o Sistema Operacional, que será tratado no próximo artigo.
Com o lançamento do Windows 8 pela Microsoft, está explicito que o futuro da computação está no uso de dispositivos cada vez mais pervasivos. Hoje poucos dão conta do fato que um iPad tem mais poder de processamento do que um computador desktop da década de 90.
O que faz com que smartphones e tablets ganhem a preferência dos usuários está na experiência de uso. Qualquer um que tirou fotos num equipamento desses para postar na Internet acha muito mais divertida essa experiência do que aquela onde era necessário descarregar as fotos num computador para fazer o upload.
Essa facilidade encontrada nos equipamentos da moda se deve principalmente ao reconhecimento do fato que o usuário busca por coisas simples e intuitivas para usar no dia a dia. Isso traz ao desenvolvedor o desafio de escolher o modelo mais adequado para atender a esses anseios.
As plataformas voltadas para os dispositivos que se enquadram naquilo que é denominado BYOD (bring your own device) – iOS, Android e agora Windows RT – tem como uma das bases de apoio o desenvolvimento de aplicações (agora conhecidas simplesmente como Apps) que exploram os seus recursos. Por exemplo, um GPS permite detectar onde estamos, o que é explorado por apps que fazem pesquisas sensíveis à localização.
Outro modelo de construção de é baseado em web applications. Neste a aplicação roda em servidores centralizados e são acessadas via navegadores que enviam requisições e recebem de volta respostas na forma de páginas renderizadas em HTML.
O modelo web é percebido como vantajoso para se disponibilizar um produto para um público amplo. No entanto há limitações devido a uma certa incompatibilidade na aplicação dos padrões HTML por navegadores e existe uma dificuldade referente à manutenção de estado. De uma forma simplificada, faz- se necessário escrever código apenas para guardar as informações referentes a uma sessão (tempo entre o início e o fim do uso da aplicação).
Se apps além de não terem problemas de compatibilidade conseguem usar os recursos do dispositivo, elas lidam com as diferenças entre plataformas. A rigor, para se ter uma rodando num iPhone, tablet com Android ou em um ultrabook com Windows 8, precisa-se escrever códigos específicos por sistema operacional.
Hoje não há uma solução clara para se ter uma única base de código que rode em qualquer dispositivos. Web applications são o que se consegue chegar mais perto dessa experiência, mas não conseguem explorar por completo os recursos desses equipamentos. Apps podem ser mais fáceis de codificar, porém demandam esforços para cada uma das plataformas que se deseje atender.
O Windows 8 vislumbra uma possibilidade que se for evoluída pode minimizar a questão de se ter construções distintas por plataforma. Nele é possível codificar com JavaScript e HTML5, de forma próxima à adotada em web applications. Ainda está longe do ideal, porque há extensões proprietárias desse SO para usar os recursos do dispositivo, mas numa evolução do HTML quem sabe seria possível usar tudo que um computador oferece?
O desenvolvimento de software ainda é percebido como algo empírico, mesmo com avanços como ferramentas IDE (Integrated Development Environment) e aplicação de técnicas como Scrum. Uma das possíveis causas dessa sensação estaria no fato de que programação ainda é muito influenciada pelo estilo de quem a executa.
Uma possibilidade para se buscar maior previsibilidade na codificação é o uso de geradores de código. Com eles abre-se a possibilidade de aplicar experiências de outros ramos de engenharia àquela que é denominada engenharia de software (ou de computação). Por exemplo, através do scaffolding é possível criar código de forma padronizada, a partir de uma especificação.
Hoje já existem recursos como o Entity Data Model que constroem toda um mapeamento objeto-relacional a partir de DSL (Domain Specific Language) que representa visualmente um domínio de problema. Entretanto apesar de esforços como MDA, ainda há oportunidades de evolução para se chegar ao ponto de que construir um software será equivalente à fazer um prédio ou montar um carro, usando plantas e outros desenhos técnicos.
O ideal era que se conseguisse um DSL capaz não apenas de refletir os requisitos a serem atendidos por um software como até mesmo os recursos necessários para executá-lo. Daí poderia-se chegar num nível de abstração alto o suficiente para que apenas os desenvolvedores dos geradores de código necessitassem conhecer linguagens de programação. E o uso de uma linguagem ou outra seria em função dos recursos de infraestrutura disponíveis e não da capacitação dos analistas envolvidos no desenvolvimento do software.
No desenvolvimento de softwares empresariais - aqueles voltados para execução de processos nas organizações - o uso de SGBDR (Sistemas de Gerenciamento de Banco de Dados Relacional) como meio para armazenar informações é regra corrente. Vale observar que há outras abordagens para a questão de como guardar informações (como NoSql), porém o modelo relacional está bastante consolidado e possui uma ampla base de conhecimento sobre como aplica-lo.
Considerando que o sistema a ser desenvolvido usará banco de dados, quem desenhar a arquitetura da solução precisará decidir sobre a forma como as informações serão lidas e atualizadas. Para auxiliar na escolha, devem-se pesar os prós e contras de cada uma das alternativas que forem analisadas.
Assumindo que a maior parte do desenvolvimento nas empresas está baseado em linguagens orientadas a objeto (Java e as suportadas pelo .NET Framework), o problema a resolver está na transformação de dados vindos do modelo relacional para representação de objetos (também conhecido como Impedance Mismatch). De uma forma bastante simplificada, o modelo relacional é anterior à orientação a objetos e consequentemente não suporta técnicas como o uso de interfaces e herança entre objetos.
Um dos melhores artigos que já li na internet – The Vietnam of Computer Science – trata especificamente desse assunto. Há uma introdução falando da Guerra do Vietnam, que é a analogia que o autor faz com as restrições ao uso de ferramentas de ORM (Object-Relational Mapping), que oferecem mecanismos para transformar dados relacionais em objetos. Segundo o artigo, sucessos iniciais que se atingem ao usar ORM acabam levando a um ponto onde não se consegue ter mais retorno, mesmo aumentando o investimento no uso da ferramenta. Já se passaram anos de sua publicação e ele continua atual, mesmo com a evolução de frameworks como o Hibernate e o Entity Framework.
Sem tomar parte na polêmica, um ponto comum nas diversas abordagens para ORM está no uso de queries parametrizadas. Na prática isso significa que as instruções SQL são montadas na aplicação que usa o banco de dados. Normalmente a ferramenta gera essas instruções de forma transparente ao programador e as submete ao banco. Consequentemente estas dão grande produtividade no desenvolvimento de sistemas baseados em SGBDR.
Outra maneira de ler e gravar informações em banco de dados está no uso de stored procedures (SPs) Elas encapsulam as instruções SQL em código que é executado no próprio banco de dados. Hoje algumas das ferramentas de ORM (como as citadas acima) dão suporte ao uso de SPs, mas é necessário levar em conta que as procedures demandam uma codificação adicional. E deve-se lembrar de que cada SGBDR possui a sua própria sintaxe para essa codificação.
Dois pontos a destacar em favor das stored procedures seriam desempenho e segurança. Os SGBDR validam o código de uma SP ao gravá-la, assim em tempo de execução ele já executa o processamento, dispensando a necessidade de conferir se a instrução SQL está correta. Do ponto de vista da segurança, SPs diminuem a superfície de ataque a um SGBDR. Para acessar os dados num SGBDR é feita uma conexão usando uma conta de usuário do banco. Ao se dar os acessos necessários para executar instruções SQL, pode-se expor o banco de dados a vulnerabilidades do tipo SQL Injection. Quando são usadas SPs, esse tipo de vulnerabilidade é bastante mitigado, por exemplo, dando acesso somente para execução destas à conta de usuário do banco.
Para escolher qual a forma com que as informações serão recuperadas de SGBDRs, considere que há três requisitos para ponderar: produtividade, desempenho e segurança. Como eu particularmente gosto de usar SPs, uma sugestão para aumentar a produtividade no uso delas está na extensão de geradores de código normalmente associados a ferramentas de ORM para que construam as SPs.
Essa pergunta é uma provocação. Para aqueles que como eu trabalham com TI há tempos, a resposta é contraditória. Se por um lado a Internet e mais recentemente a computação móvel abriu um mundo de possibilidades, está cada vez mais complexa a construção de sistemas para atenderem as demandas do mercado.
Quem usa um smartphone ou tablet não se dá conta que tem nas mãos um computador com capacidade de processamento maior que PCs da época que comecei minha carreira profissional (ver The Future of the Smartphone). Entretanto para quem desenvolve uma aplicação corporativa para esses aparelhos tem um desafio que também é maior em relação ao que se fazia no tempo do DOS.
Fiz a ressalva de que a complexidade de desenvolvimento de aplicações corporativas aumentou porque a construção de aplicativos que exploram recursos do dispositivo (jogos e manipulação de mídias, por exemplo) é relativamente simples, como era de se esperar. A questão é que para as empresas realmente ganharem dinheiro com os usuários de dispositivos móveis elas necessitam oferecer serviços.
Um serviço na perspectiva atual, onde a computação se encontra em todos os lugares, seria uma forma de ganhar dinheiro com o atendimento das necessidades e desejos dos clientes. Para isso ela deve coletar, processar e extrair resultados de dados que trafegam a taxas cada vez mais altas e com um volume cada vez maior de atualizações. Isso leva a um conjunto de requisitos como disponibilidade e segurança que são difíceis de realizar. Por exemplo, o Facebook se destacou da concorrência por garantir um downtime muito baixo, graças a uma combinação de programação mais equipamentos capazes de prover essa confiabilidade.
Segurança é particularmente um aspecto difícil de implementar. Por mais que os fornecedores de sistemas operacionais e demais softwares usados para execução de sistemas trabalhem, sempre há brechas sendo descobertas. E esse aspecto é relegado tanto na formação profissional como na hora em que se negocia os recursos para desenvolvimento de aplicações.
O desenvolvimento de sistemas não evoluiu como esperado porque a computação atual demanda um conjunto de computadores rodando softwares especializados para ter no ar serviços que interessem aos usuários. Cada um desses computadores – servidores de banco de dados, application servers e dispositivos dos usuários (para ficar apenas nos mais óbvios) demandam ferramentas de programação específicas. Tudo isso tem que funcionar de forma coordenada e em caso de mudanças todos precisam atualizar-se de forma sincronizada. Ou seja, há muitos pontos de falha e lei de Murphy garante que estas acontecem…
Para simplificar a TI, deve-se começar com esse questionamento. Ao se buscar respostas para essa essa pergunta, ve-se aspectos que no dia a dia não estão claros para os profisisonais da área, mesmo os mais experientes. Um dos jargões da moda – “pensar fora da caixa” – realmente faz sentido nesta situação.
Quando escolhi a hospedagem do meu domínio (há quase uma década atrás!) optei por usar servidores Windows. O motivo principal era porque estava me especializando nessa plataforma e consequentemente me sentia mais confortável com ela, caso desejasse fazer alguma customizãção.
De lá para cá o modelo de blog se popularizou e muitas opções de software livre tornaram-se disponíveis. Com elas praticamente não mais o que customizar, pois há toda uma comunidade de desenvolvedores criando novos itens que dão possibilidades ilimitadas quanto a aparência e funcionalidade para um site de conteúdo.
Mas para manter-me fiel a hospedagem escolhida (em servidor Windows), estou adotando uma ferramenta de código aberto que é escrita em .NET Framework. Afinal o legal da Internet é a liberdade de escolha e todas as opções à disposição tem boa qualidade.
Ah, se quiser saber mais sobre o BlogEngine.NET, acesse o site do projeto no Codeplex, que é site de software livre apoiado pela Microsoft. Pois é, talvez esteja na hora de mudar seus conceitos…