Refatorando Funções Complexas com SOLID e Ciência da Computação
O Problema
Durante o desenvolvimento de sistemas, é comum encontrarmos funções que crescem descontroladamente. Veja o exemplo abaixo, que representa uma função responsável por determinar se um botão deve ser desabilitado com base em uma série de regras:
desabilitarBotao() {
let response = true;
const produto_alterado = this.produtos
.map(item => ({
...item,
valor_total: isNaN(item.valor_total) ? '' : item.valor_total.toString()
}))
.find(item => item.valor_total == '' || parseInt(item.valor_total) == 0);
if (this.produtos.length > 0 && !this.variables.select_multiple_product) {
response = !!produto_alterado;
this.disabled = !!produto_alterado;
} else {
response = this.produtos.length === 0 || !!produto_alterado;
this.disabled = false;
}
if (!this.variables.max_amount && this.produtos.length >= 1 && this.verificaEstoque()) {
response = true;
}
if (this.produtos.some(item => item.desconto > this.variables.max_discount)) {
response = true;
}
this.poTab.tabsChildren.forEach(tab => {
if (tab.label == 'Resumo') {
tab.disabled = response;
}
});
return response;
}
Principais Problemas
- Alta complexidade ciclomática: vários ifs aninhados, múltiplas variáveis de controle.
- Baixa coesão: mistura regras de negócio com lógica de interface (UI).
- Dificuldade para testes unitários: exige mock da estrutura do componente inteiro.
- Violação do SRP (Princípio da Responsabilidade Única) do SOLID.
A Solução com SOLID
Refatoramos a função utilizando o Princípio da Responsabilidade Única (SRP) e o Princípio da Inversão de Dependência (DIP), criando uma classe ProdutoValidator que encapsula toda a lógica de validação:
export class ProdutoValidator {
constructor(
private produtos: ProdutoInterface[],
private variables: VariablesInterface,
private verificaEstoqueFn: () => boolean
) {}
isProdutoInvalido(): boolean {
return this.produtos
.map(item => ({
...item,
valor_total: isNaN(item.valor_total) ? '' : item.valor_total.toString()
}))
.some(item => item.valor_total === '' || parseInt(item.valor_total) === 0);
}
excedeuDesconto(): boolean {
return this.produtos.some(item => item.desconto > this.variables.max_discount);
}
estoqueInsuficiente(): boolean {
return !this.variables.max_amount && this.produtos.length >= 1 && this.verificaEstoqueFn();
}
deveDesabilitar(): boolean {
const produtoInvalido = this.isProdutoInvalido();
const nenhumProduto = this.produtos.length === 0;
if (!this.variables.select_multiple_product) {
return this.produtos.length > 0 && produtoInvalido;
}
if (nenhumProduto || produtoInvalido) return true;
if (this.estoqueInsuficiente()) return true;
if (this.excedeuDesconto()) return true;
return false;
}
}
Agora a função principal do componente fica simples, clara e expressiva:
desabilitarBotao(): boolean {
const validator = new ProdutoValidator(this.produtos, this.variables, this.verificaEstoque.bind(this));
const desabilitar = validator.deveDesabilitar();
this.disabled = desabilitar;
this.poTab.tabsChildren.forEach(tab => {
if (tab.label === 'Resumo') {
tab.disabled = desabilitar;
}
});
return desabilitar;
}
Fundamento Científico
Essa abordagem segue princípios da Engenharia de Software e da Ciência da Computação:
Conceito | Aplicação |
---|---|
Complexidade Ciclomática (McCabe) | Reduzimos a quantidade de caminhos lógicos, facilitando testes |
SRP (Responsabilidade Única) | Separação entre lógica de decisão e atualização da UI |
DIP (Inversão de Dependência) | ProdutoValidator depende de abstrações (ex: função verificaEstoque) |
Coesão e Acoplamento | Aumentamos a coesão do validador e reduzimos acoplamento com a UI |
Testabilidade | Agora é possível escrever testes unitários isolados para o validador |
Conclusão
Refatorar usando SOLID não é apenas sobre “deixar bonito”. É sobre tornar o sistema escalável, testável, previsível e de fácil manutenção. Ao dividir a lógica em pequenas responsabilidades e respeitar os princípios da orientação a objetos, conseguimos evoluir o software com confiança.