Introdução: Por que SOLID é a base de um código Swift de qualidade
Todo desenvolvedor iOS/macOS, mais cedo ou mais tarde, se depara com uma situação em que o código se torna um "espaguete": mudanças em um lugar quebram todo o aplicativo, e os testes se tornam um pesadelo. É aqui que os princípios SOLID entram em ação — cinco regras fundamentais de design orientado a objetos, formuladas por Robert Martin.
Neste artigo, vamos analisar cada princípio SOLID com exemplos em Swift, mostrar como sua violação leva a problemas e fornecer soluções prontas. Você aprenderá a escrever código fácil de manter, estender e testar.
1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
Teoria
SRP afirma: uma classe deve ter apenas um motivo para mudar. Em outras palavras, cada classe é responsável por exatamente uma funcionalidade.
Violação do SRP (código ruim)
class UserManager { func createUser(name: String, email: String) { // Criação de usuário saveToDatabase(user: User(name: name, email: email)) sendWelcomeEmail(to: email) logEvent("User created: \\(name)") } private func saveToDatabase(user: User) { /* ... */ } private func sendWelcomeEmail(to email: String) { /* ... */ } private func logEvent(_ message: String) { /* ... */ }}Aqui, UserManager é responsável por criar, salvar, enviar e-mails e registrar — quatro motivos para mudar.
Correção (código bom)
class UserService { private let repository: UserRepository private let emailService: EmailService private let logger: Logger init(repository: UserRepository, emailService: EmailService, logger: Logger) { self.repository = repository self.emailService = emailService self.logger = logger } func createUser(name: String, email: String) { let user = User(name: name, email: email) repository.save(user) emailService.sendWelcome(to: email) logger.log("User created: \\(name)") }}
class UserRepository { func save(_ user: User) { /* apenas salvar */ }}class EmailService { func sendWelcome(to email: String) { /* apenas enviar */ }}class Logger { func log(_ message: String) { /* apenas registrar */ }}Agora, cada classe tem uma única responsabilidade, facilitando os testes e a manutenção.
2. Open/Closed Principle (OCP) — Princípio Aberto/Fechado
Teoria
As classes devem estar abertas para extensão, mas fechadas para modificação. Adicionamos novas funcionalidades por meio de herança ou protocolos, sem tocar no código existente.
Violação do OCP
class PaymentProcessor { func process(payment: Any, type: String) { if type == "card" { // Processamento de cartão } else if type == "paypal" { // Processamento de PayPal } // Ao adicionar um novo tipo — alteramos esta classe }}Correção usando protocolos
protocol PaymentMethod { func process(amount: Double)}
class CardPayment: PaymentMethod { func process(amount: Double) { /* processamento de cartão */ }}
class PayPalPayment: PaymentMethod { func process(amount: Double) { /* processamento de PayPal */ }}
class PaymentProcessor { func process(payment: PaymentMethod, amount: Double) { payment.process(amount: amount) }}
// Adicionamos um novo tipo sem alterar o código existenteclass ApplePayPayment: PaymentMethod { func process(amount: Double) { /* processamento de Apple Pay */ }}Agora, para adicionar um novo método de pagamento, basta criar uma nova classe que implemente o protocolo PaymentMethod.