继承问题 儿童代码重复 继承层次结构过于复杂 改变父母的行为会导致孩子犯错误 在这篇文章中,我们将看看这些问题是什么以及我们如何使用组合来解决它们。 面向对象语言的问题在于它们拥有随身携带的所有这些隐式环境。 。 - 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--; } } 圣骑士类问题 现在,让我们介绍一个新职业, 。圣骑士既可以战斗也可以施法。我们如何解决这个问题?以下是一些同样缺乏优雅的解决方案: 圣骑士 我们可以使 成为 的后代,并从头开始在其中实现 和 方法。在这种情况下,违反了 原则,因为每个方法在创建时都会被复制,并且需要与 Mage 和 Fighter 类的方法不断同步以跟踪变化。 Paladin Character fight() cast() DRY 和 方法可以在 类级别实现,以便所有三种角色类型都具有它们。这是一个稍微好一点的解决方案,但在这种情况下,开发人员必须覆盖法师的 方法和战士的 cast() 方法,将它们替换为空方法或解决错误。 fight() cast() Character fight() 作品 这些问题可以通过使用组合的函数式方法来解决。不从它们的类型开始,而是从它们的功能开始就足够了。基本上,我们有两个决定角色能力的关键特征——战斗能力和施法能力。 可以使用扩展定义角色状态的工厂函数来设置这些功能: 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 是这样做的完美语言。