Refatoração de código legado em C++: estratégias e exemplos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course
```html

Refatoração de código legado em C++: guia passo a passo



Trabalhar com código legado em C++ é uma das tarefas mais difíceis, mas inevitáveis, na carreira de um desenvolvedor. O código legado frequentemente carece de testes unitários, contém números mágicos, vazamentos de memória e dependências confusas. No entanto, a refatoração desse código sem o risco de quebrar o sistema é possível se seguirmos estratégias comprovadas. Neste artigo, vamos analisar as principais abordagens para refatorar código C++ legado com exemplos práticos.



1. Preparação: criando uma rede de segurança



Antes de alterar o código, é necessário criar uma "rede de segurança" de testes. Para C++, isso é especialmente importante, pois erros podem levar a comportamento indefinido ou falha do programa.



Passo 1.1: Teste de caracterização

Escreva testes que capturem o comportamento atual do código, mesmo que esteja incorreto. Use frameworks como Google Test ou Catch2.



// Exemplo de teste de caracterização para uma função legada#include <gtest/gtest.h>

// Função legada que vamos refatorarint calculatePrice(int quantity, float basePrice) { if (quantity > 100) { return static_cast<int>(basePrice * quantity * 0.9); } else { return static_cast<int>(basePrice * quantity); }}

TEST(PriceCalculatorTest, LegacyBehavior) { EXPECT_EQ(calculatePrice(50, 10.0f), 500); EXPECT_EQ(calculatePrice(150, 10.0f), 1350); // 150*10*0.9}


Passo 1.2: Integração em CI/CD

Adicione os testes ao sistema de integração contínua. Isso garante que as alterações não quebrem a lógica existente.



2. Estratégia: Refatoração "Inteligente" sem Dor



Não tente reescrever tudo de uma vez. Use uma abordagem incremental.



2.1. Extração de Interfaces (Extract Interface)

Se o código está fortemente acoplado a estados globais ou classes específicas, comece extraindo abstrações.



Problema: A classe trabalha diretamente com o sistema de arquivos.

// Código legadoclass DataProcessor {public:    void process(const std::string& filename) {        std::ifstream file(filename);        // lógica complexa de processamento    }};


Solução: Introduzimos uma interface e injeção de dependência.

// Nova interfaceclass IFileReader {public:    virtual std::string read(const std::string& path) = 0;    virtual ~IFileReader() = default;};

// Refatoraçãoclass DataProcessor {public: explicit DataProcessor(std::unique_ptr<IFileReader> reader) : reader_(std::move(reader)) {} void process(const std::string& filename) { auto data = reader_->read(filename); // lógica de processamento }

private: std::unique_ptr<IFileReader> reader_;};


2.2. Substituição de números mágicos por constantes

Código legado frequentemente contém números cujo significado não é claro.



// Antes da refatoraçãoif (buffer.size() > 4096) {    resizeBuffer(8192);}

// Após a refatoraçãoconstexpr size_t kMaxBufferSize = 4096;constexpr size_t kResizeTarget = 8192;

if (buffer.size() > kMaxBufferSize) { resizeBuffer(kResizeTarget);}


3. Combate a vazamentos de memória e construções inseguras



Código C++ legado frequentemente usa ponteiros brutos e gerenciamento manual de memória. Substitua-os por ponteiros inteligentes e wrappers RAII.



3.1. Substituição de new/delete por std::unique_ptr

// Código antigo (risco de vazamento em caso de exceção)void processData() {    Data* data = new Data();    // ... trabalho com data ...    delete data;}

// Novo código (seguro)void processData() { auto data = std::make_unique<Data>(); // ... trabalho com data ... // delete não é necessário}


3.2. Uso de std::vector em vez de arrays brutos

```

Blogs

Book Recommendations