Fundamentos de Engenharia de Software: Modelos, Processos e Garantia de Qualidade

O Ciclo de Vida e os Pilares da Engenharia de Software

O ciclo de vida de um software abrange o período desde a concepção inicial do produto até a sua descontinuação e retirada de uso. Este ciclo é tradicionalmente segmentado em fases estruturadas: levantamento de requisitos, planejamento, design, codificação, testes e manutenção. A engenharia de software sustenta essas fases através de três elementos fundamentais: ferramentas (o ambiente de suporte automatizado), métodos (as técnicas e abordagens técnicas para execução) e processos (o fluxo de trabalho e as etapas gerenciais).

Modelos de Processo de Desenvolvimento

A escolha do modelo de processo dita como as atividades do ciclo de vida serão organizadas e executadas.

Modelo em Cascata

Este modelo linear e sequencial exige que cada fase (requisitos, design, implementação, testes, manutenção) seja concluída e aprovdaa antes que a próxima se inicie. É orientado a documentos e ideal para projetos com requisitos estáveis e bem compreendidos. Suas principais limitações incluem a dificuldade em acomodar mudanças tardias e a descoberta de erros de design apenas nas fases finais de teste.

Modelo V e Modelos Evolutivos

O Modelo V estende o cascata associando cada fase de desenvolvimento à sua correspondente fase de teste, enfatizando a validação e verificação paralelas. Já os Modelos Evolutivos (como a Prototipagem) são iterativos, permitindo a construção de versões incrementais do software para refinar requisitos incertos. O Modelo em Espiral combina a natureza iterativa com uma forte ênfase em análise de riscos, sendo adequado para sistemas grandes, complexos e de alto risco.

Modelos Incrementais, Fonte e Unificado

  • Incremental: O sistema é entregue em partes funcionais. O primeiro incremento geralmente contém as funcionalidades centrais (núcleo), permitindo feedback antecipado do usuário.
  • Fonte (Fountain): Focado no desenvolvimento orientado a objetos, caracteriza-se pela sobreposição de fases (iteratividade) e ausência de barreiras rígidas entre análise, design e codificação.
  • Processo Unificado (UP/RUP): Um framework iterativo e incremental orientado a casos de uso e centrado na arquitetura. Divide-se nas fases de Iniciação, Elaboração, Construção e Transição.

Paradigmas e Metodologias de Desenvolvimento

As metodologias definem a abordagem técnica e gerencial para a construção do software.

  • Estruturada: Focada no processo e no fluxo de dados, utilizando a abordagem "top-down, stepwise refinement".
  • Jackson: Orientada a estruturas de dados, mapeando a entrada e saída de dados para a arquitetura do programa.
  • Orientada a Objetos (OO): Baseia-se em conceitos de encapsulamento, herança e polimorfismo, utilizando UML para modelagem.
  • Ágil: Prioriza a entrega contínua de valor, adaptação a mudanças e colaboração intensa com o cliente. Frameworks como Scrum (iterações chamadas de Sprints) e Extreme Programming - XP (focado em práticas de engenharia como programação em par, TDD e integração contínua) são amplamente utiilzados.

Engenharia de Requisitos e Design de Sistema

A análise de requisitos captura as expectativas do usuário, dividindo-se em requisitos funcionais (o que o sistema deve fazer) e não funcionais (restrições de desempenho, segurança, usabilidade e ambiente).

O design de software é dividido em Design Arquitetural (ou de Alto Nível), que define a estrutura modular e as interfaces, e o Design Detalhado, que especifica os algoritmos e estruturas de dados internas. O princípio cardinal do design é alcançar alta coesão e baixo acoplamento. Outras regras de ouro incluem: minimizar a profundidade da árvore de chamadas, favorecer alto fan-in (muitos módulos usando um módulo comum) e baixo fan-out (um módulo chamando poucos outros), e garantir que o escopo de controle de um módulo contenha seu escopo de ação.

Estratégias e Técnicas de Teste de Software

O teste é o processo de execução de um programa com o objetivo de encontrar defeitos. As estratégias tradicionais progridem do微观 para o macro:

  1. Teste Unitário: Foca na lógica interna e estruturas de dados de módulos individuais, frequentemente utilizando stubs e drivers.
  2. Teste de Integração: Combina módulos testados. Pode ser Top-Down (usando stubs) ou Bottom-Up (usando drivers).
  3. Teste de Regressão: Reexecuta subconjuntos de testes para garantir que novas alterações não introduziram defeitos.
  4. Teste de Fumaça (Smoke Testing): Avalia a estabilidade básica do sistema integrado antes de testes mais rigorosos.

Métodos de Caixa Preta e Caixa Branca

O teste de Caixa Preta ignora a estrutura interna, focando em requisitos funcionais usando técnicas como Partição de Equivalência e Análise de Valor Limite. O teste de Caixa Branca examina a lógica interna e o fluxo de controle.

Para ilustrar a métrica de complexidade de McCabe e as técnicas de cobertura de caixa branca, considere o seguinte trecho de código:

