SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
28.11.2024

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!

Video

Did you like this article? Rate it from 1 to 5:

Thank you for voting!