首页 > web前端 > js教程 > 继承与组合

继承与组合

DDD
发布: 2024-12-27 03:26:09
原创
312 人浏览过

Inheritance vs Composition

让我们首先看一个如何在 ES5 中实现继承的示例

class Animal {
    constructor(name, energy) {
        this.name = name
        this.energy = energy
    }
    eat(amount) {
        console.log(`${this.name} is eating.`)
        this.energy += amount
    }
    sleep(length) {
        console.log(`${this.name} is sleeping.`)
        this.energy += length
    }
    play(length) {
        console.log(`${this.name} is playing.`)
        this.energy -= length
    }
}

class Dog extends Animal {
    constructor(name, energy, breed) {
        super(name, energy)
        this.breed = breed
    }
    bark() {
        console.log('Woof Woof!')
        this.energy -= .1
    }
}

class Cat extends Animal {
    constructor(name, energy, declawed) {
        super(name, energy)
        this.declawed = declawed
    }
    meow() {
        console.log('Meow!')
        this.energy -= .1
    }
}
登录后复制

我们可以像这样可视化层次结构:

Animal
    name
    energy
    eat()
    sleep()
    play()

    Dog
        breed
        bark()

    Cat
        declawed
        meow()
登录后复制

稍后,您的任务是向系统添加另一个实体:用户。

User
    email
    username
    pets
    friends
    adopt()
    befriend()

Animal
    name
    energy
    eat()
    sleep()
    play()

    Dog
        breed
        bark()

    Cat
        declawed
        meow()
登录后复制

到目前为止一切都很好。然而,您的项目经理现在告诉您也为用户添加吃饭、睡觉和玩耍的功能。你会怎么做?以下是我们在 OOP 中解决这个问题的方法:

Mammal
    name
    eat()
    sleep()
    play()

    User
        email
        username
        pets
        friends
        adopt()
        befriend()

    Animal
        energy

        Dog
            breed
            bark()

        Cat
            declawed
            meow()
登录后复制

这看起来非常脆弱,因为必须引入另一个实体,随着程序的发展,它现在将具有自己的重要性。这种反模式通常被称为“God Object”。因此,OOP 的问题在于,实体在编写时具有含义,并且可以在以后随着需求的变化而更改。这些更改可能会破坏类层次结构。

我认为缺乏可重用性来自于面向对象的语言,而不是函数式语言。因为面向对象语言的问题是它们带有所有这些隐式环境。你想要一根香蕉,但你得到的是一只拿着香蕉的大猩猩和整个丛林。
——乔·阿姆斯特朗(Erlang 的创建者)

与其思考事物是什么,不如让我们转向事物的作用。

  • 狗是睡觉者、食者、玩耍者和吠叫者。
  • 猫会睡觉、吃东西、玩耍和喵喵。

我们可以将它们作为函数并在需要时将它们组合在一起,而不是将这些方法与类紧密耦合。伟大的!那么我们如何对具体的实例进行操作呢?好吧,我们将实例直接传递给我们的函数。闭包让函数记住传递的状态(实例)。

const eater = (state) => ({
    eat(amount) {
        console.log(`${state.name} is eating.`)
        state.energy += amount
    }
})
const sleeper = (state) => ({
    sleep(length) {
        console.log(`${state.name} is sleeping.`)
        state.energy += length
    }
})
const player = (state) => ({
    play(length) {
        console.log(`${state.name} is eating.`)
        state.energy -= length
    }
})
const barker = (state) => ({
    bark(length) {
        console.log(`Woof Woof!`)
        state.energy -= .1
    }
})
const meower = (state) => ({
    meow(length) {
        console.log(`Meow!`)
        state.energy -= .1
    }
})
登录后复制

狗作为睡眠者、食者、玩家和吠叫者的示例:

function Dog(name, energy, breed) {
    let dog = {
        name,
        energy,
        breed
    }

    return Object.assign(
        dog,
        eater(dog),
        sleeper(dog),
        player(dog),
        barker(dog)
    )
}

const dog = Dog('Dog', 10, 'Bulldog')
dog.eat(10)
dog.bark()
登录后复制

现在用户还可以吃饭、睡觉和玩耍:

function User(email, username) {
    let user = {
        email,
        username,
        pets: [],
        friends: []
    }

    return Object.assign(
        user,
        eater(user),
        sleeper(user),
        player(user)
    )
}
登录后复制

恭喜,您已经将自己从紧密耦合的继承结构中解放出来。

以上是继承与组合的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板