Historia pytania:
Wzorzec „Delegacja” jest znany wielu językom OOP, to zasada przekazywania pracy jednemu obiektowi przez inny. W Javie delegacja jest realizowana ręcznie — poprzez pole wewnętrzne i proxy metod. W Kotlinie delegacja jest przeniesiona na poziom składni za pomocą słowa kluczowego by.
Problem:
Realizacja wzorca delegacji w Javie prowadzi do „boskich” klas proxy, obciążonych kodem szablonowym i wielkimi kosztami wsparcia interfejsu. Trudno jest utrzymać aktualizację kontraktów interfejsu i zmieniać delegatów.
Rozwiązanie:
Kotlin pozwala tworzyć klasy, które implementują interfejs niebezpośrednio, a przekazując wszystkie jego metody innemu obiektowi za pomocą zapisu class Foo(...) : MyInterface by delegateObj. Umożliwia to pisanie zwięzłego i zrozumiałego kodu, pozbywając się rutyny bez utraty elastyczności.
Przykład kodu:
interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fun print() = println(x) } class Derived(b: Base) : Base by b fun main() { Derived(BaseImpl(42)).print() // 42 }
Kluczowe cechy:
Czy klasa deklarująca może zmienić zachowanie konkretnej metody, mimo że zachodzi delegacja?
Tak — jeśli zrealizować metodę interfejsu jawnie w klasie-delegacie (Derived), „przeciąży” ona delegowane zachowanie dla konkretnej metody.
Przykład:
class Derived(b: Base) : Base by b { override fun print() = println("Overrided!") }
Czy można delegować od razu kilka interfejsów różnym obiektom?
Nie, w Kotlinie nie można bezpośrednio delegować kilku różnych interfejsów różnym obiektom w jednej deklaracji. Będzie trzeba napisać klasę z ręczną delegacją lub połączyć dziedziczenie i delegację, jeśli architektura na to pozwala.
Czy delegacja działa z klasami abstrakcyjnymi, czy tylko z interfejsami?
Można delegować tylko interfejsy, nie klasy abstrakcyjne — ponieważ klasy abstrakcyjne mogą mieć stany i metody chronione, które są niekompatybilne z deklaracją delegacji za pomocą by.
Programista ręcznie implementuje wzorzec delegacji dla dziesięciu metod dużego interfejsu. Przy każdej rozbudowie interfejsu zapomina dodać nowe metody-proxy. Kod rośnie, błędy się mnożą.
Zalety:
Wady:
Zastosowano składnię by do automatycznego delegowania interfejsu. Łatwo zmieniać implementację i podmieniać delegata na bieżąco, nie ryzykując błędów przy wsparciu kontraktu.
Zalety:
Wady: