Aula 166: Princípios SOLID em Swift
Nesta aula, exploraremos os princípios SOLID, um conjunto de princípios de design destinados a tornar os designs de software mais compreensíveis, flexíveis e manuteníveis. O acrônimo SOLID representa:
- S: Princípio da Responsabilidade Única (SRP)
- O: Princípio Aberto/Fechado (OCP)
- L: Princípio da Substituição de Liskov (LSP)
- I: Princípio da Segregação de Interfaces (ISP)
- D: Princípio da Inversão de Dependência (DIP)
Vamos nos aprofundar em cada princípio, acompanhados por exemplos de código em Swift.
1. Princípio da Responsabilidade Única (SRP)
O Princípio da Responsabilidade Única afirma que uma classe deve ter uma, e somente uma, razão para mudar. Isso significa que cada classe deve ter uma única responsabilidade, tornando assim o sistema mais fácil de entender e gerenciar.
Exemplo
class Usuario {
var nome: String
init(nome: String) {
self.nome = nome
}
}
class RepositorioDeUsuarios {
func salvar(usuario: Usuario) {
// Código para salvar o usuário
}
}
class ServicoDeUsuarios {
func enviarEmailDeBoasVindas(usuario: Usuario) {
// Código para enviar um email de boas-vindas
}
}
Neste exemplo, Usuario
, RepositorioDeUsuarios
e ServicoDeUsuarios
têm responsabilidades distintas. A classe Usuario
representa os dados do usuário, RepositorioDeUsuarios
lida com a persistência de dados, e ServicoDeUsuarios
gerencia operações relacionadas ao usuário.
2. Princípio Aberto/Fechado (OCP)
O Princípio Aberto/Fechado afirma que as entidades de software devem ser abertas para extensão, mas fechadas para modificação. Isso significa que devemos ser capazes de adicionar novas funcionalidades sem alterar o código existente.
Exemplo
protocol Forma {
func area() -> Double
}
class Retangulo: Forma {
var largura: Double
var altura: Double
init(largura: Double, altura: Double) {
self.largura = largura
self.altura = altura
}
func area() -> Double {
return largura * altura
}
}
class Circulo: Forma {
var raio: Double
init(raio: Double) {
self.raio = raio
}
func area() -> Double {
return Double.pi * raio * raio
}
}
class CalculadoraDeArea {
func calcularArea(para formas: [Forma]) -> Double {
return formas.reduce(0) { $0 + $1.area() }
}
}
Aqui, podemos estender nosso sistema criando novas formas como Triangulo
ou Quadrado
sem modificar a CalculadoraDeArea
. Isso respeita o OCP.
3. Princípio da Substituição de Liskov (LSP)
O Princípio da Substituição de Liskov afirma que objetos de uma superclasse devem ser substituíveis por objetos de uma subclasse sem afetar a correção do programa. Isso requer que as subclasses se comportem como esperado quando substituídas por sua classe pai.
Exemplo
class Ave {
func voar() {
print("Voando")
}
}
class Andorinha: Ave {
// A andorinha pode voar
}
class Avestruz: Ave {
override func voar() {
fatalError("Avestruzes não podem voar!")
}
}
A classe Avestruz
viola o LSP porque não pode substituir Ave
sem quebrar a funcionalidade. Uma abordagem melhor seria separar aves voadoras de aves não voadoras.
4. Princípio da Segregação de Interfaces (ISP)
O Princípio da Segregação de Interfaces afirma que nenhum cliente deve ser forçado a depender de métodos que não utiliza. Ele incentiva a definição de interfaces menores e mais específicas em vez de uma grande interface de uso geral.
Exemplo
protocol Trabalhavel {
func trabalhar()
}
protocol Comestivel {
func comer()
}
class Funcionario: Trabalhavel, Comestivel {
func trabalhar() {
print("Trabalhando")
}
func comer() {
print("Comendo")
}
}
class Robot: Trabalhavel {
func trabalhar() {
print("Trabalhando")
}
}
Neste exemplo, as interfaces Trabalhavel
e Comestivel
são segregadas, permitindo que o Robot
implemente apenas o que é necessário.
5. Princípio da Inversão de Dependência (DIP)
O Princípio da Inversão de Dependência afirma que módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações. O objetivo é reduzir o acoplamento entre diferentes módulos.
Exemplo
protocol ServicoDeNotificacao {
func enviarNotificacao(mensagem: String)
}
class ServicoDeNotificacaoPorEmail: ServicoDeNotificacao {
func enviarNotificacao(mensagem: String) {
print("Enviando email: \(mensagem)")
}
}
class Usuario {
var servicoDeNotificacao: ServicoDeNotificacao
init(servicoDeNotificacao: ServicoDeNotificacao) {
self.servicoDeNotificacao = servicoDeNotificacao
}
func notificarUsuario() {
servicoDeNotificacao.enviarNotificacao(mensagem: "Bem-vindo!")
}
}
// Uso
let servicoEmail = ServicoDeNotificacaoPorEmail()
let usuario = Usuario(servicoDeNotificacao: servicoEmail)
usuario.notificarUsuario()
Neste exemplo, a classe Usuario
depende da abstração ServicoDeNotificacao
em vez de uma implementação concreta. Isso nos permite alterar os métodos de notificação com facilidade.
Conclusão
Compreender e aplicar os princípios SOLID em Swift ajuda a criar uma base de código mais modular e manutenível. Seguindo esses princípios, os desenvolvedores podem garantir que suas aplicações sejam mais fáceis de entender e modificar ao longo do tempo. Tente incorporar esses princípios em seu próximo projeto e observe como eles podem melhorar seu design!