Go 語言之對不同型別的資料進行分組

大雄45發表於2022-11-02
導讀 重要的是要記住,在 Go 中,沒有物件導向的概念,所以子型別或子類的概念真的不存在,這些設計模式應該被避免。
對不同型別的資料進行分組

以下是不應該遵循或實施的反模式:

type Animal struct {
    Name string
    IsMammal bool
}

動物型別被宣告為一個基礎型別,它試圖定義所有動物共有的資料。我也嘗試向動物提供一些常見的行為。

func (a *Animal) Speak() {
    fmt.Println("UGH!",
    "My name is", a.Name, ", it is", a.IsMammal, "I am a mammal")
}

大多數動物都有以這種或那種方式說話的能力。然而,試圖將這種常見的行為僅僅適用於一種動物並沒有任何意義。在這一點上,我不知道這種動物發出什麼聲音,所以我寫了 UGH

type Dog struct {
    Animal
    PackFactor int
}

現在,真正的問題開始了。我試圖用嵌入的方式使狗擁有動物的一切,而且更多。從表面上看,這似乎是可行的,但也會有問題。既然如此,狗確實有一種特殊的說話方式

func (d *Dog) Speak() {
    fmt.Println("Woof!",
    "My name is", d.Name,
    ", it is", d.IsMammal,
    "I am a mammal with a pack factor of", d.PackFactor)
}

在說話方法的實現中,我可以把 UGH 換成 Woof 。這就是 具體到狗的說話方式

type Cat struct {
    Animal
    ClimbFactor int
}

如果我打算養一隻代表動物的狗,那麼我就必須養一隻貓。使用嵌入法,貓是動物的一切,而且更多。

func (c *Cat) Speak() {
    fmt.Println("Meow!",
    "My name is", c.Name,
    ", it is", c.IsMammal,
    "I am a mammal with a climb factor of", c.ClimbFactor)
}

在實現 Speak 的方法中,我可以把 UGH 改成 Meow 。這就是具體到貓的說話方式 一切看起來都很好,看起來嵌入提供的功能與其他語言中的繼承相同。然後我試著把狗和貓分組,因為它們有共同的DNA,都是動物。

// This does not compile, a Dog and Cat are not an Animal.
animals := []Animal{
    Dog{
        Animal: Animal{
            Name: "Fido",
            IsMammal: true,
        },
        PackFactor: 5,
    },
    Cat{
        Animal: Animal{
        Name: "Milo",
        IsMammal: true,
    },
    ClimbFactor: 4,
    },
}
for _, animal := range animals {
    animal.Speak()
}

當我嘗試這樣做時,編譯器抱怨狗和貓不是動物,這是真的。 嵌入與繼承不同,這是我需要遠離的模式。 狗是狗,貓是貓,動物是動物。 我不能把狗和貓當作動物來傳遞,因為它們不是。 這種機制也不是很靈活。 它需要開發人員進行配置,除非我可以訪問程式碼並且可以隨著時間的推移進行配置更改,否則這不是很靈活 如果這不是我們構建 Dog 和 Cat 集合的方式,我們如何在 Go 中做到這一點? 這不是透過共同的 DNA 進行分組,而是透過共同的行為進行分組。 行為是關鍵。

type Speaker interface {
    Speak()
}

如果我使用一個介面,那麼我可以定義我想要對不同型別的資料進行分組的通用行為方法集:

speakers := []Speaker{
    &Dog{
        Animal: Animal{
            Name: "Fido",
            IsMammal: true,
        },
        PackFactor: 5,
    },
    &Cat{
        Animal: Animal{
            Name: "Milo",
            IsMammal: true,
        },
        ClimbFactor: 4,
    },
}
for _, speaker := range speakers {
    speaker.Speak()
}

在新的程式碼中,我現在可以根據狗和貓的共同行為集將它們分組。的行為,也就是狗和貓能說話的事實,把它們歸為一類。 事實上,Animal 型別確實是型別汙染,因為宣告一個型別只是為了共享一組公共狀態是一種方式,應該避免.

type Dog struct {
    Name string
    IsMammal bool
    PackFactor int
}
type Cat struct {
    Name string
    IsMammal bool
    ClimbFactor int
}

在這種特殊情況下,我寧願看到動物型別被刪除,欄位被複制並貼上到狗和貓型別中。稍後我將有關於更好的模式的說明,以消除這些情況的發生。 以下是原始程式碼中的程式碼異類:

Animal 型別提供了一個可重用狀態的抽象層。

程式永遠不需要建立或單獨使用 Animal 型別的值。

泛化了 Animal 型別的 Speak 方法的實現。

永遠不會呼叫 Animal 型別的 Speak 方法。

總結

關於宣告型別的指南:

宣告代表新事物或獨特事物的型別。

不要僅僅為了可讀性而建立別名。

驗證任何型別的值是單獨建立或使用的。

嵌入型別不是因為我需要狀態,而是因為我們需要行為。

如果我不考慮行為,我就會將自己鎖定在未來不進行級聯程式碼更改就無法成長的設計中。

作為現有型別的別名或抽象的問題型別。

唯一目的是共享公共狀態的問題型別。

原文來自:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2851674/,如需轉載,請註明出處,否則將追究法律責任。

相關文章