Padrões de Projeto em Swift: quando e por quê
\n\nVocê já reescreveu o mesmo código dez vezes? Ou, ao abrir seu projeto antigo um mês depois, percebeu horrorizado que não conseguia entender nada? Bem-vindo ao clube. Geralmente isso acontece quando a arquitetura do aplicativo é construída "de qualquer jeito", sem nenhum sistema. E é aí que entram os padrões de projeto em Swift — modelos testados pelo tempo que transformam o caos em uma estrutura organizada e compreensível.
\n\nNeste artigo, não vamos apenas listar definições secas. Vamos entender quando realmente vale a pena aplicar cada padrão e quando ele só vai complicar sua vida. Pronto para organizar seu código? Vamos nessa.
\n\nO que são padrões de projeto e por que são importantes para Swift?
\n\nUm padrão de projeto não é um pedaço de código pronto, mas um esquema comprovado para resolver um problema típico. Imagine que você está construindo uma casa. Em vez de inventar como conectar as vigas toda vez, você usa conexões padrão — parafusos, porcas, cantoneiras. Os padrões são essas "conexões de construção" para o programador.
\n\nEm Swift, com seu poderoso sistema de protocolos e value types, muitos padrões ganham um novo significado. Por exemplo, o delegate aqui não é apenas uma interface, mas uma solução elegante baseada em protocolos. E o singleton em Swift pode ser implementado com uma constante let sem código extra.
\n\nSe você programa em Swift e não usa padrões, mais cedo ou mais tarde enfrentará problemas:
\n- \n
- Alto acoplamento de classes — uma mudança em um lugar quebra tudo. \n
- Duplicação de código — os mesmos algoritmos espalhados por todo o projeto. \n
- Dificuldade de teste — a lógica não está isolada. \n
Chega de sofrer. Vamos analisar os cinco padrões mais úteis para um desenvolvedor iOS.
\n\n1. Singleton — quando o objeto deve existir em uma única instância
\n\nO padrão mais popular e, talvez, o mais perigoso. Singleton garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela.
\n\nQuando usar:
\n- \n
- Gerenciamento de configurações do aplicativo (UserDefaults). \n
- Trabalho com rede (URLSession.shared). \n
- Registro de logs. \n
- Gerenciador do Core Data (persistent container). \n
Quando não usar: Se você cria um singleton para cada segunda classe, isso é um antipadrão. Ele dificulta os testes e cria dependências ocultas.
\n\nExemplo em Swift:
\nfinal class NetworkManager {\n static let shared = NetworkManager()\n \n private init() {} // Inicializador privado — ponto chave\n \n func fetchData(from url: URL) async throws -> Data {\n // Lógica real da requisição\n return Data()\n }\n}\n\n// Uso\nlet data = try await NetworkManager.shared.fetchData(from: url)\n\n\nObserve o private init(). Sem ele, qualquer um poderia criar uma segunda instância, e o padrão perderia o sentido.
2. Factory Method — quando criar um objeto é uma ciência completa
\n\nÀs vezes, a lógica de criação de um objeto é muito complexa para ser colocada no construtor. Ou o tipo do objeto precisa ser determinado em tempo real. O Factory Method move a criação para um método separado, que pode ser sobrescrito em subclasses.
\n\nQuando usar:
\n- \n
- Criação de diferentes tipos de view controllers dependendo do estado. \n
- Geração de modelos a partir de JSON com campos diferentes. \n
- Criação de elementos de UI com estilos diferentes. \n
Exemplo em Swift:
\nprotocol Button {\n var title: String { get }\n func render()\n}\n\nstruct PrimaryButton: Button {\n var title: String\n func render() { print("Botão azul: \\(title)") }\n}\n\nstruct SecondaryButton: Button {\n var title: String\n func render() { print("Botão cinza: