Padrão Decorator
Parte da seção Programação Orientada a Objetos do Journey de Kotlin da Coddy. Lição 47 de 57.
O padrão decorator adiciona comportamento a um objeto envolvendo-o em outro objeto com a mesma interface. O decorator encaminha as chamadas ao objeto que envolve e adiciona sua própria parte, portanto os decorators podem ser empilhados:
interface Coffee {
fun cost(): Int
fun description(): String
}
class Espresso : Coffee {
override fun cost() = 200
override fun description() = "espresso"
}
class Milk(private val inner: Coffee) : Coffee {
override fun cost() = inner.cost() + 50
override fun description() = inner.description() + ", milk"
}
class Syrup(private val inner: Coffee) : Coffee {
override fun cost() = inner.cost() + 30
override fun description() = inner.description() + ", syrup"
}Dentro de main:
val order = Syrup(Milk(Milk(Espresso())))
println("${order.description()}: ${order.cost()}")Saída:
espresso, milk, milk, syrup: 330Quando os decoradores alteram o resultado, a ordem deles importa: cada um trabalha com o que o objeto dentro dele retornou:
interface Text {
fun render(): String
}
class Word(private val w: String) : Text {
override fun render() = w
}
class Bracket(private val inner: Text) : Text {
override fun render() = "[" + inner.render() + "]"
}
class Twice(private val inner: Text) : Text {
override fun render() = inner.render() + inner.render()
}Dentro de main:
println(Twice(Bracket(Word("hi"))).render())
println(Bracket(Twice(Word("hi"))).render())Saída:
[hi][hi]
[hihi]Com by, um decorador escreve apenas os membros que altera e encaminha automaticamente o restante:
interface Source {
fun read(): String
fun name(): String
}
class FileSource(private val content: String) : Source {
override fun read() = content
override fun name() = "notes.txt"
}
class Censored(private val inner: Source, private val word: String) : Source by inner {
override fun read() = inner.read().replace(word, "***")
}Dentro de main:
val src = Censored(FileSource("the secret plan"), "secret")
println("${src.name()}: ${src.read()}")Saída:
notes.txt: the *** planOs decorators são combinados enquanto o programa é executado, por exemplo, a partir das escolhas de um cliente, enquanto uma subclasse fixa uma combinação quando o código é escrito. Três extras como subclasses exigiriam uma classe para cada combinação; como decorators, fold os empilha a partir de uma lista:
interface Coffee {
fun cost(): Int
}
class Espresso : Coffee {
override fun cost() = 200
}
class Extra(private val inner: Coffee, private val price: Int) : Coffee {
override fun cost() = inner.cost() + price
}Dentro de main:
val prices = mapOf("milk" to 50, "syrup" to 30, "cream" to 70)
val choices = listOf("milk", "cream", "milk")
val coffee = choices.fold<String, Coffee>(Espresso()) { c, extra -> Extra(c, prices.getValue(extra)) }
println(coffee.cost())Saída:
370Desafio
FácilAs pizzas são construídas com decoradores da interface fornecida Pizza (com price() e toppings()). Escreva Base (800, sem coberturas) e os decoradores Cheese (+150, adiciona cheese), Olives (+100, adiciona olives) e DoubleUp, que duplica o preço de tudo que estiver dentro dele e repete suas coberturas duas vezes. Em seguida, escreva build(names), que começa com Base e o envolve com um decorador por nome, na ordem (cheese, olives ou double).
O código fornecido lê linhas de nomes separados por vírgulas, ou plain, constrói cada pizza e imprime 1050: cheese, olives ou 800: plain.
Seu código deve estar em Base.kt, Cheese.kt, Olives.kt, DoubleUp.kt, Build.kt e Pizza.kt. Main.kt contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
fun main() {
// Código de entrada/saída fornecido: mantenha-o como está
val input = generateSequence(::readLine).toList()
for (line in input) {
val names = if (line == "plain") listOf() else line.split(",")
val pizza = build(names)
val toppings = pizza.toppings()
println("${pizza.price()}: " + if (toppings.isEmpty()) "plain" else toppings.joinToString(", "))
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesConstrutores PrimáriosBlocos init e ValidaçãoConstrutores SecundáriosRecapitulação - Cartão da Biblioteca4Herança
Classes Abertas e SubclassesSobrescrevendo Membrossuper e ConstrutoresClasses AbstratasRevisão - Diretório de Funcionários2Propriedades e Encapsulamento
Acessores PersonalizadosModificadores de VisibilidadeSetters Privadoslazy e lateinitRevisão - Conta Bancária5Interfaces
Declarando InterfacesMétodos PadrãoMúltiplas InterfacesInterfaces como TiposRecapitulação - Métodos de Pagamento8Genéricos
Funções genéricasClasses genéricasRestrições de tipoVariância com in e outRecapitulação - Fila de prioridade11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Playground de Kotlin