DEV Community

Cover image for Identificando Code Smells em JavaScript
Roberto Umbelino
Roberto Umbelino

Posted on

Identificando Code Smells em JavaScript

Fala devs, tudo bem? Nesse post vou falar sobre um tópico importante que às vezes passa despercebido, que são os Code Smells. 🐽

📑 Sobre

Code Smells são sinais de que algo pode estar errado no seu código. Eles não são bugs propriamente ditos, mas indicam problemas de design que podem levar a bugs no futuro. Basicamente, são aqueles "toques" que fazem você pensar: "Hmm, isso pode causar problemas mais tarde." Vamos dar uma olhada em alguns exemplos em JavaScript! 😎


1. Duplicated Code (Código Duplicado)

Código duplicado é tipo quando você cola o mesmo trecho de código em várias partes do seu projeto, aí depois dá aquele nó e vira uma bagunça pra manter tudo certinho.

function calcularPrecoTotalComImposto(preco, taxaImposto) {
    return preco + (preco * taxaImposto);
}

function calcularPrecoTotalComDescontoEImposto(preco, desconto, taxaImposto) {
    const precoComDesconto = preco - desconto;
    // Código duplicado
    return precoComDesconto + (precoComDesconto * taxaImposto);
}
Enter fullscreen mode Exit fullscreen mode

Refatorado:

// Código refatorado
function calcularPrecoTotalComImposto(preco, taxaImposto) {
    return preco + (preco * taxaImposto);
}

function calcularPrecoTotalComDescontoEImposto(preco, desconto, taxaImposto) {
    const precoComDesconto = preco - desconto;
    return calcularPrecoTotalComImposto(precoComDesconto, taxaImposto);
}
Enter fullscreen mode Exit fullscreen mode

2. Long Method (Método Longo)

Um método longo é aquele que faz muitas operações em sequência, fazendo com que fique muito difícil de entender e modificar.

// Método longo
function processarPedido(pedido) {
    // Validar pedido
    if (!pedido.id || !pedido.itens || pedido.itens.length === 0) {
        throw new Error('Pedido inválido');
    }

    // Calcular preço total
    const precoTotalInicial = itens.reduce((total, item) => total + item.preco * item.quantidade, 0);

    // Aplicar desconto
    const precoComDesconto = pedido.desconto ? precoTotalInicial - pedido.desconto : precoTotalInicial;

    // Aplicar imposto    
    const taxaImposto = 0.1;
    const precoFinal = precoComDesconto + precoComDesconto * taxaImposto;

    // Finalizar pedido
    console.log(`Pedido ${pedido.id} processado com preço total ${precoFinal.toFixed(2)}`);
}
Enter fullscreen mode Exit fullscreen mode

Refatorado:

// Método refatorado
function validarPedido(pedido) {
    if (!pedido.id || !pedido.itens || pedido.itens.length === 0) {
        throw new Error('Pedido inválido');
    }
}

function calcularPrecoTotal(itens, desconto = 0) {
    const precoTotal = itens.reduce((total, item) => total + item.preco * item.quantidade, 0);
    return precoTotal - desconto;
}

function aplicarImposto(precoTotal) {
    return precoTotal * 1.1;
}

function processarPedido(pedido) {
    validarPedido(pedido);
    const precoTotal = calcularPrecoTotal(pedido.itens, pedido.desconto);
    const precoTotalComImposto = aplicarImposto(precoTotal);
    console.log(`Pedido ${pedido.id} processado com preço total ${precoTotalComImposto.toFixed(2)}`);
}
Enter fullscreen mode Exit fullscreen mode

3. Large Class (Classe Grande)

Uma classe grande tem muitas responsabilidades ou métodos, o que pode indicar que ela está fazendo mais do que deveria e pode ser difícil de manter.

// Classe grande
class Pedido {
    constructor(id, itens, desconto) {
        this.id = id;
        this.itens = itens;
        this.desconto = desconto;
    }

    validar() {
        if (!this.id || !this.itens || this.itens.length === 0) {
            throw new Error('Pedido inválido');
        }
    }

    calcularPrecoTotal() {
        const precoTotal = itens.reduce((total, item) => total + item.preco * item.quantidade, 0);
        return precoTotal - desconto;
    }

    aplicarImposto(precoTotal) {
        return precoTotal * 1.1;
    }

