Go 為什么不支持類和繼承?
本文轉載自微信公眾號「腦子進煎魚了」,作者陳煎魚。轉載本文請聯系腦子進煎魚了公眾號。
大家好,我是煎魚。
大家在早期學習 Go 時,一旦跨過語法的階段后。馬上就會進入到一個新的糾結點,Go 不支持面向對象嗎?
這門編程語言里沒有類(class)、繼承(extends),沒法一把搜了,面試問啥面向對象(OOP)?
今天煎魚就帶大家一起來了解這之中的思考,Go 真的不支持嗎?
類和繼承
類是什么
類(class)在面向對象編程中是一種面向對象計算機編程語言的構造,是創建對象的藍圖,描述了所創建的對象共同的特性和方法(via @維基百科)。
例子如下:
- class SimpleClass
- {
- // 聲明屬性
- public $var = '腦子進煎魚了';
- // 聲明方法
- public function displayVar() {
- echo $this->var;
- }
- }
每個類的定義都以關鍵字 class 開頭,后面跟著類名,后面跟著一對花括號,里面包含有類的屬性與方法的定義。
繼承是什么
繼承是面向對象軟件技術當中的一個概念,如果一個類別 B “繼承自”另一個類別 A,就把這個 B 稱為 “A的子類”,而把 A 稱為 “B的父類別” 也可以稱 “A 是 B 的超類”(via @維基百科)。
例子如下:
- // 父類
- class Foo
- {
- public function printItem($string)
- {
- echo '煎魚1: ' . $string . PHP_EOL;
- }
- public function printPHP()
- {
- echo 'PHP is great.' . PHP_EOL;
- }
- }
- // 子類
- class Bar extends Foo
- {
- public function printItem($string)
- {
- echo '煎魚2: ' . $string . PHP_EOL;
- }
- }
繼承有如下兩個特性:
- 子類具有父類別的各種屬性和方法,不需要再次編寫相同的代碼。
- 子類別繼承父類時,可以重新定義某些屬性,并重寫某些方法,使其獲得與父類別不同的功能。
結構和組合
在 Go 里就比較 ”特別“ 了,因為沒有傳統的類,也沒有繼承。
取而代之的是結構和組合的方式。這也是業內對 Go 是否 OOP 爭議最大的地方。
結構體
我們可以在 Go 中通過結構體的方式來組織代碼,達到類似類的方式。
例子如下:
- package main
- import "fmt"
- type person struct {
- name string
- age int
- }
- func(p *person) hello(){}
- func newPerson(name string) *person {
- p := person{name: name}
- p.age = 42
- return &p
- }
- func main() {
- fmt.Println(person{"煎魚1", 22})
- fmt.Println(person{name: "煎魚2", age: 33})
- ...
- }
在上述代碼中,我們可以定義結構體內的屬性,也可以針對結構體這些類型定義只屬于他們的方法。
在聲明實例上,可以配合 newXXX 的初始化方法來生成,這是 Go 里約定俗成的方式。
組合
類的聲明采取結構體的方式取代后,也可以配套使用 ”組合“ 來達到類似繼承的效果。
例子如下:
- type man struct {
- name string
- }
- func (m *man) hello1() {}
- type person struct {
- man
- name string
- }
- func (p *person) hello2() {}
- func newPerson(name string) *person {
- p := person{name: name}
- return &p
- }
- func main() {
- p := newPerson("腦子進煎魚了")
- p.hello1()
- }
在上述代碼中,我們分別定義了 man 和 person 兩個結構體,并將 man 嵌入到 person 中,形成組合。
你可以在 main 方法中能夠看到,person 實例是可以使用和調用 man 實例的一些公開屬性和方法的。
在簡單的使用效果上會與繼承有些接近。
Go 是面向對象的語言嗎
“Go 語言是否一門面向對象的語言?”,這是一個日經話題。官方 FAQ 給出的答復是:
是的,也不是。原因是:
- Go 有類型和方法,并且允許面向對象的編程風格,但沒有類型層次。
- Go 中的 "接口 "概念提供了一種不同的方法,我們認為這種方法易于使用,而且在某些方面更加通用。還有一些方法可以將類型嵌入到其他類型中,以提供類似的東西,但不等同于子類。
- Go 中的方法比 C++ 或 Java 中的方法更通用:它們可以為任何類型的數據定義,甚至是內置類型,如普通的、"未裝箱的 "整數。它們并不局限于結構(類)。
- Go 由于缺乏類型層次,Go 中的 "對象 "比 C++ 或 Java 等語言更輕巧。
為什么不支持類和繼承
有的人認為類和繼承是面向對象的必要特性,必須要有,才能是面向對象的語言,但其實也并非如此。
面向對象(OOP)有不同的含義和解讀,許多概念也可以通過結構體、組合和接口等方式進行表達,說是不支持傳統的 OOP。
其實真相是 Go 是選擇了另外一條路,也就是 ”組合優于繼承“。我們所提到的類和繼承并不是定義 OOP 的一種準則,只是協助完成 OOP 的方法之一。
不要本末倒置了,不讓工具來定義 OOP 的理念。
總結
在今天這篇文章中,我們介紹了常說的類和繼承的業內定義和使用案例。同時面向 Go 讀者群里的疑惑,進行了解答。
實質上,Go 是 OOP,也不是 OOP。類和繼承只是實現 OOP 的一種方式,但并不是沒有這兩者,他就不是 OOP 了。
不支持的原因也很明確,Go 在設計上,選擇了組合優于繼承的編程設計模式,它不是傳統那種面向類型的范式。