Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas

Princípios da Engenharia de Software: Impacto na Qualidade, Notas de estudo de Informática

Este documento discute dois princípios fundamentais da engenharia de software e de seu impacto na qualidade do software produzido. Aborda conceitos como coesão, independência, abstração, portabilidade, manutenção, tipos de dados e documentação. Além disso, oferece referências para obras relacionadas.

Tipologia: Notas de estudo

2010

Compartilhado em 20/07/2010

julio-cesar-vw8
julio-cesar-vw8 🇧🇷

5 documentos

1 / 29

Documentos relacionados


Pré-visualização parcial do texto

Baixe Princípios da Engenharia de Software: Impacto na Qualidade e outras Notas de estudo em PDF para Informática, somente na Docsity! última actualização em: 5-nov-96 Dos Princípios da Engenharia de Software e do seu Impacto na Qualidade: Conceitos, Técnicas e Recomendações para a Codificação Fernando Brito e Abreu Gonçalo Pereira INESC Resumo Este documento propõe um enquadramento de referência, sob a forma de um conjunto de princípios, que permite justificar a adopção de certas técnicas e ferramentas. Esse conjunto resulta de uma reflexão sobre boas práticas de Engenharia de Software. A forma como esses princípios contribuem para a Qualidade do software é descrita. Baseado no enquadramento referido é apresentado um conjunto de recomendações para a codificação que permitem definir estratégias de reificação dos princípios. A abordagem é, tanto quanto possível, independente de linguagens de programação, ambientes de desenvolvimento ou de plataformas computacionais. Índice 1. Introdução 2 1.1 Princípios de Engenharia de Software 2 1.2 Caracterização da Qualidade do Software 3 2. Princípio da Modularização 5 2.1 Introdução 5 2.2 Influência na Qualidade do Software 5 2.3 Heurísticas e Recomendações 5 2.4 Automatização do processo de construção de versões executáveis 6 3. Princípio da Abstracção 8 3.1 Introdução 8 3.2 Influência na Qualidade do Software 9 3.3 Heurísticas e Recomendações 9 4. Princípio da Ocultação 11 4.1 Introdução 11 4.2 Influência na Qualidade do Software 11 4.3 Heurísticas e Recomendações 11 5. Princípio da Coesão 12 5.1 Introdução 12 5.2 Influência na Qualidade do Software 13 5.3 Heurísticas e Recomendações 13 6. Princípio da Independência 14 6.1 Introdução 14 6.2 Influência na Qualidade do Software 15 6.3 Heurísticas e Recomendações 15 7. Princípio da Uniformização 16 7.1 Introdução 16 7.2 Influência na Qualidade do Software 16 7.3 Heurísticas e Recomendações 17 8. Princípio da Reutilização 22 8.1 Introdução 22 8.2 Influência na Qualidade do Software 22 8.3 Heurísticas e Recomendações 22 9. Princípio da Rastreabilidade 25 9.1 Introdução 25 9.2 Influência na Qualidade do Software 26 9.3 Heurísticas e Recomendações 26 Glossário 27 Bibliografia 28 Pág. 2 1. Introdução A A A A A A A A A A A A A A AA AA AA AA AA AA AA A A A A A A A A A A A A A A Funcionalidade Fiabilidade Facilidade de utilização Eficiência Facilidade de manutenção Portabilidade A A A A A A A A AA AA AA AA A A A A A A A A Modularização AAAAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAAAAAA AAAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAAAAA A A A A A A AA AA AA A A A A A AAbstracção AAAAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAAAAAA AAAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAAAAA A A A A A A AA AA AA A A A A A AOcultação AAAAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAAAAAA AAAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAAAAA A A A A A A A A AA AA AA AA A A A A A A A A Coesão AAAAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAAAAAA AAAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAAAAA A A A A A A AA AA AA A A A A A AIndependência AAAAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAAAAAA AAAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAAAAA A A A A A A AA AA AA A A A A A AUniformização AAAAAAAAAAAAAAAA AAAAAAAAAAAA AAAAAAAAAAA AAAAAAAAA AAAAAAAAAAAAAA AAAAAAAAAAAAAAA A A A A A A AA AA AA A A A A A A Reutilização AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAA AAAA AAAA AAAA AAAA AA A AAA AAA AAAA AAAA AAAA AAAA AAAAA AAA AAAA AAAA AAA AA AAA AAA AAAA AAAA AAAA AAAA AAAA AAA AAA AAA AAAA AAAA AAAA AAAA AAAA AAAA A A A A A A AA AA AA A A A A A ARastreabilidade 1.1 Princípios de Engenharia de Software A Engenharia de Software é a ciência que condensa o conhecimento adquirido sobre as práticas cuja adopção conduz à produção (e evolução) de sistemas de software de acordo com requisitos especificados, sem falhas e dentro do calendário e orçamento previsto. Esse conhecimento, relativo às soluções apropriadas para os problemas mais comuns que essa produção e manutenção envolve, é em geral apresentado de uma forma dispersa ou estanque e quase sempre guiada por técnicas ou ferramentas (de análise, desenho, codificação, teste, gestão de configurações, etc). Julgamos por isso haver um déficit ao nível da razão de ser da adopção dessas técnicas ou ferramentas, ou seja, no tocante à formalização filosófica do conhecimento de que trata a Engenharia de Software. Propõe-se em seguida um conjunto de princípios que, no entender dos autores, enquadra esse conhecimento e fornece uma estrutura conceptual propícia à racionalização sobre a adopção de técnicas e ferramentas. Esses princípios são: • Princípio da Modularização - divida-se para conquistar; • Princípio da Abstracção - adicione-se detalhe incrementalmente; • Princípio da Ocultação - esconda-se o que não é necessário perceber; • Princípio da Coesão - concentre-se o que respeita ao mesmo problema parcelar; • Princípio da Independência - minimize-se as ligações entre os componentes; • Princípio da Uniformização - convencione-se e use-se a mesma simbologia; • Princípio da Reutilização - incorpore-se componentes já existentes e provados; • Princípio da Rastreabilidade - conheça-se as transformações e as dependências. Na próxima secção será revisto um modelo que define a Qualidade do software através da explicitação de um conjunto de características. Cada uma das secções que se lhe seguem abordará um dos princípios em particular e discutirá a sua influência nessas características da qualidade do software. Se bem que com problemas essenciais e específicos [Brooks87], a Engenharia de Software tem muito em comum com outros ramos da Engenharia. Para ilustrar tais semelhanças, sempre que possível e apropriado, será efectuado um paralelo com a reificação do mesmo princípio em outro(s) ramo(s) da Engenharia. Ainda para cada princípio serão propostas heurísticas e recomendações práticas. 1.2 Caracterização da Qualidade do Software A Engenharia de Software tem em comum com as outras engenharias, entre outras coisas, o desiderato da concepção de produtos de qualidade. A identificação dos atributos caracterizadores da qualidade do software e a sua importância relativa tem várias nuances consoante o domínio de aplicação, o ponto de vista do observador e mesmo o horizonte temporal. Com efeito em software de tempo real a eficiência é uma preocupação dominante. Se os sistemas forem críticos (por exemplo se envolverem risco de vida) então tem de ser dada grande relevância à fiabilidade. Para os utilizadores de sistemas interactivos a funcionalidade e a facilidade de utilização são geralmente os factores dominantes, enquanto do ponto de vista estrito das equipas de desenvolvimento o que é mais relevante é a facilidade de manutenção. A médio e longo prazo, porém, a portabilidade pode ser decisiva. Para um gestor de projectos de desenvolvimento de software, que tem de avaliar as melhorias de qualidade em conjunto com outros factores tais como prazos e orçamentos, interessa combinar (e pesar) as diversas características. Não admira portanto que vários autores [Meyer88, Pressman92, Quintin93, Sommerville92, Stalhane92] apresentem interpretações não completamente coincidentes sobre quais as características fundamentais da qualidade Pág. 5 2. Princípio da Modularização 2.1 Introdução A modularização trata da questão de como a estrutura (de um programa) pode ajudar a atingir certos objectivos. Talvez o maior deles seja dominar a complexidade e por isso este princípio defende uma estratégia de dividir para conquistar. Cada unidade de divisão, geralmente denominada módulo, é habitualmente reificada através de um ficheiro. A modularização permite que: • um problema seja atacado em simultâneo por pessoas distintas; • as alterações provocadas num módulo não condicionem obrigatoriamente a recompilação de todo o sistema em construção; por outras palavras, a modularização permite a compilação separada. A modularização é uma prática comum em outros ramos da Engenharia. Numa linha de montagem de automóveis esta é bem evidente e imprescindível. Um computador (ou qualquer sistema electrónico com algum grau de sofisticação) é constituído por vários módulos como écran, teclado, rato, fonte de alimentação, unidades de disco, CD e disquetes, carta do processados, controladores de entradas/saídas, circuitos de memória, microprocessador, etc. Tais módulos são projectados e construídos separadamente, inclusivamente por empresas distintas. 2.2 Influência na Qualidade do Software A portabilidade depende da modularização pois esta permite compartimentar claramente as porções do código que limitam a portabilidade, nomeadamente as referentes à instalação, configuração e a todas as dependências de tudo o que é exterior ao sistema em causa. A facilidade de manutenção depende da modularização porque esta ao garantir uma compartimentação das zonas de código a alterar, permite a compilação separada que acelera o processo de construção de versões executáveis enquanto que, por outro lado, facilita a possibilidade de indivíduos distintos poderem em simultâneo alterar e testar módulos distintos. A fiabilidade depende da modularização porque ao permitir que módulos distintos possam ser testados separadamente (unit test - teste modular, geralmente estrutural) antes de serem integrados para formar um sistema mais complexo, e por isso mais dificilmente testável (geralmente com teste funcional), vem concorrer para o aumento da fiabilidade. 2.3 Heurísticas e Recomendações 2 2.3.1 Critérios para a criação A modularização não é simplesmente a divisão arbitrária de um grande programa em partes mais pequenas (os módulos). A divisão em módulos deverá ser efectuada com base nos princípios da coesão (agregação de componentes com afinidades semânticas ou funcionais) e da independência (minimização da dependência inter-modular) que adiante serão descritos. Os critérios de partição em módulos devem ser explicitados porque, pelo Princípio da Uniformização, estes devem ser mantidos em futuras evoluções do sistema. MOD1 - A documentação externa de cada módulo deve mencionar explicitamente qual o critério que orientou a sua criação, para que fique claro quais os componentes que aí poderão ser adicionados futuramente. (vide princípio da coesão) Pode ser impossível satisfazer em simultâneo um conjunto de objectivos, pois um pode ditar um dado tipo de partição e outro uma diferente. Assim, o resultado final de uma decomposição em módulos é sempre uma solução de compromisso. Seja qual for o critério de decomposição, é importante que cada módulo tenha uma identificação e interface definida e que seja claro quais os módulos de que depende. Sendo um módulo uma agregação física de componentes com uma dada coesão e independência, interessa identificar inequivocamente esses componentes. 2 - Cada uma será identificada por um símbolo correspondente às primeiras três letras do princípio correspondente, seguido de um número de série (por cada princípio). Por vezes é também indicado outro(s) princípio com o qual a heurística está também relacionada. Pág. 6 MOD2 - A documentação de cada módulo deve indicar quais os outros de que este depende. (vide princípio da rastreabilidade) Algumas linguagens não fornecem suporte directo para a implementação de módulos, cabendo neste caso ao programador a responsabilidade de suportar funcionalidade idêntica, pela adopção de práticas de programação adequadas. 2.3.2 Modularidade sem suporte directo na linguagem Como já foi referido, através de uma disciplina de programação adequada é possível obter as vantagens da modularização. Fundamentalmente adopta-se um conjunto de regras que especifiquem que apenas é permitido o acesso a determinados elementos de um módulo, independentemente daquilo que seja permitido pelo compilador. Um conjunto de recomendações a seguir, em linguagens que não ofereça suporte directo, é descrito a seguir: MOD3 - Colocar cada módulo num ficheiro fonte separado. (vide princípio da independência) MOD4 - Criar uma convenção de identificação que permita a distinção entre as rotinas do módulo que são de acesso público e as que não o são, quando a linguagem não tiver mecanismos para tal. (vide princípio da ocultação) MOD5 - Utilizar uma convenção de nomes para os dados que permita identificar o módulo proprietário dos dados e se são públicos ou privados. Esta convenção deve ser consistente com a adoptada para as rotinas. (vide princípio da uniformização) MOD6 - Impedir as rotinas de utilizarem dados internos a outros módulos, mesmo se o compilador tratar todos os dados como globais. (vide princípio da independência) 2.4 Automatização do processo de construção de versões executáveis A automatização do processo de construção de versões executáveis e a manutenção automática do sincronismo temporal entre os vários componentes de um programa apresenta as seguintes vantagens: • economia de tempo pois todo o processo é realizado automaticamente; • eliminação de inconsistências introduzidas se o processo for efectuado manualmente, que levam à produção de executáveis incorrectos. Quando o numero de ficheiros contendo código fonte é muito elevado torna-se impraticável a não automatização do processo; A automatização do processo de consiste na criação de um guião que depois será usado por um processador de guiões (em inglês, o processador de guiões e o guião são habitualmente designados por make e makefile, respectivamente). O guião guarda informação acerca dos ficheiros necessários e respectivas dependências bem como acerca da forma de tratar cada ficheiro e das ferramentas utilizadas para o efeito, com vista à produção de um ou mais executáveis. A manutenção de coerência entre os vários ficheiros é feita à custa da marca temporal que o sistema operativo normalmente associa a cada um deles. Com base nesta e nas dependências entre os ficheiros obtidas a partir do guião, o processador de guiões verifica se as dependências temporais entre os vários ficheiros são verificadas. Aqueles ficheiros alvo para as quais estas não se verificarem são reconstruídos, de acordo com as regras do guião. Na figura seguinte apresenta-se um exemplo que servirá para ilustrar a criação de um guião. Na situação descrita pretende-se criar um ficheiro executável denominado “executavel.exe” e duas bibliotecas, “bibliotecaS.slib” e “bibliotecaD.dlib”, uma estática e outra dinâmica, que serão usadas pelo executável. Ambas podem também ser usadas por outras aplicações. Estão representados os ficheiros gerados ao longo de processo de construção, com as respectivas dependências indicadas por setas, onde o ficheiro que se encontra no extremo da seta tem uma dependência em relação ao que está na origem, o que significa que se o segundo for alterado o primeiro terá de ser reconstruído. Os ficheiros que contêm as definições são “defA.inc”, “defB.inc”, “defBibS.inc” e “defBibD.inc”. O ficheiro que contem os pontos de entrada e de saída do programa denomina-se “principal.src”. Existem dois outros ficheiros, “moduloA.src” e “moduloB.src”, que contêm funções que realizam outras tarefas específicas deste programa. Os ficheiros “bibliotecaS.src” e “bibliotecaD.src”, contêm o código fonte das bibliotecas estática e dinâmica, respectivamente. Estas bibliotecas contem funções de utilização geral e que se pretendem disponibilizar também para outros programas. Da observação da figura é patente a diferença na utilização destas duas bibliotecas. Enquanto que a biblioteca estática, ficheiro “bibliotecaS.slib”, é ligada (linked em inglês) com os Pág. 7 restantes ficheiros objecto após ter sido gerada, a biblioteca dinâmica será chamada directamente pelo programa em tempo de execução, pelo que o executável não apresenta em relação a ela uma dependência de construção. defA.inc defB.inc defBibS.inc defBibD.inc bibliotecaD.src moduloA.src principal.src moduloB.src bibliotecaS.src moduloA.obj moduloB.obj principal.obj bibliotecaS.obj bibliotecaD.obj. bibliotecaS.slib bibliotecaD.dlib executavel.exe Figura 2 - Grafo de Dependências de Construção Um guião encontra-se estruturado em duas partes: a primeira contem definições e a segunda as regras de dependências e as instruções de reconstrução, para cada ficheiro. As definições (denominadas macros em inglês) são atribuições a um nome de um qualquer valor. Isto apresenta duas vantagens: permite a utilização do nome em vez do valor, e na eventualidade de alterações posteriores estas são apenas feitas na atribuição reflectindo-se automaticamente em todo o guião. As regras de dependência, da forma ficheiroA : [nomeFicheiro]+ , indicam que ficheiroA terá de ser reconstruído se qualquer dos ficheiros de que depende for alterado. A reconstrução é efectuada executando o conjunto de comandos que se encontram a seguir à dependência. Apresenta-se de seguida um exemplo de guião, elaborado com base no grafo de dependências anterior. {********************************************************** * Guião para o projecto XPTO * * Versão: 1.1 * * Historial: * * 1.0 22/1/96 M. Dias Criação * 1.1 24/1/96 L. Silva Adiciona suporte para * edição de ligações dinâmica **********************************************************} Pág. 10 obrigam a que uma função seja fisicamente declarada num programa antes da ocorrência da primeira instrução que a chame, ou então que se diga explicitamente que a sua declaração ocorre em outro ponto do programa. Isso pode ser conseguido, por exemplo, declarando apenas o cabeçalho da função e uma qualquer palavra chave indicando que a implementação é diferida. 3.3.2 Extensão aos tipos de dados pré-definidos A criação de novos tipos de dados, nomeadamente tipos abstractos de dados, fornece um suporte directo para este principio, permitindo uma transição mais fácil da fase de desenho para a fase de codificação. Um tipo abstracto de dados é um conjunto de dados e operações que os manipulam e pode ser usado como extensão aos tipos de dados oferecidos pela linguagem. Por exemplo, uma pilha pode ser definida com base num array e nas operações Inicializar, LerTopo, Por e Tirar, que colocam e retiram um elemento do topo da pilha, respectivamente. Outros exemplos tradicionais de tipos abstractos de dados são as listas, filas de espera, estruturas hierárquicas (árvores) ou circulares (anéis) e grafos. Algumas linguagens como ADA, CLU e Modula-2 possuem primitivas para a definição de tipos abstractos de dados. ABS2 - Se a linguagem suportar a definição de tipos abstractos pelo programador esta possibilidade deve ser extensivamente utilizada. (vide princípio da coesão) A vantagem desta prática é que se houver necessidade de posteriormente alterar a estrutura de dados subjacente ao tipo, apenas é necessário efectuar esta alteração no ponto do programa onde este foi definido, ao invés de em todos os pontos onde existam declarações. A criação de um novo tipo abstracto de dados é um processo que pode ser decomposto nos passos seguintes: 1. Identificação do comportamento da entidade que se pretende modelar, ao nível de abstracção adequado. 2. Identificação do conjunto de estruturas de dados necessárias para em cada instante representar o estado da entidade. 3. Identificação de um conjunto de invariantes para o tipo, ou seja, predicados que devem ser verdadeiros enquanto uma instância do tipo existir. 4. Identificação o conjunto de operações associadas. Tipicamente estas operações permitem modelar o comportamento da entidade. Algumas provocarão alterações no estado da entidade, escrevendo nas estruturas de dados. Outras serão operações apenas de leitura, que permitem obter informações acerca do estado da entidade. 5. Codificação das operações. 3.3.3 Exemplo de criação de um tipo abstracto de dados Considere-se um elevador que pode servir um numero arbitrário de andares, especificado na sua criação. Consideremos ainda que o elevador monitoriza em cada instante o peso total, que não poderá exceder um determinado limite. De acordo com a descrição de comportamento anterior, o estado do elevador é caracterizado pelo andar e pelo peso. É agora possível derivar invariantes para o tipo. Neste caso, um invariante é que o andar actual não pode ser superior ao máximo especificado na criação do elevador. O outro é que o peso não poderá em caso algum exceder o limite. As operações associadas a este tipo abstracto de dados poderiam ser: CriaElevador( andar_de_topo: integer); DestroiElevador(); SobeUmAndar(); DesceUmAndar(); MudaParaAndar( andar: integer); AndarActual( VAR andar: integer); PesoActual( VAR peso: integer); AbrePorta(); FechaPorta(); As duas primeiras operações fazem parte de qualquer tipo de dados abstracto e denominam-se construtor e destrutor do tipo, respectivamente. Tipicamente exercem funções de inicialização, como seja alocação de memória, e de finalização, como libertação de recursos quando a instância do tipo deixa de ser necessária. As operações SobeUmAndar, DesceUmAndar e MudaParaAndar denominam-se modificadores pois alteram a informação que representa o estado do objecto. As operações AndarActual e PesoActual são selectores pois permitem obter informações acerca do estado do objecto. Finalmente, as operações AbrePorta e FechaPorta são designadas de acessórias. Pág. 11 4. Princípio da Ocultação 4.1 Introdução Este princípio foca a importância de esconder detalhes de realização não relevantes. Dito desta forma o objectivo da ocultação parece, e é, semelhante ao da abstracção, mas dela difere pois aqui se pretende tornar inacessível certos detalhes por forma a não afectarem partes do sistema para além das estritamente previstas. A abstracção ajuda a identificar os detalhes a esconder. A ocultação trata de definir e compelir as restrições de acesso que, de outra forma, seriam apenas implícitas. O princípio da ocultação, da mesma forma que os da abstracção e da coesão dá ênfase ao “o quê?” em detrimento do “como?”. Os princípios da ocultação e da coesão considerados conjuntamente, estão na base daquilo que é habitual designar por capsulação de informação. A particular ênfase nesta última, através da construção designada por classe, é um dos aspectos distintivos do paradigma da orientação a objectos [Wegner87]. A utilização da ocultação em outros ramos da Engenharia é recorrente, como por exemplo na Engenharia Electrotécnica como de seguida veremos. Na área dos sistemas digitais são os circuitos integrados (processadores, controladores, memórias, etc.) utilizados em projectos como caixas pretas com um conjunto de entradas e saídas, os pinos, ao nível dos quais apresentam um dado comportamento. A topologia dos circuitos internos, envolvendo por vezes milhões de dispositivos elementares (transístores) e outras tantas inter-conexões, são propositadamente escondidas. Na área das comunicações, os protocolos de comunicação são em geral construídos em camadas. Cada camada oculta (capsula) o que recebe da camada inferior, adicionando informação relevante a trocar nesse nível. 4.2 Influência na Qualidade do Software A eficiência pode melhorar com a ocultação porque saber que apenas alguns procedimentos podem aceder a certos dados partilhados, permite reduzir o número de verificações (em tempo de execução) sobre a validade desses dados; a estratégia habitual em alguns sistemas operativos de não permissão de acesso directo aos dispositivos de entradas e saídas e de memória secundária, que permite uma optimização global da utilização desses dispositivos é outro exemplo corroborante. A facilidade de manutenção depende da ocultação pois os programadores não se “perdem” a tentar perceber porções do código cuja compreensão não serve o objectivo imediato de efectuar uma dada operação de manutenção cujos requisitos foram correctamente definidos. A portabilidade depende da ocultação pois esta permite que a porção do sistema em construção que é dependente do ambiente exterior, cuja complexidade associada é por vezes elevada e cujo domínio é diferente daquele do problema em estudo, possa ser isolada; assim a maior parte do sistema a construir ou manter não tem que obedecer senão a constrangimentos aplicacionais. 4.3 Heurísticas e Recomendações A observância do principio da ocultação, depende em grande parte dos mecanismos facultados pela linguagem de programação. Existem linguagens que oferecem mecanismos que permitem definir o âmbito de visibilidade de qualquer construção, permitindo uma efectiva ocultação de informação. A declaração de dados, ao definir âmbitos de visibilidade, permite obter uma das vantagens da ocultação, a restrição de acesso. 4.3.1 Declaração de dados A declaração dos tipos associados às variáveis utilizadas permite que compilador possa alocar o espaço de memória apropriado para cada uma delas. Dependendo das linguagens a declaração pode ser explicita, implícita sem redefinição ou implícita com redefinição. Declaração explicita consiste em declarar em conjunto, numa cláusula dedicada prevista na gramática da linguagem, o nome e tipo de todas as variáveis que venham a ser utilizadas num determinado raio de acção. Pág. 12 Na declaração implícita sem redefinição, a variável é utilizada onde for necessário, sem qualquer declaração prévia. O compilador determina o tipo da variável em função das expressões que lhe forem atribuídas. No entanto uma vez determinado o tipo de uma variável não é possível atribuir mudá-lo. Na declaração implícita com redefinição é possível atribuir expressões de tipos diferentes à mesma variável. Qualquer umas das declarações implícitas fere o Princípio da Coesão. OCU1 - Sempre que a linguagem o permita, a declaração explícita deve ser preferida. Esta diminui a probabilidade de introdução de defeitos fortuitos ou acidentais (vide princípios da coesão e rastreabilidade) A declaração implícita sem redefinição é permitida em várias linguagens. Nesta circunstância deve-se fazer no início do programa uma inicialização de todas as variáveis. Embora continue sem haver indicação expressa do tipo, esta prática aumenta a legibilidade do código. A redefinição de uma declaração implícita deve ser absolutamente evitada mesmo que a linguagem o permita, já que tal corresponde a utilizar a mesma variável para mais do que um objectivo. A validade de uma estrutura de dados é delimitada num determinado âmbito ou raio de acção (scope em inglês). Este corresponde à zona do programa em que a variável pode ser referenciada. A declaração delimita também o tempo de vida da variável, que é o intervalo de tempo durante o qual existe memória alocada para a mesma. O tempo de vida coincide com o período em que se estão a executar as instruções que constituem o seu raio de acção. OCU2 - O raio de acção (e tempo de vida) deve ser mantido tão pequeno quanto possível. Uma das razões para isto é a optimização da utilização de memória, já que uma variável só tem memória alocada enquanto está a ser executada a parte do programa correspondente ao seu raio de acção. A outra razão é o aumento da legibilidade do programa, uma vez que a limitação do raio de acção reduz o volume de informação que é necessário apreender em cada ponto do programa. (vide princípio da coesão) Um corolário do que se acabou de dizer é que: OCU3 - O numero de variáveis globais deve ser limitado ao mínimo indispensável bem com o numero de variáveis partilhadas entre funções. O não cumprimento deste principio é potenciador do aparecimento de efeitos colaterais indesejados. (vide princípio da independência) A utilização de variáveis globais, isto é de variáveis partilhadas por vários módulos, é uma técnica de programação que, embora por vezes cómoda, é particularmente perigosa em projectos de alguma dimensão e envolvendo equipas com vários elementos. Na maioria dos casos é possível estruturar o código de forma a eliminá-las completamente, com reflexo positivo no numero de erros introduzido e na legibilidade. 5. Princípio da Coesão 5.1 Introdução Este princípio defende a proximidade física de componentes fortemente interrelacionados (semântica ou funcionalmente). Poderia, por isso, ser também designado por princípio da localização. A coesão pode servir outros princípios. Uma abstracção, por exemplo, será mais inteligível se for coesa do que se apresentar um grande espalhamento ou dispersão física. Podem igualmente ser encontrados exemplos da adopção deste princípio em outros ramos da Engenharia. Quase todas as peças necessárias ao funcionamento do motor de um automóvel se encontram circunscritas numa dada posição do chassis (na frente ou na retaguarda). Os manuais de qualquer equipamento são em geral organizados com base na coesão semântica. É típico que um capítulo foque o painel sinóptico, outro as funcionalidades permitidas, outro os cuidados de manutenção e outro as entidades que estão habilitadas a efectuar revisões de fundo. Em equipamentos electrónicos, a divisão em módulos é guiada pela coesão funcional (carta de controlo de entradas e saídas, carta controladora gráfica, unidade de alimentação, etc.). Pág. 15 A limitação do acoplamento (interferência) electromagnético é uma preocupação dominante e regulamentada no projecto de sistemas electrónicos. O acoplamento capacitivo nas linhas de transmissão é também indesejável pois provoca o efeito conhecido por cross-talk. 6.1.1 Várias formas de acoplamento Existem vários critérios de classificação do acoplamento entre procedimentos: • dimensão - refere-se ao número de ligações entre os procedimentos; entenda-se por ligação cada caso concreto de passagem de informação entre os dois procedimentos, • sentido - pode haver acoplamento só no sentido chamador / chamado (invocação), só no sentido chamado / chamador (retorno) ou nos dois sentidos (bilateral); • explicitação - refere-se à visibilidade do acoplamento; se o acoplamento se faz através da interface (seja num ou noutro sentido), então o acoplamento diz-se explícito, senão diz-se implícito; o acoplamento implícito pode ser directo quando um procedimento usa dados internos ao outro ou indirecto quando um procedimento altera dados globais (visíveis a ambos os procedimentos) e o outro os lê. Este é nitidamente um aspecto da interligação com o princípio da ocultação. • proximidade - refere-se ao grau de “conhecimento” mútuo entre os procedimentos que se encontram acoplados; • flexibilidade - refere-se à facilidade com que se consegue alterar as ligações entre procedimentos; 6.2 Influência na Qualidade do Software A fiabilidade depende da independência pois assim se reduzem os efeitos colaterais potencialmente causadores de falhas ou de comportamento não especificado. A eficiência depende da independência pois esta provoca uma redução do tempo gasto em operações de gestão da pilha e do amontoado. A facilidade de manutenção depende da independência porque esta restringe a propagação de alterações, por vezes imperceptíveis à primeira vista (efeitos colaterais); por outras palavras, o não acoplamento favorece a estabilidade, um dos atributos da facilidade de manutenção. Para melhor compreender e dominar os efeitos do acoplamento que não se pode evitar é habitual recorrer a ferramentas que geram referências cruzadas. A portabilidade depende da independência porque as alterações introduzidas para permitir a instalação num novo ambiente não provocarão a proliferação dos indesejáveis efeitos colaterais. 6.3 Heurísticas e Recomendações 6.3.1 Várias formas de acoplamento A passagem de parâmetros, mecanismo pelo qual se faz a correspondência entre parâmetros formais e actuais pode apresentar várias formas, explícitas ou implícitas, consoante a linguagem. Na passagem dita por valor é feita uma cópia do valor do parâmetro actual, enquanto que na passagem dita por referência apenas o endereço e não o valor do parâmetro é copiado. No primeiro caso há uma perda de eficiência devido ao tempo de cópia e ao espaço adicional ocupado pela mesma. No segundo há o risco da modificação inadvertida da variável na rotina que a recebe. IND1 - Como regra geral, sempre que não se pretenda alterar o parâmetro de entrada no interior da função deve usar-se a passagem por valor. Esta garante que quaisquer que sejam as operações realizadas no interior da função o valor do parâmetro actual utilizado como argumento não será alterado, o que impede a introdução de comportamentos não desejáveis. (vide princípio da rastreabilidade) Algumas linguagens suportam uma terceira possibilidade de passagem de parâmetros que consiste em fazer uma passagem por referência mas especificando que o valor referenciado não pode ser alterado no interior da função. Isto permite juntar a eficiência da passagem por referencia à fiabilidade da passagem por valor. IND2 - A utilização da passagem de parâmetros por referência deve ser restringida a situações em que a eficiência for imperativa (quando as estruturas de dados a passar forem de grande dimensão), se não existirem mecanismos que impeçam a ocorrência de efeitos colaterais. Pág. 16 6.3.2 Outras formas de acoplamento A dependência entre blocos de um programa pode porém não ser explicitada através de parâmetros. Esta é a situação quando se utilizam variáveis partilhadas (por vezes designadas de variáveis globais4) e os seus malefícios já foram discutidos aquando da apresentação do princípio da ocultação. IND3 - Deve ser evitado o acoplamento indirecto que ocorre aquando da utilização de variáveis partilhadas para troca de dados entre vários componentes. Algumas linguagens (ex: Eiffel) possuem mecanismos embutidos para a explicitação da forma como a informação deve ser recebida (pré-condições) e como deve ser devolvida (pós-condições). Caso estejam disponíveis, devem ser utilizados extensivamente, pois aumentam a clarificação das dependências. IND4 - Os vários componentes devem apresentar uma interface bem definida, que torne explícita a relação entre eles. 7. Princípio da Uniformização 7.1 Introdução Este princípio, que também poderia ser designado por Princípio da Normalização, destina-se a evitar inconsistências e diferenciações desnecessárias. A diversidade (oposto da uniformidade) é especialmente patente na produção de código fonte em diferentes equipas de desenvolvimento, ou mesmo de indivíduo para indivíduo, na ausência de mecanismos que a evitem. Quando associado à abstracção, este princípio advoga que a transição entre níveis não seja disruptiva, isto é, que se deva minimizar as conversões ao nível dos conceitos, mecanismos e notações ao longo do ciclo de vida do software. Este princípio está também por trás do paradigma da orientação a objectos pois neste há uniformidade de tratamento de dados e procedimentos o que não acontece nas abordagens procedimentais tradicionais. Algumas propostas recentes [Budd94] que advogam que se deve tirar o melhor de cada paradigma para conseguir resolver eficaz e eficientemente toda a classe de problemas computáveis (abordagem multi-paradigma / linguagem Leda), visam na realidade propor formalismos que esbatam as barreiras da não uniformidade evidente entre linguagens representativas de cada paradigma. A uniformização é talvez o princípio mais universalmente adoptado nos outros ramos da Engenharia e talvez o que mais catalisa a passagem de uma prática artesanal para uma industrial. A uniformização permite (i) evitar que pessoas ou mesmo organizações sejam tentadas a "reinventar a roda", (ii) produzir produtos com aceitação alargada, em particular quando devem obedecer a requisitos de interoperacionalidade ou produção em série (isto é, para reutilização) e mesmo (iii) evidenciar a qualidade de produtos (e processos) através de acções de avaliação de conformidade, habitualmente ditas de certificação. Várias organizações a nível internacional como a ISO/IEC, CEN/CENELEC e NATO, ou nacional como o IPQ, AFNOR, DIN e IEEE, são dedicadas às tarefas de produção e acompanhamento das actividades de normalização [Abreu94]. Praticamente todos os componentes utilizados nas várias Engenharias se encontram normalizados desde os parafusos e porcas, tubagens, resistências e condensadores, jantes e pneus, às lâmpadas de iluminação. Os próprios sistemas de unidades (peso, comprimento, área, volume, massa, pressão, frequência, etc.) estão uniformizados. Um longo caminho tem ainda de ser percorrido na área dos métodos quantitativos em Engenharia de Software até que se consiga chegar a consensos mesmo só no tocante a este último ponto [Humphrey89, Zuse91, Abreu93, Hetzel93]. 7.2 Influência na Qualidade do Software A facilidade de utilização depende da uniformização pois os utilizadores mais rapidamente aprendem a utilizar e a tirar partido das funcionalidades se já conhecerem o paradigma de interacção; esta constatação esteve por base da 4 - A primeira designação é mais feliz dado que a ideia de “global” pode denotar um raio de acção (âmbito) espalhado a todo o programa. Na verdade o acoplamento entre dois componentes por identificadores partilhados implica que o raio de acção destes tenha apenas de incluir esses mesmos componentes. Pág. 17 crescente fusão entre os mecanismos de interacção aplicacionais com aqueles utilizados pelo sistema operativo da plataforma de destino (como por exemplo nos caso do Macintosh ou Windows). A facilidade de manutenção depende da uniformização porque, em particular na manutenção evolutiva, em que os requisitos são reformulados, o esforço de passagem do domínio do problema (análise) para o domínio da solução (desenho e codificação) será reduzido pela diminuição das quebras em particular ao nível das representações e conceitos associados. A adopção de um conjunto de regras tendentes à uniformização dos identificadores de todo o tipo de entidades manipuláveis é também um factor potenciador da facilidade de manutenção. A portabilidade depende da uniformização porque se o tratamento das dependências em relação a todos os ambientes suportados for efectuado com base no mesmo tipo de abstracções e correspondentes representações, então a extensão ao suporte de um novo ambiente será mais facilmente compreendida, produzida e integrada. 7.3 Heurísticas e Recomendações Esta secção descreve um conjunto de convenções, independente da linguagem de reificação, para identificadores das várias entidades utilizadas como tipos, constantes, variáveis, ficheiros, palavras reservadas, etc. Entre os benefícios da adopção desta normalização podem contar-se: • a melhoria da clareza do código por via do seu poder denotativo; • a redução do tempo despendido a “inventar” identificadores; • a proliferação de identificadores com a mesma semântica, mesmo no mesmo projecto; • a redução do tempo de aprendizagem para pessoal que mude de projecto; • uma redução da probabilidade de colisões de identificadores (name clashing) em tempo de compilação ou de edição de ligações (link); • uma compensação das fraquezas de certas linguagens. UNI1 - Devem ser definidas e utilizadas convenções para o estilo de codificação que permitam evitar discrepâncias devido a gostos pessoais, aumentando a uniformidade de projectos com mais do que um participante. (vide princípio da reutilização) 7.3.1 Considerações sobre o tipo de letra Algumas linguagens fazem distinção entre letras maiúsculas e minúsculas (dizem-se case-sensitive em inglês), enquanto que outras não o fazem. Nas primeiras o identificadores total, Total e TOTAL correspondem a entidades distintas. Como é óbvio esta possibilidade nunca deve ser explorada. As convenções quanto ao tipo de identificadores não se destinam a ser utilizadas por compiladores mas sim a facilitar a vida aos programadores, e estes fazem sempre a distinção entre tipos de letra (minúscula ou maiúscula) seja qual for a linguagem. Dado que não é habitual que uma linguagem imponha um determinado tipo de letra, esta possibilidade deve ser explorada convenientemente. Um problema típico que pode ser resolvido com recurso ao tipo de letra é a questão da destrinça entre variáveis, constantes e funções. Com efeito numa expressão é igualmente válido utilizar o valor de uma constante, variável ou valor devolvido por uma função. Para complicar a percepção, algumas linguagens permitem que uma função sem parâmetros seja invocada sem os parêntesis que em geral envolvem a lista de parâmetros. Deve assim fornecer-se um veículo para destrinçar este tipo de casos. No exemplo seguinte não é possível fazer essa destrinça: resultado = deposito_maximo * (factor - taxa) Uma possível solução para o problema é a de adoptar diferentes tipos de letra, como por exemplo maiúsculas para constantes, minúsculas para variáveis e a primeira maiúscula e as restantes minúsculas para as funções (ou genericamente para qualquer tipo de procedimento). Assim teríamos por exemplo: resultado = Deposito_Maximo * (factor - TAXA) 7.3.2 Considerações sobre a dimensão Não há receita exacta para o comprimento de um identificador mas valores entre 8 e 16 parecem ser suficientes para garantir uma representação mnemónica apropriada, sem acarretar um esforço de escrita exagerado. Muitas das linguagens que colocavam restrições ao comprimento máximo dos identificadores, ou já caíram em desuso ou foram actualizadas, entre outras coisas, no sentido de se levantar essa restrição. Os identificadores podem por vezes ser Pág. 20 7.3.7 Resumo A tabela seguinte resume as recomendações propostas para os identificadores. Outras propostas podem ser encontradas em [McConnell93]. Tipo letra Prefixo Sufixo Palavras reservadas / Módulos MAIÚSCULAS Procedimentos / Funções só 1ª Maiúscula Tipos pré-definidos minúsculas Tipos definidos pelo programador minúsculas _t Constantes MAIÚSCULAS Variáveis minúsculas Nomes lógicos de ficheiros: - pré-definidos minúsculas - criados pelo programador minúsculas _f Identificadores locais Identificadores de módulo m_ Identificadores globais g_ Tabela 1 - Recomendações para os identificadores Exemplo: O seguinte programa, que lê duas sequências ordenadas de entrada, as junta numa só também ordenada, que escreve no écran, está escrito conforme as recomendações propostas. PROGRAM JuntaSequencias (input, output); CONST m_COMPR_S1 = 3; m_COMPR_S2 = 4; m_COMPR_FINAL = m_COMPR_S1 + m_COMPR_S2; m_TAB= Chr(9); TYPE m_gama_s1_t = 1..m_COMPR_S1; m_gama_s2_t = 1..m_COMPR_S2; m_gama_final_t = 1..m_COMPR_FINAL; m_sequencia_t = (sequencia1, sequencia2); VAR m_s1: ARRAY[m_gama_s1_t] OF integer; m_s2 : ARRAY[m_gama_s2_t] OF integer; m_final : ARRAY[m_gama_final_t] OF integer; m_posicao1 : m_gama_s1_t; m_posicao2 : m_gama_s2_t; m_posicao_final : m_gama_final_t; {***********************************************************************} PROCEDURE LeSequencia(sequencia: m_sequencia_t; dimensao: m_gama_final_t); VAR posicao : integer; BEGIN CASE sequencia OF sequencia1: Writeln('Sequência 1 (',dimensao,' valores) : '); sequencia2: Writeln('Sequência 2 (',dimensao,' valores) : '); END; FOR posicao:=1 TO dimensao DO IF sequencia = sequencia1 THEN REPEAT Write('m_s1[',posicao:2,']='); Readln( m_s1[posicao] ) UNTIL (posicao=1) OR (m_s1[posicao]>=m_s1[posicao-1]) ELSE Pág. 21 REPEAT Write('m_s2[',posicao:2,']='); Readln( m_s2[posicao] ) UNTIL (posicao=1) OR (m_s2[posicao]>=m_s2[posicao-1]) END; {***********************************************************************} PROCEDURE AtribuiValor(sequencia: m_sequencia_t); BEGIN IF sequencia = sequencia1 THEN BEGIN m_final[m_posicao_final] := m_s1[m_posicao1]; m_posicao1 := m_posicao1+1 END ELSE BEGIN m_final[m_posicao_final] := m_s2[m_posicao2]; m_posicao2 := m_posicao2+1 END END; {***********************************************************************} BEGIN { Lê as sequencias ordenadas de entrada } LeSequencia(sequencia1, m_COMPR_S1); LeSequencia(sequencia2, m_COMPR_S2); { Junta as sequencias numa so', tambem ordenada } m_posicao1 := 1; m_posicao2 := 1; FOR m_posicao_final:=1 TO m_COMPR_FINAL DO BEGIN IF NOT ((m_posicao1>m_COMPR_S1) OR (m_posicao2>m_COMPR_S2)) THEN IF m_s1[m_posicao1] <= m_s2[m_posicao2] THEN AtribuiValor(sequencia1) ELSE AtribuiValor(sequencia2) ELSE IF m_posicao1 > m_COMPR_S1 THEN AtribuiValor(sequencia2) ELSE AtribuiValor(sequencia1); END; { Escreve a sequência de saída, ordenada } FOR m_posicao_final:=1 TO m_COMPR_FINAL DO Write(m_final[m_posicao_final]:4, TAB); Writeln END. Pág. 22 8. Princípio da Reutilização 8.1 Introdução Este princípio advoga que um produto de software deve incorporar componentes pré-fabricados e que partes deste devam ser construídas com o intuito da incorporação em outros produtos de software. A reutilização é potencialmente capaz de produzir um grande impacto na produtividade e na qualidade do software produzido. A médio e longo prazo permite reduzir drasticamente o esforço de desenvolvimento, diminuindo o custo final do sistema ou permitindo aplicar essa poupança na construção de mais funcionalidades, garantia de qualidade ou outras actividades. Os componentes reutilizáveis são geralmente desenhados com maior cuidado que o normal. Além disso, a sua utilização repetida faz transparecer (e permite assim eliminar) a maioria dos defeitos no seu desenho ou implementação. É por isso que estes componentes tendem a ser de maior qualidade. Esta é assim incorporada nos sistemas que deles fazem uso. A possibilidade de reutilização é uma razão forte no sentido de favorecer a estruturação do código em componentes independentes. Durante o desenvolvimento de um programa é essencial identificar os componentes reutilizáveis, que sendo agrupados em bibliotecas, poderão ser usados posteriormente. A reutilização só é possível se num programa for claramente separado o código que é especifico do programa daquele que é de utilização genérica, e que pode ser importado de bibliotecas ou escrito para ser posteriormente reutilizado. Para maximizar a possibilidade de reutilização deve ser efectuada uma decomposição do problema a resolver de acordo com níveis de abstracção. Para problemas diferentes esta abordagem gera frequentemente ao nível do código estruturas de dados e procedimentos semelhantes e que podem ser reutilizados. Em grande parte devido à uniformização atrás referida tem-se assistido, nas mais variadas áreas da Engenharia, à proliferação de indústrias de componentes. Estes são, por sua vez, utilizados por outras indústrias que os incorporam em produtos finais. Para além de algumas peças de motor, muitos fabricantes de automóveis usam apenas peças fabricadas por terceiros. O grau de reutilização (percentagem de incorporação de componentes produzidos por terceiros) ronda mesmo os 100% em alguns conhecidos “fabricantes” de computadores pessoais, que assim mais não passam de integradores. A reutilização poderá ser efectuada a vários níveis de abstracção, desde o desenho até à reificação, desde a peça elementar (porca ou parafuso) até a um sofisticado subsistema (motor de automóvel ou reactor). 8.2 Influência na Qualidade do Software A funcionalidade dos componentes de software melhora com a sua reutilização pois estes tendem a evoluir no sentido da genericidade (logo de uma funcionalidade mais alargada) para poderem ser usados em variados contextos. A fiabilidade dos componentes de software melhora com a sua reutilização pois ao serem usados em variados contextos e por variadas pessoas, tenderão a evidenciar (e a ver corrigidas) rapidamente quaisquer defeitos de que enfermassem originalmente. A facilidade de utilização melhora com a reutilização dos mecanismos de interacção simbólica (por ícones, botões, barras de opção deslizantes, etc) geralmente disponíveis nos ambientes de desenvolvimento, sob a forma de bibliotecas estáticas ou dinâmicas. A eficiência pode sofrer impactos negativos por parte da reutilização porque o inevitável aumento de fiabilidade e tendência para a generalidade dos componentes reutilizáveis é em geral inconciliável com a optimização dos recursos despendidos. A facilidade de manutenção aumenta com a reutilização pois este princípio contraria a replicação de código, logo do esforço correspondente de actualização do mesmo. Para além disso os componentes repetidamente utilizados tendem a tornar-se mais estáveis, isto é, a não suscitarem a ocorrência de efeitos colaterais cujo impacto no esforço de manutenção pode ser considerável. 8.3 Heurísticas e Recomendações 8.3.1 Problemas e Obstáculos na Reutilização Podemos considerar várias granularidades para a reutilização [Sommerville92] : Pág. 25 * real valor_inicialo valor inicial em escudos * integer n_dias numero de dias durante o qual foi amortizado * real taxa_anual taxa anual, introduzida como um decimal * * Valor de Retorno: * Um real representando o valor residual do equipamento, em escudos * * Funções Chamadas: * Função: Localização: * Potência funçõesMatemáticas.src * ConverteAnos funçõesTempo.src * Comentários: * Esta função estava anteriormente no módulo funçõesFinanceiras.src. * Mantiveram-se os parâmetros de E/S, e substituiu-se o algoritmo de calculo, por um mais rápido. ***************************************************************************************} O cabeçalho começa com o nome da função. De seguida segue-se uma descrição de alto nível dos objectivos da função. De seguida os parâmetros de entrada e saída, se os houver, com indicação do tipo e daquilo que representam. Segue-se uma descrição do valor de retorno. Em seguida vem uma descrição das funções chamadas e da sua localização. Finalmente, pode acrescentar-se algum comentário. Regra geral este comentário deve dizer respeito à função como um todo. REU3 - Comentar extensivamente a interface e forma de utilização dos componentes e atribuir nomes semanticamente ricos aos mesmos; só assim será facilitada a sua posterior reutilização. (vide princípio da uniformização) Os comentários incluídos junto ao código, destinam-se a facilitar a posterior alteração do programa. Devem porém ser consideradas as seguintes recomendações: REU4 - Evitar comentários que sejam tirados directamente do código, isto é, dizer em linguagem natural o que o código diz em linguagem simbólica. Comentar preferencialmente blocos de código, explicitando o raciocínio, ao invés de instruções individuais. REU5 - Se um bloco de código ou componente é tão obscuro que necessita de muitos comentários, então provavelmente precisa de ser rescrito. 9. Princípio da Rastreabilidade 9.1 Introdução Este princípio (traceability em inglês) comporta dois aspectos, a rastreabilidade sincrónica e a rastreabilidade diacrónica: • o primeiro refere-se à possibilidade de identificar e verificar em cada momento a correspondência entre as representações 8 do mesmo requisito ou entidade do universo de discurso a vários níveis de abstracção; • o segundo corresponde à possibilidade de verificar que evolução temporal teve uma dada representação ao mesmo nível de abstracção, isto é, quais as mutações operadas ao longo do tempo. A rastreabilidade é fundamental e os correspondentes mecanismos, genericamente designados por gestão de configurações, adoptados nos vários ramos da Engenharia. Para garantir a rastreabilidade sincrónica, as placas de circuito impresso tem inscrito um código de identificação distinto em cada posição em que é colocado um componente (circuito integrado, resistência, condensador, bobine, interruptor, díodo, ficha, etc.). Para garantir a rastreabilidade diacrónica, as mesmas placas tem também impressas um número de versão e/ou série. Os esquemas de 8 - Estas correspondem aos vários produtos (deliverables) concebidos ao longo do ciclo de vida do desenvolvimento de software, tais como especificações de requisitos, desenhos do sistema, código fonte ou manuais de instalação e operação. Pág. 26 montagem e manutenção de qualquer veículo (automóvel, comboio, navio, avião ou nave espacial) identificam clara e inequivocamente os componentes utilizados. Essa mesma identificação é utilizada pelos serviços de aprovisionamento (gestão de stocks). As organizações que prestam serviços de manutenção de qualquer linha de equipamentos, tem de possuir informação detalhada sobre a evolução dos mesmos (vários modelos produzidos) para saberem que peças podem ser trocadas por outras equivalentes. 9.2 Influência na Qualidade do Software A facilidade de utilização melhora com a rastreabilidade pois o utilizador mais facilmente relacionará o programa executável com os manuais de instalação ou de utilização. A fiabilidade crescerá mais rapidamente se houver uma grande rastreabilidade entre as situações potencialmente provocadoras de falhas e as ajudas disponíveis (por exemplo help on-line) para as evitar; também a rastreabilidade conseguida pela correspondência entre os efeitos da ocorrência de falhas (por exemplo mensagens de erro) e o ponto do código fonte onde ocorreram, permite uma melhor facilidade de recuperação, um dos atributos da fiabilidade. A facilidade de manutenção aumenta com a rastreabilidade pois este princípio vai no sentido de uma diminuição drástica do tempo perdido quer a procurar as correspondências entre vários tipos de documentos e / ou código fonte, quer a recuperar de suposições erróneas devido a incoerências entre os mesmos (que a falta de rastreabilidade potencia). 9.3 Heurísticas e Recomendações 9.3.1 Gestão de Configurações O termo Gestão de Configurações (GC) é utilizado para identificar o conjunto de mecanismos e actividades envolvidas na identificação e organização dos vários componentes do software (bem como do próprio ambiente de desenvolvimento) e para controlar a sua modificação durante, ou após, o processo de desenvolvimento. A GC deve ser uma parte integrante do processo de desenvolvimento de software ao longo de todas as fases do ciclo de vida. Permite alterações a documentos e outros produtos existentes de uma forma controlada, impedindo a destruição da sua integridade. Por configuração entenda-se o conjunto de componentes que definem uma versão particular de um sistema ou de um componente de um sistema. Cada uma destas versões deverá ter uma identificador único. O número mínimo de componentes pertencentes a uma dada configuração é o necessário para permitir uma eficaz manutenção correctiva e evolutiva do produto correspondente. Em software um componente ou item de configuração é qualquer produto resultante do processo de desenvolvimento como um documento de especificação de requisitos, módulo de programa fonte, bateria de testes, manual de utilização ou programa executável. RAS1 - Deverá ser adoptado um sistema de gestão de configurações que permita verificar as rastreabilidades sincrónica e diacrónica. RAS2 - O sistema de gestão de configurações adoptado deverá permitir a regressão isto é a reposição de uma versão de parte ou de todo o sistema em construção na situação em que se encontrava num dado instante do passado. RAS3 - As mensagens de erro correspondentes a situações de excepção (isto é, não previstas ou que não deveriam ocorrer), que eventualmente poderão provocar a interrupção da execução do programa, devem tanto quanto possível permitir a identificação imediata do ponto correspondente do código fonte em que ocorreram. (vide princípio da uniformização) RAS4 - Cada componente do código fonte deverá ter uma referência explícita (ou de alguma forma permitir a localização) do requisito a que diz respeito. (vide princípios da abstracção e da uniformização) Das recomendações anteriores decorre: RAS5 - Todos os requisitos explicitados e/ou entidades do domínio do problema representados a vários níveis de abstracção deverão possuir um identificador e uma versão associada para permitir a sua rastreabilidade sincrónica e diacrónica. (vide princípios da abstracção e da uniformização) Pág. 27 10. Glossário São de seguida explicitados alguns termos utilizados no presente documento, para evitar potenciais más interpretações na sua leitura. abstracção - representação de uma dada entidade em que se salientam certo tipo de propriedades e se desprezam outras cujo pormenor complicaria a percepção do todo. acoplamento - ligação entre dois procedimentos (inter-conectividade); o grau de acoplamento traduz a força de ligação entre esses procedimentos. amontoado - zona da memória central, associada à execução de um programa, destinada a fornecer o armazenamento necessário para as variáveis dinâmicas (heap, em inglês); a sua gestão é feita através de mecanismos de “recolha do lixo”. assembly - linguagem mnemónica correspondente (numa relação biunívoca) ao conjunto de instruções máquina do processador central do computador em causa. baixo para cima - abordagem em que se constrói algo partindo do conhecimento do grau de detalhe final e se tenta abstrair em passos sucessivos representações cada vez mais abstractas das entidades em presença e respectivos inter-relacionamentos (bottom-up, em inglês); sinónimo de estratégia de composição, ascendente ou abordagem do particular para o geral; cima para baixo - abordagem típica na análise em que um dado problema é primeiramente estudado a um nível de maior abstracção, e depois é aprofundado por passos sucessivos, onde se vai incluindo progressivamente um maior grau de detalhe (top-down, em inglês); sinónimo de estratégia de decomposição, descendente ou abordagem do geral para o particular. coesão - ligação semântica ou funcional entre componentes. tipo abstracto de dados - estrutura de dados (armazena o estado do tipo) e conjunto de operações que sobre ela podem actuar (caracteriza o comportamento do tipo). edição de ligações - (linking, em inglês) agrupamento de vários componentes, previamente compilados. Se os componentes ficarem agrupados de forma permanente num executável denomina-se estática. Se o agrupamento apenas ocorre em tempo de execução, permanecendo os componentes como unidades independentes, denomina-se dinâmica (ex.: bibliotecas de ligação dinâmica). efeitos colaterais - efeitos, geralmente indesejados, provocados em pontos distintos daquele que está a ser sujeito a alterações, por efeito de acoplamento (side-effects, em inglês). encastoamento9 - mecanismo disponível em algumas linguagens, para forçar explicitamente a conversão entre tipos (casting, em inglês). FILO - First In Last Out - estrutura em que o primeiro elemento a entrar é o último a sair, tal como acontece numa pilha de elementos. FIFO - First In First Out - estrutura em que o primeiro elemento a entrar é também o primeiro a sair, tal como acontece numa fila de espera. função - tipo particular de procedimento que tem uma correspondência directa com o conceito homónimo da matemática, no sentido em que tem a si associado um tipo que é o do contradomínio da função; o domínio da função é a união dos domínios dos parâmetros de entrada. módulo - conjunto de procedimentos geralmente agrupado num só ficheiro. parâmetro actual - parâmetro passado a um procedimento aquando da sua invocação; pode tomar a forma de uma variável (no caso de passagem por referência) ou de uma expressão incluindo qualquer número de variáveis, constantes e/ operadores (no caso de passagem por valor). parâmetro formal - parâmetro declarado na interface do procedimento, geralmente com definição do respectivo domínio (tipo de dados). pilha - zona da memória central, associada à execução de um programa, destinada a fornecer o armazenamento necessário para os endereços de retorno nas chamadas a procedimentos (stack, em inglês), segundo uma filosofia FILO; a sua gestão pode ser crítica em situações tais como as de procedimentos recursivos. por referência - mecanismo segundo o qual o endereço da variável inscrita na lista de parâmetros actuais é passado como parâmetro a uma rotina. por valor - mecanismo segundo o qual o valor calculado da expressão inscrita na lista de parâmetros actuais é passado a uma rotina. 9 - encastoar, v. tr. pôr castão a; engastar (meter no engaste); embutir; encaixar [Costa79]
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved