Arquitetura de um programa ou sistema computacional é um conjunto de componentes e conexões entre eles. A arquitetura define elementos de software, incorpora informações sobre como os elementos se relacionaram um com os outros. Isto também significa que a arquitetura omite certas informações sobre elementos que não pertencem a sua interação. Dessa forma, uma arquitetura é em primeiro lugar uma abstração de um sistema que suprime detalhes de elementos que não afetam a maneira como eles usam, são usados por, ou interagem com outros elementos. Na arquitetura de um software definimos a plataforma e como os componentes do sistema irão se organizar. Evidentemente que os requisitos ainda são importantes, pois, por exemplo, um sistema Web deverá influenciar na arquitetura, ou então uma aplicação que será de tempo real.
Mesmo não possuindo uma definição consensual, afinal diversos livros, artigos ou autores definem a arquitetura da sua forma, a arquitetura de software de um sistema computacional pode ser definida como a suas estruturas, que são compostas de elementos de software, de propriedades externamente visíveis desses componentes, e do relacionamento entre eles.
A arquitetura de um sistema é feita por profissionais com bastante experiência em diferentes organizações, vastos tipos de projetos, conhecimentos de diversos padrões de arquitetura, etc. A troca de experiência com outros programadores ou profissionais da área também são extremamente importantes quando estamos definindo a arquitetura de um software.
A arquitetura de um sistema possui elementos utilitários, elementos de interação, elementos que fazem parte do domínio do problema, elementos de conexão, elementos de persistência, etc. Assim, na arquitetura sempre definimos os seus elementos que precisarão ser utilizados e como eles se conectam.
Entre as questões de desenvolvimento pertinentes na definição da arquitetura temos que considerar também qual plataforma iremos utilizar, quais as formas de persistência que serão adotada, como faremos para integrar componentes, como será a implantação no cliente, se teremos um jar único ou diversas dlls, etc. Também devemos documentar a arquitetura (elementos e suas conexões), requisitos de qualidades priorizados, requisitos e razões da arquitetura, mapeamento das tecnologias utilizadas, etc. Essa documentação é importante para que possamos também explicar porque aquela arquitetura foi escolhida, isso ajuda no entendimento da arquitetura e pode abrir para discussões sobre melhorias ou alterações na arquitetura.
Alguns padrões arquiteturais já foram pensados por profissionais experientes para resolver problemas que ocorrem frequentemente nos projetos. Apesar de existirem diversos padrões arquiteturais também podemos combinar esses diferentes estilos de arquiteturas, pois podem atender melhor às nossas necessidades de projetos específicos. Muitas organizações combinam estilos arquiteturais para resolver seus problemas. Por isso é sempre interessante entender as características básicas de cada um dos estilos e escolher ou combinar aqueles que atendem melhor às necessidades de um projeto específico. Entre as arquiteturas existentes temos: Dados compartilhados, Máquina virtual, Três camadas, Camadas, MVC, Pipe-andFilter e muitos outros.
No restante do artigo veremos mais sobre o padrão arquitetural Pipe-And-Filter que é muito utilizado pelos sistemas operacionais como o Shell do Linux.
Padrão Pipe-And-Filter
Nas arquiteturas Pipe-and-Filter, os componentes computacionais são os filtros que agem como transdutores que recebem uma entrada, transformam de acordo com um ou mais algoritmos, e então geram uma saída para um canal de comunicação. Esses condutores de entradas e saídas são chamados de pipes.
Uma arquitetura pipe-and-filter é linear, como ilustra a imagem abaixo:
Figura 1: Exemplo ilustrativo de uma arquitetura pipe-and-filter.
Os filtros devem ser componentes independentes. Também podemos juntar os diferentes filtros em diferentes posições afim de termos um resultado diferente. Como todos os componentes usam a mesma interface eles podem ser compostos em diferentes soluções conectando os componentes em diferentes pipes. Podemos também adicionar novos filtros, omitir alguns já existentes ou, como já foi dito, encadear de forma diferentes dentro de uma nova sequencia, sem precisarmos alterar os filtros.
Um exemplo bastante clássico da arquitetura pipe-and-filter é o Sheel do Linux, onde existe um número de pequenos programas que tipicamente fazem uma única coisa e podem ser encadeados juntos usando o mecanismo de Pipe do Linux. Basicamente o pipe do Unix é uma das maneiras que o sistema operacional utiliza para comunicação entre processos tratando-se de uma ferramenta muito poderosa. O exemplo abaixo demonstra o uso dessa ferramenta no Linux:
$ ls | grep b | sort -r | tee arquivo.out | wc -l
No exemplo acima o comando "ls" lista o conteúdo do diretório, no entanto devido ao Pipe ele não envia o resultado para tela e sim para o comando "greb b", que por sua vez filtra os nomes de arquivos que contém a letra "b". O segundo Pipe envia a saída do comando "grep b" para "sort -r", que classifica os nomes em ordem crescente. A saída do comando "sort -r" é passada para o comando "tee", que divide os dados em dois, como uma conexão em T, fazendo com que as informações processadas por "sort -r" sejam escritas no arquivo "arquivo.out", e por fim o comando "wc -l" conta as linhas do arquivo "arquivo.out". Portanto, o resultado será a quantidade de arquivos que contém a letra b impressa na tela e o nomes desses arquivos em "arquivo.out".
Este exemplo tem todas as características do padrão de arquitetura pipe-and-filter, ou seja, componentes com independência computacional que executam uma transformação nas informações da entrada e condutores de comunicação que transmitem a informação da saída de um componente para a entrada do próximo.
Portanto, usamos a arquitetura Pipe-and-Filter para dividir uma grande tarefa de processamento em uma sequencia de pequenos e independentes passos de processamento (Filters) que são conectados por canais (Pipes). Cada filtro expõe uma simples interface que recebe mensagens dos pipes de entrada, processam a mensagem e publicam o resultado para o pipe de saída. Os Pipes conectam um filtro ao próximo enviando mensagens de saída de um filtro para o próximo filtro.
Implementação do Pipe-And-Filter
No exemplo abaixo temos uma demonstração de um Filtro bastante simples onde ele lê de um Pipe, que foi recebido por parâmetro, as informações de entrada, manipula essas informações de alguma forma, e por fim seta na saída do Pipe o resultado final.
Listagem 1: Exemplo de um filtro.
while (true) { Element x = inputPipe.read (...); outputPipe.write (f (x)) ; }Neste exemplo a informação não está sendo manipulada, mas poderíamos fazer qualquer tipo de manipulação em cima do que esta sendo recebido do Pipe.
Muitos sistemas operacionais de linha de comando fornecem uma entrada e uma saída como parâmetro para cada programa.
Outros padrões de projetos como o Decorator utilizam os princípios do Pipe-And-Filter. Um exemplo clássico deste uso são as bibliotecas do Java InputStream, OutputStream, FileInputStream, FileOutputStream, FilterInputStream, FilterOutputStream, etc.
Os programadores precisam compor esses streams dinamicamente como abaixo:
myStringBuffer = new StringBuffer ("Este é um exemplo de string a ser lida");
FilterInputStream myStream = new LineNumberInputStream (new BufferedInputStream (new StringBufferInputStream(myStringBuffer))) ;
myStream.read();
myStream.line();
Vale ressaltar que existem duas formas de criar filtros. Um são os Filtros Ativos (Active Filter) que direcionam o fluxo das informações nos Pipes. E temos também os Filtros Passivos (Passive Filter) que são direcionados pelo fluxo de informação (entrada/saida) nos pipes. Na arquitetura Pipe-and-Filter temos que ter pelo menos um filtro ativo que pode ser o ambiente do sistema (por exemplo, entradas do usuário).
Coleções podem ser usadas para armazenar as informações passadas através dos pipes, também podemos usar arquivos, arrays, árvores, dicionários, etc.
Neste artigo, vimos o que é a arquitetura de software, quais são seus elementos, o que são as suas interações. Também vimos mais detalhadamente o padrão de arquitetura de software Pipe-and-Filter que é muito utilizado inclusive pelo Shell do Linux. Analisamos os seus elementos e como eles interagem entre sí. Entre as vantagens de usar o padrão Pipe-And-Filter podemos concluir que pequenos passos de processamento são mais fáceis de reutilizar em diferentes contextos do que grandes componentes, além disso, diferentes fontes de entrada de dados existem e precisamos apresentar e armazenar o resultado final de diferentes formas, além de outras vantagens que o padrão oferece.
Bibliografia
- John Dooley. Software Development and Professional Practice. Apress, 2011.
- Pipes and Filters
- Pipes and Filters