Geschichte der Frage:
Das "Delegationsmuster" ist vielen objektorientierten Programmiersprachen bekannt, es ist ein Prinzip, bei dem die Arbeit von einem Objekt an ein anderes übertragen wird. In Java wird die Delegation manuell umgesetzt – durch interne Felder und Methodenproxying. In Kotlin wird die Delegation durch das Schlüsselwort by auf die Syntaxebene gehoben.
Problem:
Die Implementierung des Delegationsmusters in Java führt zu "göttlichen" Proxy-Klassen, die mit Code-Vorlagen überladen sind und hohen Wartungsaufwand bei der Unterstützung von Schnittstellen verursachen. Es ist schwierig, die Verträge von Schnittstellen zu aktualisieren und Delegaten zu ändern.
Lösung:
Kotlin ermöglicht die Erstellung von Klassen, die Schnittstellen nicht direkt implementieren, sondern alle ihre Methoden an ein anderes Objekt delegieren durch die Notation class Foo(...) : MyInterface by delegateObj. Dies ermöglicht das Schreiben von prägnantem und verständlichem Code, der Routinetätigkeiten vermeidet, ohne die Flexibilität zu verlieren.
Code-Beispiel:
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 }
Wichtige Merkmale:
Kann eine deklarierende Klasse das Verhalten einer bestimmten Methode trotz Delegation ändern?
Ja – wenn die Methode der Schnittstelle explizit in der delegierenden Klasse (Derived) implementiert wird, überschreibt sie das delegierte Verhalten für die spezifische Methode.
Beispiel:
class Derived(b: Base) : Base by b { override fun print() = println("Überholt!") }
Kann man mehrere Schnittstellen an verschiedene Objekte delegieren?
Nein, in Kotlin kann man nicht direkt mehrere unterschiedliche Schnittstellen an verschiedene Objekte in einer Deklaration delegieren. Man muss eine Klasse mit manueller Delegation schreiben oder Vererbung und Delegation kombinieren, wenn die Architektur dies zulässt.
Funktioniert Delegation auch mit abstrakten Klassen oder nur mit Schnittstellen?
Man kann nur an Schnittstellen delegieren, nicht an abstrakte Klassen – da abstrakte Klassen Zustände und geschützte Methoden haben können, die mit der Delegationsdeklaration über by unvereinbar sind.
Ein Entwickler implementiert manuell das Delegationsmuster für ein Dutzend Methoden einer großen Schnittstelle. Bei jeder Erweiterung der Schnittstelle vergisst er, die neuen Proxy-Methoden hinzuzufügen. Der Code wächst, und Fehler häufen sich.
Vorteile:
Nachteile:
Die Syntax by wurde für die automatische Delegation der Schnittstelle verwendet. Es ist einfach, die Implementierung zu ändern und den Delegaten zur Laufzeit auszutauschen, ohne besonders das Risiko einzugehen, den Vertrag falsch zu unterstützen.
Vorteile:
Nachteile: