Модел на делегация в Котлин

1. Общ преглед

Има много случаи на използване, при които делегирането се предпочита пред наследяването. Kotlin има чудесна поддръжка на езиково ниво за това.

В този урок ще говорим за родната поддръжка на Kotlin за модела на делегиране и ще го видим в действие.

2. Прилагане

Първо, да приемем, че имаме пример за код със структурата по-долу в библиотека на трети страни:

interface Producer { fun produce(): String } class ProducerImpl : Producer { override fun produce() = "ProducerImpl" }

След това нека декорираме съществуващото изпълнение с помощта на ключовата дума „by“ и добавим допълнителната необходима обработка:

class EnhancedProducer(private val delegate: Producer) : Producer by delegate { override fun produce() = "${delegate.produce()} and EnhancedProducer" }

И така, в този пример посочихме, че класът EnhancedProducer ще капсулира делегиран обект от тип Producer . Освен това може да използва функционалност от внедряването на Producer .

И накрая, нека проверим дали работи както се очаква:

val producer = EnhancedProducer(ProducerImpl()) assertThat(producer.produce()).isEqualTo("ProducerImpl and EnhancedProducer")

3. Използвайте калъфи

Сега, нека разгледаме два често използвани случая за модел на делегиране.

Първо, можем да използваме модела на делегиране, за да реализираме множество интерфейси, използвайки съществуващи реализации :

class CompositeService : UserService by UserServiceImpl(), MessageService by MessageServiceImpl()

Второ, можем да използваме делегиране, за да подобрим съществуващо изпълнение .

Последното е това, което направихме в предишния раздел. Но по-реален пример като този по-долу е особено полезен, когато не можем да модифицираме съществуваща реализация - например код на библиотека на трета страна:

class SynchronizedProducer(private val delegate: Producer) : Producer by delegate { private val lock = ReentrantLock() override fun produce(): String { lock.withLock { return delegate.produce() } } }

4. Делегирането не е наследство

Сега винаги трябва да помним, че делегатът не знае нищо за декоратора. Така че, не бива да пробваме с тях подход, подобен на GoF Template Method .

Нека разгледаме пример:

interface Service { val seed: Int fun serve(action: (Int) -> Unit) } class ServiceImpl : Service { override val seed = 1 override fun serve(action: (Int) -> Unit) { action(seed) } } class ServiceDecorator : Service by ServiceImpl() { override val seed = 2 }

Тук делегатът ( ServiceImpl ) използва свойство, дефинирано в общия интерфейс, и ние го заместваме в декоратора ( ServiceDecorator ). Това обаче не засяга обработката на делегата:

val service = ServiceDecorator() service.serve { assertThat(it).isEqualTo(1) }

И накрая, важно е да се отбележи, че в Kotlin можем да делегираме не само интерфейси, но и да отделяме свойствата .

5. Заключение

В този урок говорихме за делегиране на интерфейс на Kotlin - кога трябва да се използва, как да го конфигурирам и неговите предупреждения.

Както обикновено, пълният изходен код за тази статия е достъпен в GitHub.