def classificar_risco_cliente(idade, score_credito, renda_mensal):
    nivel_risco = "baixo"
    
    if idade > 65 or score_credito < 300:
        if renda_mensal < 2000 and score_credito < 300:
            nivel_risco = "critico"
        else:
            nivel_risco = "alto"
            
    return nivel_risco

A Complexidade Ciclomática de McCabe é calculada pela fórmula V(G) = Arestas - Nós + 2 ou V(G) = Regiões Fechadas + 1. No grafo de fluxo de controle do código acima, existem 2 predicados compostos, o que resulta em um V(G) = 3, indicando que são necessários no mínimo 3 caminhos básicos independentes para cobrir a lógica.

As técnicas de cobertura lógica incluem: Cobertura de Instrução (executar cada linha), Cobertura de Decisão (cada branch verdadeiro/falso), Cobertura de Condição (cada subcondição verdadeiro/falso) e Cobertura de Caminho (executar todas as combinações possíveis de caminhos).

Manutenção, Qualidade e Métricas

A manutenção é a fase mais longa do ciclo de vida, classificada em: Corretiva (reparar defeitos ocultos), Adaptativa (ajuste a novos ambientes/hardware), Perfeitiva (melhoria de desempenho ou novas funções) e Preventiva (melhorias para facliitar manutenções futuras).

As métricas de qualidade quantitativas incluem:

  • Confiabilidade: Probabilidade de operação sem falhas. Medida por MTTF / (1 + MTTF), onde MTTF é o Tempo Médio Entre Falhas.
  • Disponibilidade: Medida por MTBF / (1 + MTBF), onde MTBF é o Tempo Médio Entre Falhas (incluindo reparo).
  • Manutenibilidade: Medida por 1 / (1 + MTTR), onde MTTR é o Tempo Médio Para Reparo.

Modelos como o ISO/IEC 9126 (focando em Funcionalidade, Confiabilidade, Usabilidade, Eficiência, Manutenibilidade e Portabilidade) e o McCall (focado em Operação, Revisão e Transição) fornecem frameworks para avaliar a qualidade do produto.

Gestão de Projetos, Riscos e Configuração

A estimativa de custos utiliza modelos como o COCOMO (Construtive Cost Model), que evoluiu do modelo básico (estático) para o COCOMO II, incorporando pontos de função e objetos.

O cronograma é visualizado através de Diagramas de Gantt (foco em duração e paralelismo) e Redes PERT (foco em dependências e caminho crítico). Abaixo, uma implementação lógica para cálculo de tempos e identificação do caminho crítico em uma rede PERT:

def calcular_pert(atividades):
    # atividades é um dicionário: {no: [(no_destino, duracao), ...]}
    nos = set(atividades.keys())
    for destino in [dest for dest, _ in sum(atividades.values(), [])]:
        nos.add(destino)
        
    ve = {no: 0 for no in nos}  # Tempo Mais Cedo
    vl = {no: float('inf') for no in nos}  # Tempo Mais Tarde
    
    # Cálculo Forward Pass (Tempo Mais Cedo)
    for no_atual in sorted(nos):
        if no_atual in atividades:
            for prox_no, duracao in atividades[no_atual]:
                if ve[no_atual] + duracao > ve[prox_no]:
                    ve[prox_no] = ve[no_atual] + duracao
                    
    tempo_total = ve[max(nos, key=lambda x: ve[x])]
    vl[max(nos, key=lambda x: ve[x])] = tempo_total
    
    # Cálculo Backward Pass (Tempo Mais Tarde)
    for no_atual in sorted(nos, reverse=True):
        if no_atual in atividades:
            for prox_no, duracao in atividades[no_atual]:
                if vl[prox_no] - duracao < vl[no_atual]:
                    vl[no_atual] = vl[prox_no] - duracao
                    
    # Identificação do Caminho Crítico (Folga = 0)
    caminho_critico = []
    for no in nos:
        folga = vl[no] - ve[no]
        if folga == 0:
            caminho_critico.append(no)
            
    return caminho_critico, tempo_total

A Gestão de Configuração controla mudanças através de repositórios (Biblioteca de Desenvolvimento, Biblioteca Controlada e Biblioteca de Produto). A Gestão de Riscos envolve a identificação, projeção (Probabilidade x Impacto) e mitigação de riscos de projeto, técnicos e de negócios, utilizando planos RMMM (Risk Mitigation, Monitoring, and Management).

Tolerância a Falhas e Revisão de Software

A garantia de qualidade passa por revisões formais (focadas no design e na lógica do código) e pela implementação de técnicas de tolerância a falhas. A tolerância a falhas baseia-se primordialmente no conceito de redundância, que pode ser classificada em:

  • Redundância de Estrutura: Inclusão de hardware ou módulos de software adicionais (estática, dinâmica ou híbrida) para assumir o controle em caso de falha.
  • Redundância de Informação: Adição de bits de verificação (como paridade ou CRC) para detectar e corrigir erros em dados.
  • Redundância de Tempo: Reexecução de instruções ou transações para mitigar erros transitórios.
  • Tecnologias Adicionais: Software e hardware auxiliares necessários para detectar, isolar e recuperar o sistema de estados errôneos.

Tags: engenharia-de-software ciclo-de-vida modelos-agil teste-de-software gestao-de-projetos

Publicado em 7-6 16:49