When should I use 'by' delegation instead of manual method forwarding?
← Code Quality · Ref: Q859
When a class manually delegates most trait methods to a field, EK9 detects this pattern and requires using the 'by' keyword instead (E11023). The 'by' keyword eliminates boilerplate delegation code.
THE PROBLEM
Manually overriding 5+ trait methods just to forward calls to a field is tedious and error-prone:
override methodA() -> delegate.methodA() override methodB() -> delegate.methodB() override methodC() -> delegate.methodC()
This is boilerplate that the compiler can generate automatically.
THE FIX
Use 'with trait of X by field' to auto-delegate all unoverridden methods:
MyClass with trait of Formatter by delegate delegate as Formatter: ConcreteFormatter()
Now only override the methods you need to customize.
BENEFITS
1. Eliminates N lines of forwarding boilerplate
2. New trait methods are automatically delegated
3. Only customized methods need override
4. Clear intent: delegation is visible in the class declaration
See Q210 for trait delegation basics. See Q264 for adapter pattern. See Q266 for cross-cutting concerns via delegation.
Example
defines module qa.quality.use.by.delegation defines trait <?- Formatter trait with multiple methods. Used to demonstrate delegation with 'by'. -?> Formatter formatHeading() as abstract -> heading as String <- rtn as String? formatParagraph() as abstract -> paragraph as String <- rtn as String? formatFooter() as abstract <- rtn as String? defines class <?- Concrete implementation of Formatter. Provides default formatting behavior. -?> PlainFormatter with trait of Formatter override formatHeading() -> heading as String <- rtn as String: `=== ${heading} ===` override formatParagraph() -> paragraph as String <- rtn as String: " " + paragraph override formatFooter() <- rtn as String: "---end---" default operator ? <?- Correct: uses 'by' delegation to auto-delegate Formatter methods. Only overrides formatHeading to add custom behavior. formatParagraph and formatFooter are auto-delegated. -?> FancyFormatter with trait of Formatter by delegate delegate as Formatter: PlainFormatter() FancyFormatter() -> formatter as Formatter this.delegate: formatter override formatHeading() -> heading as String <- rtn as String: `*** ${heading} ***` default operator ? defines program DelegationDemo() stdout <- Stdout() plain <- PlainFormatter() fancy <- FancyFormatter(plain) //formatHeading is overridden in FancyFormatter stdout.println(fancy.formatHeading("Title")) //formatParagraph is auto-delegated to PlainFormatter stdout.println(fancy.formatParagraph("Some content here")) //formatFooter is auto-delegated to PlainFormatter stdout.println(fancy.formatFooter())
Other ways to ask this
- What is E11023 MISSING_BY_DELEGATION in EK9?
- Why does EK9 require 'by' delegation for trait forwarding?
- How do I eliminate boilerplate delegation code?
Coming from another language?
Java: no built-in delegation, manual forwarding or Lombok @Delegate. Kotlin: 'by' delegation on interfaces, same concept. Rust: no delegation, manual impl forwarding. Go: embedded structs provide implicit forwarding. Python: __getattr__ for dynamic delegation. EK9: 'with trait of X by field' with E11023 enforcement when manual delegation detected.
Keywords: override, forwarding, quality, trait, clean-code, by, composition, boilerplate, E11023, delegation