paint-brush
继承与组合:以 JavaScript 中的角色扮演游戏为例经过@kliukovkin
13,348 讀數
13,348 讀數

继承与组合:以 JavaScript 中的角色扮演游戏为例

经过 Georgii Kliukovkin4m2022/12/21
Read on Terminal Reader

太長; 讀書

通过组合,您可以避免继承问题,而 javascript 是一种完美的语言。
featured image - 继承与组合:以 JavaScript 中的角色扮演游戏为例
Georgii Kliukovkin HackerNoon profile picture

继承问题

  • 儿童代码重复
  • 继承层次结构过于复杂
  • 改变父母的行为会导致孩子犯错误


在这篇文章中,我们将看看这些问题是什么以及我们如何使用组合来解决它们。


面向对象语言的问题在于它们拥有随身携带的所有这些隐式环境。你想要一根香蕉,但你得到的是一只拿着香蕉和整个丛林的大猩猩。 - Joe Armstrong,Erlang 的创造者

角色扮演游戏继承

考虑创建角色扮演游戏角色层次结构的过程。最初,需要两种类型的角色——战士和法师,每个角色都有一定的生命值和名字。这些属性是公共的,可以移至父 Character 类。

 class Character { constructor(name) { this.name = name; this.health = 100; } }


战士可以攻击,消耗他的耐力:

 class Warrior extends Character { constructor(name) { super(name); this.stamina = 100; } fight() { console.log(`${this.name} takes a mighty swing!`); this.stamina--; } }


法师可以施放消耗一定法力的法术:

 class Mage extends Character { constructor(name) { super(name); this.mana = 100; } cast() { console.log(`${this.name} casts a fireball!`); this.mana--; } }

圣骑士类问题

现在,让我们介绍一个新职业,圣骑士。圣骑士既可以战斗也可以施法。我们如何解决这个问题?以下是一些同样缺乏优雅的解决方案:


  • 我们可以使Paladin成为Character的后代,并从头开始在其中实现fight()cast()方法。在这种情况下,违反了DRY原则,因为每个方法在创建时都会被复制,并且需要与 Mage 和 Fighter 类的方法不断同步以跟踪变化。


  • fight()cast()方法可以在Character类级别实现,以便所有三种角色类型都具有它们。这是一个稍微好一点的解决方案,但在这种情况下,开发人员必须覆盖法师的fight()方法和战士的 cast() 方法,将它们替换为空方法或解决错误。

作品

这些问题可以通过使用组合的函数式方法来解决。不从它们的类型开始,而是从它们的功能开始就足够了。基本上,我们有两个决定角色能力的关键特征——战斗能力和施法能力。


可以使用扩展定义角色状态的工厂函数来设置这些功能:

 const canCast = (state) => ({ cast: (spell) => { console.log(`${state.name} casts ${spell}!`); state.mana--; } }) const canFight = (state) => ({ fight: () => { console.log(`${state.name} slashes at the foe!`); state.stamina--; } })


因此,角色由一组这些能力和初始属性定义,包括一般(名称和健康)和私有(耐力和法力):

 const fighter = (name) => { let state = { name, health: 100, stamina: 100 } return Object.assign(state, canFight(state)); } const mage = (name) => { let state = { name, health: 100, mana: 100 } return Object.assign(state, canCast(state)); } const paladin = (name) => { let state = { name, health: 100, mana: 100, stamina: 100 } return Object.assign(state, canCast(state), canFight(state)); }

结论

通过组合,您可以使用组合来避免继承问题,而 javascript 是这样做的完美语言。