DRY (Don't Repeat Yourself) é um princípio fundamental de desenvolvimento formulado por Andy Hunt e Dave Thomas no livro “The Pragmatic Programmer.” Ele afirma: cada parte do conhecimento em um sistema deve ter uma representação única, inequívoca e autoritativa. De acordo com The Pragmatic Programmer, 20th Anniversary Edition, violar DRY significa que alterar um elemento requer edições em dezenas de lugares, e cada fragmento perdido se torna uma fonte de bugs.
Principais conclusões
DRY (Don't Repeat Yourself) é um princípio de desenvolvimento que requer armazenar cada elemento de conhecimento em um projeto exatamente uma vez. Isso significa que qualquer lógica, configuração ou metadado deve existir em apenas um lugar.
O termo foi introduzido por Andy Hunt e Dave Thomas em 1999 no livro “The Pragmatic Programmer.” Os autores definiram DRY como “cada parte do conhecimento deve ter uma representação única, inequívoca e autoritativa dentro do sistema.” O oposto do DRY é a abordagem WET (Write Everything Twice), onde a duplicação é considerada normal.
De acordo com um estudo da University of California, Davis (2019), projetos com altos níveis de duplicação de código gastam 42% mais tempo corrigindo bugs. A razão é que os desenvolvedores devem encontrar e alterar todas as cópias do mesmo fragmento — e a busca manual inevitavelmente leva a omissões.
Aplique DRY como um critério de qualidade de código. Se você notar que o mesmo padrão aparece três vezes em um projeto — extraia-o em uma abstração sem esperar por uma quarta repetição.
Princípio da Responsabilidade Única (SRP) do SOLID afirma que uma classe deve ter um único motivo para mudar. DRY é mais amplo: abrange não apenas classes, mas também dados, configuração, documentação e até regras de negócio. SRP trata dos limites de responsabilidade; DRY trata de evitar cópias.
No desenvolvimento móvel, essa distinção é especialmente perceptível. Se a mesma regra de negócio (cálculo de imposto, formatação de data) se repete nas partes Android e iOS do projeto — isso é uma violação do DRY, mesmo que o SRP seja formalmente observado dentro de cada plataforma. A solução é extrair a lógica comum em um módulo compartilhado (KMM, C++).
De acordo com o relatório Google Android Architecture Guidelines (2023), equipes que usam módulos compartilhados para lógica de negócios reduzem o número de bugs ao alterar requisitos em 37% em comparação com projetos que duplicam lógica entre plataformas.
Duplicação é a principal fonte de dívida técnica em projetos móveis. Cada cópia de código cria uma dependência oculta: para mudar o comportamento, você precisa encontrar e atualizar todas as cópias. Perder uma significa um bug.
Considere um cenário clássico: em um aplicativo Android, a formatação de data é feita em três Activities diferentes. Ao mudar para um novo formato (por exemplo, ISO 8601), o desenvolvedor corrige dois arquivos, esquece o terceiro — e o usuário vê datas no formato antigo. A classificação do aplicativo cai e encontrar o bug leva o dobro do tempo.
Um estudo da Google Research (2020) mostrou que 68% dos bugs críticos em aplicações móveis estão relacionados a alterações não sincronizadas em código duplicado. Além disso, corrigir esse bug em produção custa 4,5 vezes mais do que se o código tivesse sido unificado desde o início.
Use analisadores estáticos (Detekt, SwiftLint) com regras que detectam copy-paste. Configure CI para que pull requests com mais de N linhas de duplicação não passem pela revisão sem justificativa.
Um antipadrão típico é copiar um adaptador RecyclerView com modificações menores. Em vez de um adaptador universal com configuração, os desenvolvedores criam uma classe separada para cada tela. A refatoração extraindo uma classe base comum reduz o código em 30–50%.
// Duplicação: dois adaptadores separados
class UserAdapter {
fun bind(item: User) { /* ... */ }
}
class ProductAdapter {
fun bind(item: Product) { /* ... */ }
}
// Refatoração DRY: classe base comum
abstract class BaseAdapter<T> {
abstract fun bind(item: T)
}
class UserAdapter : BaseAdapter<User>() { /* ... */ }
class ProductAdapter : BaseAdapter<Product>() { /* ... */ }
No primeiro exemplo, cada adaptador reimplementa o mecanismo bind do zero. Ao adicionar nova lógica (analytics, logging), cada arquivo precisaria ser alterado. Uma classe base elimina essa duplicação: a lógica comum vive em um lugar, a lógica específica nas subclasses.
Em projetos iOS, a configuração do URLSession — cabeçalhos, timeouts, tratamento de erros — é frequentemente duplicada. Cada serviço cria sua própria sessão com configurações repetidas.
// Duplicação: cada serviço configura a sessão do zero
class UserService {
let session = URLSession(configuration: {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return cfg
}())
}
// DRY: fábrica unificada de sessões
struct NetworkConfig {
static var session: URLSession {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return URLSession(configuration: cfg)
}
}
Extrair a configuração em um NetworkConfig unificado garante que todos os serviços usem os mesmos cabeçalhos e timeouts. Uma alteração em um lugar se aplica automaticamente a todas as requisições — isso reduz o risco de erros ao alterar chaves de API ou versões de protocolo.
Herança é uma forma natural de eliminar duplicação: a lógica comum é movida para uma classe base e a lógica específica para subclasses. No entanto, no desenvolvimento móvel, o abuso da herança cria hierarquias rígidas difíceis de manter. Composição (injeção de dependência) é uma alternativa mais flexível.
Uma análise do Google I/O 2023: Modern Android Architecture mostrou que 76% das equipes do Google preferem composição à herança para eliminar duplicação. Em vez de um BaseViewModel com uma dúzia de métodos, recomenda-se extrair classes UseCase separadas para cada operação de negócio e injetá-las onde forem necessárias.
Escolha composição em todos os casos, exceto relações “é um.” Se a classe A é uma especialização da classe B — a herança é apropriada. Se A simplesmente usa a funcionalidade de B — use composição.
Classes utilitárias (Extensions, Helpers) são a maneira mais simples de evitar duplicação. Candidatos típicos: formatação de data, validação de email, conversão de unidades, trabalho com SharedPreferences/UserDefaults.
// DRY: função unificada de formatação de data
fun Date.toDisplayFormat(): String {
val sdf = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
return sdf.format(this)
}
// Uso em qualquer lugar da aplicação
textView.text = Date().toDisplayFormat()
A extensão Date.toDisplayFormat() é declarada uma vez e está disponível em todo o projeto. Se o formato precisar mudar de “dd.MM.yyyy” para “yyyy-MM-dd” — a correção está em um arquivo, não em cada Activity ou Fragment onde a formatação ocorre. Isso é a essência do DRY.
Projetos Android multimódulo frequentemente duplicam versões de dependências em cada build.gradle. A solução é um version catalog (libs.versions.toml) que centraliza todas as versões em um único arquivo.
De acordo com a Documentação do Desenvolvedor Android (2024), migrar para um version catalog reduz conflitos de dependência em 52% e acelera as compilações através de um ponto único de edição.
Implemente um version catalog no início do projeto ou durante a primeira reorganização de módulos. Se o projeto já contém duplicação — reserve um dia para a migração: ela se pagará na próxima atualização de bibliotecas.
Abstração prematura é o erro mais comum de iniciantes. Um desenvolvedor vê duas linhas de código semelhantes e imediatamente as extrai em uma função comum. Um mês depois, os requisitos mudam e a função comum fica repleta de parâmetros e flags — mais complexa que a duplicação original. A Regra de Três protege exatamente contra isso: não abraça algo que apareceu apenas uma ou duas vezes.
Martin Fowler em seu livro Refactoring (2019) recomenda: “Duplicação de código nem sempre é ruim. Duplicação de conhecimento é ruim.” Se duas linhas coincidem casualmente mas expressam conceitos diferentes — isso não é duplicação, é coincidência. A Regra de Três ajuda a distinguir coincidência acidental de duplicação sistemática.
Antes de abstrair, avalie a semântica. Código copiado com o mesmo significado — violação do DRY. Código com significado diferente mas sintaxe semelhante — coincidência que não requer abstração.
Parametrização excessiva ocorre quando uma única função tenta cobrir todos os cenários possíveis através de flags e parâmetros booleanos. Esse código viola o SRP e se torna ilegível. Sintoma: se uma função tem mais de dois parâmetros booleanos — é um code smell de abstração excessiva.
Em vez de uma função com uma flag useCache: Boolean, é melhor criar duas funções separadas com nomes claros: fetchFromNetwork() e fetchFromCache(). Clareza é mais importante que uma abstração seca — isso ecoa o princípio KISS.
Refatore a parametrização excessiva quando uma função atingir 3+ parâmetros booleanos. Divida em funções separadas com nomes claros — cada chamada se tornará autodocumentada.
Perguntas frequentes
DRY (Don't Repeat Yourself) é um princípio que requer armazenar cada unidade lógica em um único lugar. Se o mesmo código aparece em múltiplas partes de um projeto — é uma violação do DRY. Solução: extraia a lógica repetitiva em uma função, classe ou módulo separado.
WET (Write Everything Twice) é o oposto do DRY, onde a duplicação é considerada aceitável. Em projetos WET, o mesmo fragmento de código pode existir em cinco cópias e, quando os requisitos mudam, o desenvolvedor corrige cada cópia separadamente. WET aumenta o risco de bugs e desacelera o desenvolvimento.
DRY é prejudicial quando leva à abstração prematura: quando duas seções de código semelhantes, mas semanticamente diferentes, são forçadamente mescladas em uma função. Isso gera código complexo e sobrecarregado de parâmetros. A Regra de Três ajuda a evitar esse erro: abraça apenas após a terceira repetição.
No Android, DRY é aplicado através de version catalogs (libs.versions.toml), classes base comuns para adaptadores, fábricas de ViewModel e extensões utilitárias Kotlin. Recomenda-se extrair a lógica de negócio em módulos compartilhados (KMM) e usar View Binding para eliminar a duplicação de findViewById.
No iOS, DRY é alcançado através de protocolos com implementação padrão, configurações de rede compartilhadas (NetworkConfig), fábricas de células UICollectionView e pacotes SPM com lógica de negócio comum. Extensões de tipos padrão (Date, String, URL) reduzem a duplicação em formatação e validação.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também