Middle
Que padrões de design podem ser usados na linguagem Kotlin?
sobes.tech IA
Resposta da IA
No Kotlin, frequentemente aplicam-se os seguintes padrões:
- Singleton: Implementa-se facilmente através de
object.object MeuSingleton { fun fazerAlgo() { // ... } } - Factory Method: Utiliza uma função abstrata na superclasse ou interface, que as subclasses implementam para criar objetos.
interface Produto { fun operacao() } class ProdutoConcretoA : Produto { override fun operacao() { // ... } } abstract class Criador { abstract fun metodoFabrica(): Produto fun algumaOperacao(): Produto { val produto = metodoFabrica() // ... return produto } } class CriadorConcretoA : Criador() { override fun metodoFabrica(): Produto { return ProdutoConcretoA() } } - Abstract Factory: Grupo de fábricas para criar famílias relacionadas de objetos.
interface ProdutoAbstratoA { fun funcaoUtilA() } interface ProdutoAbstratoB { fun funcaoUtilB() fun outraFuncaoUtilB(colaborador: ProdutoAbstratoA) } interface FabricaAbstrata { fun criarProdutoA(): ProdutoAbstratoA fun criarProdutoB(): ProdutoAbstratoB } // Implementações concretas de produtos e fábricas... - Builder: Facilita a criação de objetos complexos passo a passo.
class Pizza private constructor( val crosta: String, val molho: String?, val ingredientes: List<String> ) { data class Builder( var crosta: String = "fina", var molho: String? = null, val ingredientes: MutableList<String> = mutableListOf() ) { fun comCrosta(crosta: String) = apply { this.crosta = crosta } fun comMolho(molho: String) = apply { this.molho = molho } fun adicionarIngrediente(ingrediente: String) = apply { this.ingredientes.add(ingrediente) } fun construir() = Pizza(crosta, molho, ingredientes) } } fun main() { val pizza = Pizza.Builder() .comCrosta("grossa") .comMolho("tomate") .adicionarIngrediente("pepperoni") .adicionarIngrediente("cogumelos") .construir() // ... usar pizza } - Adapter: Permite que objetos com interfaces incompatíveis trabalhem juntos.
open class Adaptado { fun operacaoEspecifica() { // ... } } interface Alvo { fun operacao() } class Adaptador(private val adaptado: Adaptado) : Alvo { override fun operacao() { adaptado.operacaoEspecifica() } } - Decorator: Adiciona novas funções aos objetos existentes sem alterar sua estrutura. Extensões e delegação em Kotlin facilitam muito isso.
interface FonteDeDados { fun lerDados(): String fun escreverDados(dados: String) } class FonteDeDadosArquivo(private val nomeArquivo: String) : FonteDeDados { override fun lerDados(): String { // Leitura do arquivo return "" } override fun escreverDados(dados: String) { // Escrita no arquivo } } class DecoradorEncriptacao(private val fonte: FonteDeDados) : FonteDeDados by fonte { override fun lerDados(): String { // Descriptografar dados val dados = fonte.lerDados() return dados // dados descriptografados } override fun escreverDados(dados: String) { // Criptografar dados fonte.escreverDados(dados) // dados criptografados } } - Observer: Define a dependência "um para muitos". Coroutines Flow e propriedades delegadas do Kotlin podem ser usados para implementação.
// Implementação simples sem Flow interface Observador { fun atualizar(mensagem: String) } class Assunto { private val observadores = mutableListOf<Observador>() fun anexar(observador: Observador) { observadores.add(observador) } fun desanexar(observador: Observador) { observadores.remove(observador) } fun notificarObservadores(mensagem: String) { observadores.forEach { it.atualizar(mensagem) } } } - Strategy: Define uma família de algoritmos, encapsula cada um e os torna intercambiáveis.
interface EstrategiaDePagamento { fun pagar(valor: Double) } class PagarPorCartaoCredito(private val numeroCartao: String) : EstrategiaDePagamento { override fun pagar(valor: Double) { println("Pagando $valor usando Cartão de Crédito $numeroCartao") } } class PagarPorPayPal(private val email: String) : EstrategiaDePagamento { override fun pagar(valor: Double) { println("Pagando $valor usando PayPal $email") } } class Pedido(private val estrategia: EstrategiaDePagamento) { fun processarPedido(valor: Double) { // ... processamento do pedido estrategia.pagar(valor) } } - State: Permite que um objeto mude seu comportamento dependendo do estado interno. Classes seladas e expressões when simplificam sua implementação.
sealed class Estado { object Idle : Estado() object Carregando : Estado() data class Erro(val mensagem: String) : Estado() data class Sucesso(val dados: Any) : Estado() } class Contexto { var estado: Estado = Estado.Idle fun lidarComEvento() { when (estado) { Estado.Idle -> { println("Lidando com evento no estado Idle") estado = Estado.Carregando } Estado.Carregando -> { println("Lidando com evento no estado Carregando, ignorando") } is Estado.Erro -> { println("Lidando com evento no estado Erro: ${estado.mensagem}") // Possível transição para outro estado } is Estado.Sucesso -> { println("Lidando com evento no estado Sucesso: ${estado.dados}") // Possível transição para outro estado } } } } - Repository: Abstrai a lógica de acesso a dados da lógica de negócio.
Muitos outros padrões, como Facade, Proxy, Command, Iterator, Template Method e Visitor, também são aplicáveis em Kotlin. As características do Kotlin, como extensões, delegação, data classes e classes seladas, frequentemente tornam a implementação de padrões mais idiomática e concisa.