首頁 >web前端 >js教程 >繼承與組合

繼承與組合

DDD
DDD原創
2024-12-27 03:26:09313瀏覽

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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn