SOLID — cinco princípios de programação orientada a objetos formulados por Robert C. Martin (Uncle Bob) no início dos anos 2000. De acordo com a DigitalOcean, 2024, SOLID significa Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation e Dependency Inversion. Esses princípios formam a base da Clean Architecture e são aplicados no desenvolvimento Android (MVP, MVVM, Clean Architecture) e iOS (VIPER, TCA).
Principais conclusões
SOLID — acrônimo mnemônico que representa cinco princípios de design orientado a objetos. O termo foi introduzido por Robert C. Martin no artigo «Design Principles and Design Patterns» (2000) e posteriormente popularizado no livro «Agile Software Development: Principles, Patterns, and Practices» (2002). SOLID não é um framework ou biblioteca — é um conjunto de práticas que tornam o código menos acoplado, mais testável e fácil de modificar.
De acordo com Clean Coder Blog, 2014, cada princípio SOLID resolve um problema de design específico: SRP combate classes God, OCP previne mudanças em cascata, LSP protege contra herança incorreta, ISP evita interfaces grandes e DIP reduz o acoplamento forte. Juntos formam a base da Clean Architecture, que é usada em projetos Android com MVP, MVVM e MVI.
Single Responsibility Principle (SRP) — princípio da responsabilidade única. A formulação: «Uma classe deve ter apenas uma razão para mudar». Isso significa que cada módulo ou classe é responsável por exatamente uma funcionalidade ou uma entidade de domínio. Se uma classe gerencia tanto usuários quanto envio de emails — ela tem duas razões para mudar, violando SRP.
De acordo com Robert C. Martin, 2002, SRP é o princípio mais importante e ao mesmo tempo o mais violado. No desenvolvimento móvel, SRP é frequentemente violado em Activity/Fragment ao combinar lógica de UI, navegação, rede e lógica de negócios. A solução é extrair cada camada em uma classe separada: ViewModel para lógica de UI, Repository para dados, NavController para navegação.
Considere a classe UserManager, que carrega um perfil, salva configurações e envia emails. São três responsabilidades distintas, cada uma deve ser extraída em uma classe separada: UserProfileRepository (carregamento), UserSettingsStorage (salvamento) e EmailService (envio). O código cliente (ViewModel) usa as três através de Dependency Injection, e cada classe é facilmente testada isoladamente e muda sem afetar as outras.
// ❌ Violação SRP: Activity conhece rede, BD e UI
class ProfileActivity : AppCompatActivity() {
fun loadProfile() {
api.getUser() // Chamada de rede
db.saveUser() // Operação de BD
updateUI() // Atualização de UI
}
}
// ✅ SRP cumprido: camadas estão separadas
class ProfileViewModel : ViewModel() {
private val repo = UserRepository()
fun loadProfile() { repo.getUser() }
}
Sinais de violação de SRP: uma classe com mais de 200 linhas, métodos de diferentes domínios, mudanças frequentes por razões diferentes. Para desenvolvimento Android, a regra é simples: Activity lida apenas com o ciclo de vida da tela, ViewModel lida com o estado da UI, Repository lida com fontes de dados.
O princípio SRP se aplica não apenas a classes mas também à arquitetura de nível de serviço. Cada microsserviço lida com uma entidade de domínio: UserService — apenas usuários, PaymentService — apenas pagamentos, NotificationService — apenas notificações. Isso permite escalar, implantar e testar serviços de forma independente. Em aplicativos móveis, SRP no nível de microsserviços se manifesta na separação de clientes API por domínio.
Open-Closed Principle (OCP) — classes devem estar abertas para extensão (novo comportamento pode ser adicionado) e fechadas para modificação (código existente não é alterado). Isso é alcançado através de polimorfismo, classes abstratas e interfaces. Em vez de adicionar if-else a um método existente, uma nova implementação de interface é criada.
De acordo com Clean Coder Blog, 2014, OCP funciona melhor com o padrão Strategy. Por exemplo, se um aplicativo suporta diferentes métodos de pagamento (Google Pay, Apple Pay, PayPal), não é necessário adicionar um switch-case ao processador de pagamentos. Cada método de pagamento implementa uma interface comum PaymentGateway, e um novo sistema de pagamento é adicionado como uma nova classe sem modificar as existentes.
// ✅ OCP: aberto para extensão, fechado para modificação
interface PaymentGateway {
fun processPayment(amount: Double): Boolean
}
class GooglePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
// Novo sistema de pagamento — sem alterar código existente
class ApplePayGateway : PaymentGateway {
override fun processPayment(amount: Double) = true
}
Liskov Substitution Principle (LSP) — princípio de substituição de Barbara Liskov. Se S é um subtipo de T, então objetos do tipo T podem ser substituídos por objetos do tipo S sem alterar as propriedades do programa. Formalmente: uma função que usa uma classe base deve funcionar corretamente com qualquer uma de suas subclasses. Se uma subclasse lança uma exceção onde a classe base não lança — LSP foi violado.
De acordo com Robert C. Martin, 2002, LSP é o princípio SOLID mais difícil de entender. O exemplo clássico de violação é a classe Square herdando de Rectangle. Se setWidth em Square define tanto largura quanto altura, o código cliente que espera comportamento de Rectangle obtém um resultado inesperado. No desenvolvimento móvel, LSP é frequentemente violado ao herdar ViewModel — quando uma ViewModel filha adiciona dependências obrigatórias.
// ❌ Violação LSP: Square quebra o comportamento de Rectangle
open class Rectangle(open var width: Int, open var height: Int)
class Square(side: Int) : Rectangle(side, side) {
override var width
get() = super.width
set(value) { super.setBoth(value, value) }
}
Interface Segregation Principle (ISP) — clientes não devem depender de interfaces que não usam. Em vez de uma única interface «gorda», crie várias interfaces estreitas e especializadas. Se uma classe implementa uma interface mas alguns métodos lançam UnsupportedOperationException ou ficam vazios — é um sinal claro de violação de ISP.
De acordo com DigitalOcean, 2024, ISP é especialmente relevante no desenvolvimento móvel ao projetar ViewModel e Repository. Em vez de uma única interface UserRepository com todos os métodos CRUD, é melhor criar QueryUserRepository (somente leitura) e CommandUserRepository (escrita). Então um cliente somente leitura (elemento UI) depende apenas da interface Query e não sabe nada sobre métodos de escrita.
// ❌ Interface grande — cliente é forçado a implementar métodos desnecessários
interface UserOperations {
fun getUser(id: String): User
fun saveUser(user: User)
fun deleteUser(id: String)
fun exportUsers(): File
}
// ✅ ISP: interfaces segregadas
interface UserReader { fun getUser(id: String): User }
interface UserWriter { fun saveUser(user: User) }
interface UserDeleter { fun deleteUser(id: String) }
Dependency Inversion Principle (DIP) — módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações (interfaces). Abstrações não devem depender de detalhes — detalhes devem depender de abstrações. Isso não é «Dependency Injection» (DI), embora DI seja uma forma comum de implementar DIP.
De acordo com Robert C. Martin, 2019, DIP é a base da Clean Architecture. ViewModel (alto nível) não deve criar diretamente uma instância de RetrofitApi (detalhe). Em vez disso, ViewModel depende de uma interface UserRepository, e a implementação concreta UserRepositoryImpl com Retrofit é passada através do construtor. No Android, DIP é implementado através de Hilt/Dagger ou Koin: todas as dependências são fornecidas através do contêiner DI.
// ✅ DIP: Module depende de abstração, não de detalhes
class UserRepositoryImpl(
private val api: UserApi, // Depende da interface
private val db: UserDao // Depende da interface
) : UserRepository {
override suspend fun getUser(id: String): User {
return api.fetchUser(id)
}
}
// Hilt DI: detalhes são conectados através do módulo DI
@Module
object NetworkModule {
@Provides
fun provideUserApi(retrofit: Retrofit): UserApi =
retrofit.create(UserApi::class.java)
}
SOLID no desenvolvimento móvel é aplicado em todos os níveis: da arquitetura do aplicativo a classes individuais. Em projetos Android, Clean Architecture divide o código em três camadas: domain (lógica de negócios — independente de frameworks), data (repositórios, API, BD) e presentation (UI, ViewModel). A camada domain usa princípios SOLID: casos de uso (SRP), interfaces de repositório (DIP), classes de entidade (OCP + LSP).
De acordo com Android Developers Guide, 2025, SRP no Android se manifesta na separação de ViewModel, Repository e Mapper. OCP — ao adicionar novas fontes de dados através da interface DataSource. LSP — no tratamento uniforme de Result em diferentes repositórios. ISP — na abordagem CQRS (separação de repositórios Read/Write). DIP — através de Hilt/Koin para injeção de dependências.
| Princípio | Problema sem ele | Solução em projeto móvel |
|---|---|---|
| SRP | Activity com 1000+ linhas | ViewModel + UseCase + Repository |
| OCP | switch-case por tipo de pagamento | Strategy: interface PaymentGateway |
| LSP | Erro ao substituir BaseViewModel | Verificar contrato de subclasses |
| ISP | UnsupportedOperationException | Separação Reader / Writer |
| DIP | ViewModel cria Retrofit manualmente | Contêiner DI Hilt / Koin |
Erros de SOLID geralmente estão relacionados à sobrecomplicação excessiva do código. O primeiro — seguir os princípios literalmente sem considerar o contexto. Dividir uma classe UserService em 10 interfaces e 15 classes apenas para um ISP «limpo» é superengenharia. SOLID é uma ferramenta, não um objetivo. O segundo erro — confundir SRP com «um método = uma responsabilidade». Uma classe pode ter vários métodos se todos pertencerem à mesma área de responsabilidade.
De acordo com Simple Thread, 2024, o terceiro erro — ignorar LSP ao herdar ViewModel no Android. Se a ViewModel base espera LiveData mas a filha usa StateFlow — o código cliente inscrito no LiveData não receberá atualizações. O quarto — violar DIP por causa de testes: RepositoryImpl cria diretamente uma instância de OkHttpClient, impossibilitando testes unitários.
A regra de ouro: aplique SOLID quando resolver um problema real (mudanças frequentes, dificuldade de teste, duplicação). Para telas CRUD simples, a aderência estrita a todos os cinco princípios é excessiva. Para lógica de negócios, cálculos financeiros e interações com API, SOLID é essencial.
Clean Architecture (Robert C. Martin, 2012) — aplicação direta de SOLID no nível de camadas do aplicativo. SRP define os limites dos casos de uso (cada caso de uso — uma classe). OCP é implementado através de interfaces de repositório (Data Layer pode mudar sem modificar Domain). ISP fornece separação do caso de uso em limites de entrada/saída. DIP — direção das dependências para dentro da camada Domain. LSP garante que qualquer implementação de repositório seja substituível sem quebrar os casos de uso.
Perguntas frequentes
SOLID — cinco regras para escrever código fácil de mudar, testar e entender. Cada letra é um princípio: não escreva classes grandes (SRP), não mude código existente — adicione novo (OCP), não quebre o comportamento das subclasses (LSP) e outros.
SRP (Single Responsibility) é considerado o mais importante porque sua violação leva a God classes — classes enormes difíceis de testar e modificar. No entanto, sem DIP (Dependency Inversion) o código permanece fortemente acoplado, o que também é crítico.
Não é obrigatório, mas é altamente recomendado para projetos comerciais com ciclo de vida longo. Para aplicativos simples (uma tela, sem lógica de negócios), SOLID pode ser excessivo. Para projetos com 50+ telas e 3+ desenvolvedores, SOLID é o mínimo necessário.
Consequências: as classes ficam «gordas» (1000+ linhas), uma mudança em um lugar quebra três outros, é impossível escrever testes unitários, adicionar uma nova funcionalidade leva semanas em vez de dias. Com o tempo, o código se torna um «Big Ball of Mud» — emaranhado e frágil.
Sinais de conformidade: cada classe tem menos de 200 linhas, alterar uma funcionalidade não afeta 5+ arquivos, testes podem ser escritos sem simular 10 dependências, um novo desenvolvedor entende a estrutura em um dia. Ferramentas como SonarQube e detekt ajudam a identificar violações de SRP e DIP.
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