轉載:原文鏈接
Golang是不支持繼承的,因此我們在使用的時候往往使用組合。那麼,組合與繼承有什麼區別呢?組合和繼承都是面向對象編程中重要的概念。繼承讓一個類獲得另一個類的屬性和方法,形成層級關係,子類可以重用父類的功能。而組合則是將一個類的對象作為另一個類的成員變量,實現代碼複用和模塊化。繼承建立的是"is-a"關係,表示一種類型的擴展;而組合則是"has-a"關係,表示對象之間的包含關係。相比繼承,組合更加靈活,降低了類之間的耦合度,使得代碼更易於維護和擴展。當然,Golang中,可以讓父組件直接獲取子組件的公開函數和公開屬性。但是子組件和父組件之間依然是隔離的。
看一下如下例子:
package main
import "fmt"
type ClassA struct {}
func (a ClassA) Name() string { return "ClassA"}
func (a ClassA) PrintName() { fmt.Printf("I am %s", a.Name())}
type ClassB struct { ClassA}
func (b ClassB) Name() string { return "ClassB"}
func main() { var obj = new(ClassB) obj.PrintName()}
看似,我們想讓classB繼承classA,並重寫了Name方法。但實際上,classB只是覆蓋了Name方法,而沒有重寫他,對於classB裏面的classA,其仍然與classB隔離。當classA訪問Name方法時,不會用到classB的Name方法,依然是classA的Name方法。這一點與C++的非虛函數比較相似。這是組合很典型的一個特徵:隔離。
為什麼會寫出上面的代碼呢?因為我們誤把組合當繼承了。ClassB與ClassA的關係不是is-a,也就是ClassB不是ClassA的一種。相反,ClassB擁有了ClassA,他們是has-a關係。也就是ClassA的Name函數和CLassB的Name函數沒有關係。就相當於菜籃子裏面有蘋果,菜籃子有自己的名字,蘋果也有自己的名字。那麼PrintName函數是屬於ClassA的,他打印的是ClassA的名字,那當然和ClassB的Name沒關係。只不過我們錯誤的使用了一些魔法,讓原本應該為obj.ClassA.PrintName的方法變成了obj.PrintName。
那麼,什麼時候我們需要用到匿名屬性(類似繼承)的這種魔法呢?在我們定義一些結構體時,他們具有相同的屬性,並且這些屬性與其他屬性相對較為隔離。
常見的,我們定義web的返回形參類型:
type Resp { code int64 msg string}
type LoginResp { Resp Data {某種結構}}
type RegisterResp { Resp Data {某種結構}}
正如上面,Resp每次請求都會返回的內容,而且其與Data在行為上基本上沒有關聯。
Go在這方面確實與C更為接近,而不是與C++或者Java更為接近。