В Kotlin часто применяются следующие паттерны:
Singleton: Удобно реализуется через object.object MySingleton {
fun doSomething() {
// ...
}
}
Factory Method: Используется абстрактная функция в суперклассе или интерфейсе, которую реализуют подклассы для создания объектов.interface Product {
fun operation()
}
class ConcreteProductA : Product {
override fun operation() {
// ...
}
}
abstract class Creator {
abstract fun factoryMethod(): Product
fun someOperation(): Product {
val product = factoryMethod()
// ...
return product
}
}
class ConcreteCreatorA : Creator() {
override fun factoryMethod(): Product {
return ConcreteProductA()
}
}
Abstract Factory: Группа фабрик для создания связанных семейств объектов.interface AbstractProductA {
fun usefulFunctionA()
}
interface AbstractProductB {
fun usefulFunctionB()
fun anotherUsefulFunctionB(collaborator: AbstractProductA)
}
interface AbstractFactory {
fun createProductA(): AbstractProductA
fun createProductB(): AbstractProductB
}
// Concrete implementations of products and factories...
Builder: Упрощает создание сложных объектов пошагово.class Pizza private constructor(
val crust: String,
val sauce: String?,
val toppings: List<String>
) {
data class Builder(
var crust: String = "thin",
var sauce: String? = null,
val toppings: MutableList<String> = mutableListOf()
) {
fun withCrust(crust: String) = apply { this.crust = crust }
fun withSauce(sauce: String) = apply { this.sauce = sauce }
fun addTopping(topping: String) = apply { this.toppings.add(topping) }
fun build() = Pizza(crust, sauce, toppings)
}
}
fun main() {
val pizza = Pizza.Builder()
.withCrust("thick")
.withSauce("tomato")
.addTopping("pepperoni")
.addTopping("mushrooms")
.build()
// ... use pizza
}
Adapter: Позволяет объектам с несовместимыми интерфейсами работать вместе.open class Adaptee {
fun specificOperation() {
// ...
}
}
interface Target {
fun operation()
}
class Adapter(private val adaptee: Adaptee) : Target {
adaptee.specificOperation()
}
}
Decorator: Добавляет новые функции к существующим объектам без изменения их структуры. Расширения и делегирование в Kotlin очень этому способствуют.interface DataSource {
fun readData(): String
fun writeData(data: String)
}
class FileDataSource(private val filename: String) : DataSource {
override fun readData(): String {
// Чтение из файла
return ""
}
override fun writeData(data: String) {
// Запись в файл
}
}
class EncryptionDecorator(private val source: DataSource) : DataSource by source {
// Расшифровать data
val data = source.readData()
return data // расшифрованные данные
}
// Зашифровать data
source.writeData(data) // зашифрованные данные
}
}
Observer: Определяет зависимость "один ко многим". Coroutines Flow и Kotlin's delegated properties могут быть использованы для реализации.// Простая реализация без Flow
interface Observer {
fun update(message: String)
}
class Subject {
private val observers = mutableListOf<Observer>()
fun attach(observer: Observer) {
observers.add(observer)
}
fun detach(observer: Observer) {
observers.remove(observer)
}
fun notifyObservers(message: String) {
observers.forEach { it.update(message) }
}
}
Strategy: Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми.interface PaymentStrategy {
fun pay(amount: Double)
}
class PayByCreditCard(private val cardNumber: String) : PaymentStrategy {
override fun pay(amount: Double) {
println("Paying $amount using Credit Card $cardNumber")
}
}
class PayByPayPal(private val email: String) : PaymentStrategy {
println("Paying $amount using PayPal $email")
}
}
class Order(private val paymentStrategy: PaymentStrategy) {
fun processOrder(amount: Double) {
// ... some order processing
paymentStrategy.pay(amount)
}
}
State: Позволяет объекту менять свое поведение в зависимости от внутреннего состояния. Sealed классы и when выражения упрощают реализацию.sealed class State {
object Idle : State()
object Loading : State()
data class Error(val message: String) : State()
data class Success(val data: Any) : State()
}
class Context {
var state: State = State.Idle
fun handleEvent() {
when (state) {
State.Idle -> {
println("Handling event in Idle state")
state = State.Loading
}
State.Loading -> {
println("Handling event in Loading state, ignoring.")
}
is State.Error -> {
println("Handling event in Error state: ${state.message}")
// Возможно, переход в другое состояние
}
is State.Success -> {
println("Handling event in Success state: ${state.data}")
}
}
}
}
Repository: Абстрагирует логику доступа к данным из бизнес-логики.
Многие другие паттерны, такие как Facade, Proxy, Command, Iterator, Template Method и Visitor, также применимы в Kotlin. Языковые особенности Kotlin, такие как расширения, делегирование, data классы и sealed классы, часто делают реализацию паттернов более идиоматичной и лаконичной.