    processar() {
        this.validar();
        let precoTotal = this.calcularPrecoTotal();
        precoTotal = this.aplicarImposto(precoTotal);
        console.log(`Pedido ${this.id} processado com preço total ${precoTotal}`);
    }
}
Enter fullscreen mode Exit fullscreen mode

Refatorado:

// Classes refatoradas
class ValidadorDePedido {
    static validar(pedido) {
        if (!pedido.id || !pedido.itens || pedido.itens.length === 0) {
            throw new Error('Pedido inválido');
        }
    }
}

class CalculadoraDePreco {
    static calcularPrecoTotal(itens, desconto = 0) {
        const precoTotal = itens.reduce((total, item) => total + item.preco * item.quantidade, 0);
        return precoTotal - desconto;
    }
}

class CalculadoraDeImposto {
    static aplicarImposto(precoTotal) {
        return precoTotal * 1.1;
    }
}

class ProcessadorDePedido {
    constructor(pedido) {
        this.pedido = pedido;
    }

    processar() {
        ValidadorDePedido.validar(this.pedido);
        const precoTotal = CalculadoraDePreco.calcularPrecoTotal(this.pedido.itens, this.pedido.desconto);
        const precoTotalComImposto = CalculadoraDeImposto.aplicarImposto(precoTotal);
        console.log(`Pedido ${this.pedido.id} processado com preço total ${precoTotalComImposto.toFixed(2)}`);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Feature Envy (Inveja de Funcionalidade)

Feature Envy ocorre quando uma classe utiliza excessivamente métodos ou dados de outra classe, o que pode indicar uma distribuição inadequada de responsabilidades.

// Feature Envy
class Cliente {
    constructor(nome, endereco) {
        this.nome = nome;
        this.endereco = endereco;
    }
}

class Pedido {
    constructor(cliente, itens) {
        this.cliente = cliente;
        this.itens = itens;
    }

    imprimirEtiquetaDeEnvio() {
        console.log(`Envio para: ${this.cliente.nome}, ${this.cliente.endereco}`);
    }
}
Enter fullscreen mode Exit fullscreen mode

Refatorado:

// Refatorado para mover a responsabilidade
class Cliente {
    constructor(nome, endereco) {
        this.nome = nome;
        this.endereco = endereco;
    }

    obterEtiquetaDeEnvio() {
        return `Envio para: ${this.nome}, ${this.endereco}`;
    }
}

class Pedido {
    constructor(cliente, itens) {
        this.cliente = cliente;
        this.itens = itens;
    }

    imprimirEtiquetaDeEnvio() {
        console.log(this.cliente.obterEtiquetaDeEnvio());
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Primitive Obsession (Obsessão por Primitivos)

Primitive Obsession é quando se utiliza primitivos (como strings e números) para representar conceitos que deveriam ser encapsulados em classes específicas, resultando em código menos legível e mais difícil de manter.

// Primitive Obsession
class Pedido {
    constructor(id, itens, codigoDesconto) {
        this.id = id;
        this.itens = itens;
        this.codigoDesconto = codigoDesconto;
    }

    aplicarDesconto() {
        if (this.codigoDesconto === 'SAVE10') {
            // Aplicar desconto de 10%
        } else if (this.codigoDesconto === 'SAVE20') {
            // Aplicar desconto de 20%
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Refatorado:

// Refatorado para usar classes específicas
class Desconto {
    constructor(codigo, porcentagem) {
        this.codigo = codigo;
        this.porcentagem = porcentagem;
    }

    aplicar(precoTotal) {
        return precoTotal - (precoTotal * this.porcentagem / 100);
    }
}

class Pedido {
    constructor(id, itens, desconto) {
        this.id = id;
        this.itens = itens;
        this.desconto = desconto;
    }

    aplicarDesconto(precoTotal) {
        if (this.desconto) {
            return this.desconto.aplicar(precoTotal);
        }
        return precoTotal;
    }
}
Enter fullscreen mode Exit fullscreen mode

Curtiu o post? Ainda há outros exemplos que poderiam ser citados, mas isso deixaria o post muito longo 😋, mas espero que esses exemplos ajudem de alguma forma a você conseguir identificar e corrigir os code smells no seu projeto. 🚀

Top comments (0)