Padrões de Projeto em Kotlin com exemplos: de A a Z
Padrões de projeto (design patterns) são soluções comprovadas para problemas típicos de desenvolvimento. Em Kotlin, graças à sua sintaxe concisa e recursos poderosos (data class, object, extension functions, corrotinas), muitos padrões clássicos são implementados de forma mais simples e elegante do que em Java. Neste artigo, abordaremos os principais padrões em Kotlin, desde os criacionais até os comportamentais, com exemplos reais de código.
1. Padrões Criacionais (Creational)
Esses padrões gerenciam o processo de criação de objetos, tornando o sistema independente da forma como eles são criados.
1.1. Singleton (Singleton)
Garante que uma classe tenha apenas uma instância. Em Kotlin, usa-se a palavra-chave object para isso.
object DatabaseConnection { val connectionString: String = "jdbc:postgresql://localhost:5432/mydb"
fun connect() { println("Connecting to $connectionString") }}
fun main() { DatabaseConnection.connect() println(DatabaseConnection.connectionString)}Quando usar: para gerenciadores de configuração, pools de threads, caches.
1.2. Factory Method (Método Fábrica)
Define uma interface para criar um objeto, mas permite que as subclasses alterem o tipo do objeto criado.
// Produto abstratointerface Notification { fun send(message: String)}
// Produtos concretosclass EmailNotification : Notification { override fun send(message: String) = println("Email: $message")}
class SMSNotification : Notification { override fun send(message: String) = println("SMS: $message")}
// Fábricaabstract class NotificationFactory { abstract fun createNotification(): Notification
fun notifyUser(message: String) { val notification = createNotification() notification.send(message) }}
class EmailFactory : NotificationFactory() { override fun createNotification(): Notification = EmailNotification()}
class SMSFactory : NotificationFactory() { override fun createNotification(): Notification = SMSNotification()}
fun main() { val factory: NotificationFactory = EmailFactory() factory.notifyUser("Hello from Kotlin!")}Quando usar: quando o tipo dos objetos a serem criados não é conhecido antecipadamente.
1.3. Builder (Construtor)
Permite criar objetos complexos passo a passo. Em Kotlin, é bem implementado usando apply e argumentos nomeados.
data class Pizza( val size: String, val cheese: Boolean = false, val pepperoni: Boolean = false, val mushrooms: Boolean = false) { // Builder embutido class Builder(private val size: String) { private var cheese: Boolean = false private var pepperoni: Boolean = false private var mushrooms: Boolean = false
fun cheese(value: Boolean) = apply { cheese = value } fun pepperoni(value: Boolean) = apply { pepperoni = value } fun mushrooms(value: Boolean) = apply { mushrooms = value }
fun build() = Pizza(size, cheese, pepperoni, mushrooms) }}
fun main() { val pizza = Pizza.Builder("Large") .cheese(true) .pepperoni(true) .build() println(pizza)}Quando usar: quando há muitos parâmetros opcionais.
2. Padrões Estruturais (Structural)
Esses padrões definem maneiras de compor classes e objetos em estruturas maiores.
2.1. Adapter (Adaptador)
Permite que objetos com interfaces incompatíveis trabalhem juntos. Em Kotlin, é frequentemente usado com extension functions.
// Interface antigainterface OldLogger { fun logMessage(msg: String)}
// Interface novainterface NewLogger { fun log(level: Stri