Multiple inheritance sounds powerful but often leads to dangerous ambiguity. Let's see why with Naruto's Edo Tensei.
The Anime Hook: Edo Tensei's Conflicted Ninja
Imagine Orochimaru resurrects a ninja who trained under two masters, Jiraiya and Tsunade. Both taught a technique called useUltimateJutsu()
, but each technique is completely different.
Who wins when the Edo Tensei ninja tries to use useUltimateJutsu()
? This conflict is the heart of the famous Diamond Problem in programming.
What Is the Diamond Problem?
The Diamond Problem happens when a class inherits from two parents who both inherit from the same grandparent—and the parents override the same method. The child inherits that method twice, causing ambiguity on which one to run.
Here's a simplified diamond shape:
BaseNinja
/ \
Jiraiya Tsunade
\ /
EdoShinobi
Both Jiraiya and Tsunade override useUltimateJutsu()
from BaseNinja
. Which one does EdoShinobi
use?
Diamond Problem In Code (Mixin Simulation)
TypeScript forbids direct multiple class inheritance, but mixins can simulate it. Here's the pitfall:
class BaseNinja {
constructor(public name: string) {}
useUltimateJutsu() {
console.log(`${this.name} uses the basic ultimate technique`)
}
}
function JiraiyaTraining<T extends new (...args: any[]) => BaseNinja>(Base: T) {
return class extends Base {
useUltimateJutsu() {
console.log(`${this.name} uses Rasengan! 🌀`)
}
}
}
function TsunadeTraining<T extends new (...args: any[]) => BaseNinja>(Base: T) {
return class extends Base {
useUltimateJutsu() {
console.log(`${this.name} uses Cherry Blossom Impact! 🌸`)
}
}
}
class EdoShinobi extends TsunadeTraining(JiraiyaTraining(BaseNinja)) {}
const edo = new EdoShinobi('Minato (Edo)')
edo.useUltimateJutsu()
// Output: Minato (Edo) uses Cherry Blossom Impact! 🌸
// Jiraiya's Rasengan is silently overwritten and lost
The last mixin applied wins, silently overriding previous methods — a classic Diamond Problem trap.
Clean Solution: Composition
Instead of inheritance, use composition - the ninja has a list of masters to ask when performing techniques:
interface JutsuMaster {
teachUltimate(name: string): void
}
class Jiraiya implements JutsuMaster {
teachUltimate(name: string) {
console.log(`${name} learns Rasengan from Jiraiya! 🌀`)
}
}
class Tsunade implements JutsuMaster {
teachUltimate(name: string) {
console.log(`${name} learns Cherry Blossom Impact from Tsunade! 🌸`)
}
}
class EdoShinobi {
private masters: JutsuMaster[] = []
constructor(public name: string) {}
addMaster(master: JutsuMaster) {
this.masters.push(master)
}
useAllTechniques() {
this.masters.forEach(master => master.teachUltimate(this.name))
}
}
const edo = new EdoShinobi('Minato (Edo)')
edo.addMaster(new Jiraiya())
edo.addMaster(new Tsunade())
edo.useAllTechniques()
// Output:
// Minato (Edo) learns Rasengan from Jiraiya! 🌀
// Minato (Edo) learns Cherry Blossom Impact from Tsunade! 🌸
Composition lets you use all techniques explicitly, avoiding ambiguity.
Why Most Languages Avoid Multiple Inheritance
- The Diamond Problem causes ambiguous, fragile code
- Maintenance and testing suffer
- Alternative patterns (composition, interfaces, strategy) are clearer and safer
Treat multiple inheritance like Edo Tensei: tempting but forbidden for a reason.
Top comments (0)