I have a class Character who has a list of Spell objects that he can cast.
abstract class Spell {
name: string
manaCost: number
abstract cast() { ... }
}
class InvisibilitySpell extends Spell { ... }
class FireballSpell extends Spell { ... }
class Character {
mana: number
spells: Spell[]
addSpell(spell: Spell) {
this.spells.push(spell)
}
}
Casting spells requires accessing the correct spell by index in the .spells[] array
let char = new Character()
char.addSpell(new InvisibilitySpell())
char.addSpell(new FireballSpell())
char.spells[0].cast() // casts Invisibility spell
char.spells[1].cast() // casts Fireball spell
To improve the ergonomics of the interface and implement mana requirements, I plan on adding a castSpell method to the Character class that will delegate to the correct spell object, but I am not sure what the best practice is for handling the query.
I could query by the string value of the .name
class Character {
...
castSpell(spellName: string) {
let spell = this.spells.find(spell => spell.name === spellName)
if (spell && spell.manaCost <= this.mana) {
spell.cast()
}
}
}
char.castSpell('Invisibility')
Or I could query by the type itself
class Character {
...
castSpell(spellType: typeof Spell) {
let spell = this.spells.find(spell => spell instanceof spellType)
if (spell && spell.manaCost <= this.mana) {
spell.cast()
}
}
}
import { InvisibilitySpell } from './spells/InvisibilitySpell'
char.castSpell(InvisibilitySpell)
What software design considerations or technical implications might there be for doing one over the other, and are there approaches even better than these two?
kind: "fire" | "invisibility" | "whatever";tag property that will let the language understand exactly what you're doing?