Go Interface Go的面向對象思想

inteface接口 

interface 是GO語言的基礎特性之一。可以理解爲一種類型的規範或者約定。它跟java,C# 不太一樣,不需要顯示說明實現了某個接口,它沒有繼承或子類或“implements”關鍵字,只是通過約定的形式,隱式的實現interface 中的方法即可。因此,Golang 中的 interface 讓編碼更靈活、易擴展。

如何理解go 語言中的interface ?只需記住以下三點即可。

 

1、interface是方法聲明的集合

2、任何類型的對象實現了在interface接口中聲明的全部方法,則表明該類型實現了接口。

3、interface可以作爲一種數據類型,實現了該接口的任何對象都可以給對應的接口類型變量賦值。

 

interface 與 對象的 對應關係:interface可以被任意對象實現,一個類型/對象也可以實現多個interface.

Interface(接口)的意義:

實際上接口的最大的意義就是實現多態的思想, 就是我們可以根據interface類型來設計API接口,那麼這種API接口的適應能力不僅能適應當下所實現的全部模塊,也適應未來實現的模塊來進行調用。 調用未來可能就是接口的最大意義所在吧,這也是爲什麼架構師那麼值錢,因爲良好的架構師是可以針對interface設計一套框架,在未來許多年卻依然適用。

Interface  ,在 封裝,實現(implements: 其他語言叫繼承),多態。

例子 

package main

import "fmt"

type Phone interface {
	call()
}

type NokiaPhone struct {
}

func (nokiaPhone NokiaPhone) call() {
	fmt.Println("I am Nokia, I can call you!")
}

type ApplePhone struct {
}

func (iPhone ApplePhone) call() {
	fmt.Println("I am Apple Phone, I can call you!")
}

func main() {
	var phone Phone
	phone = new(NokiaPhone)
	phone.call()

	phone = new(ApplePhone)
	phone.call()
}
/**
多個對象 實現 接口,也體現了 多態 特性,同樣的一個接口類型 變量 ,分別被賦予(指向)不同的實體對象,調用call()方法,執行的結果不同,
這就體現出了 多態的特性。  也可以說 體現了 封裝(struct來體現) ,實現(implements), 多態

 */

面向對象中的依賴倒轉原則 : 一句話 業務邏輯層 ,實現層 要依賴 抽象,而不是依賴細節!。

一些原則 是 從戰略上 對業務進行抽象。

如何 對業務進行抽象出 一些 接口 (php語言中可以是 接口或抽象類)呢?來看一個例子:

業務層, 抽象層,實現層 。這個封層思想/方法 很好。一般程序員 可能是 業務層(需求)來了,想着 快速的實現(實現層),沒有仔細地思考抽象層);更好的方式是 :業務層(需求) ,再抽象層,再實現層,再業務層。這個思考方式可以一開始就這樣做,或者放在後面代碼優化的時候再這樣做。(因爲並不是所有的需求都能很好的去抽象出來一些公共的東西,要看具體的業務是什麼)

 

從 張三開奔馳,李四開寶馬 到 司機開車的抽象(抽象層); 張三,李四 實現司機;奔馳,寶馬實現車; 最後實現 張三開奔馳,李四開寶馬的業務需求(業務邏輯層)

代碼如下:

package main

import "fmt"

// ===== >   抽象層  < ========
type Car interface {
	Run()
}

type Driver interface {
	Drive(car Car)
}

// ===== >   實現層  < ========
type BenZ struct {
	//...
}

func (benz * BenZ) Run() {
	fmt.Println("Benz is running...")
}

type Bmw struct {
	//...
}

func (bmw * Bmw) Run() {
	fmt.Println("Bmw is running...")
}

type Zhang_3 struct {
	//...
}

func (zhang3 *Zhang_3) Drive(car Car) {
	fmt.Println("Zhang3 drive car")
	car.Run()
}

type Li_4 struct {
	//...
}

func (li4 *Li_4) Drive(car Car) {
	fmt.Println("li4 drive car")
	car.Run()
}


// ===== >   業務邏輯層  < ========
func main() {
	//張3 開 寶馬
	var bmw Car
	bmw = &Bmw{}

	var zhang3 Driver
	zhang3 = &Zhang_3{}

	zhang3.Drive(bmw)

	//李4 開 奔馳
	var benz Car
	benz = &BenZ{}

	var li4 Driver
	li4 = &Li_4{}

	li4.Drive(benz)
}

 

另一個 抽象的例子: 組裝電腦。

模擬組裝2臺電腦

--- 抽象層 ---

            有顯卡Card 方法display,有內存Memory 方法storage,有處理器CPU 方法calculate

--- 實現層 ---

            有 Intel因特爾公司 、產品有(顯卡、內存、CPU),有 Kingston 公司, 產品有(內存3),有 NVIDIA 公司, 產品有(顯卡)

--- 邏輯層 ---

1. 組裝一臺Intel系列的電腦,並運行  2. 組裝一臺 Intel CPU Kingston內存 NVIDIA顯卡的電腦,並運行

代碼如下:

package main
import "fmt"

//------  抽象層 -----
type Card interface{
	Display()
}

type Memory interface {
	Storage()

	/**
	模擬了一種情景,接口層寫好了,實現層寫好了,業務層寫好了。代碼完美運行了一段時間。某天某個人對接口層加上了一個方法!
	這時可能會出現什麼情況呢?
	1. 程序報錯了,報類型不對的錯誤! 原因是 子類(實現層的struct /對象)沒有實現這個方法,接口與這個 struct對象(php類比子類)無任何關係了。
	2。程序依然正常的運行着。 爲什麼呢?很可能是 struct對象 顯示地 繼承接口 的寫法。
	總結 看來顯示地 實現接口,也是有着一定好處的!同樣也可能意味着接口着很多或更多的功能(方法)你子類(struct對象)並沒有使用(實現)!!
	本人目前覺得 顯式地 繼承/實現 好一些,壞處有哪些?尚不清楚!
	與PHP implements/extends 比較 Go 的interface  似乎更加靈活,像php子類不實現接口中的所有方法就會報錯!!,而Go不會!
	顯示地 實現接口還有一個好處,就是我不用編輯器提示,我就知道這個struct 實現了該接口!這對大型工程,或者不使用編輯提示功能情況下,顯得尤其
	方便和重要!
	 */
	Card
}

type CPU interface {
	Calculate()
}

type Computer struct {
	cpu CPU
	mem Memory
	card Card
}


func (computer *Computer) DoWork() {
	computer.cpu.Calculate()
	computer.mem.Storage()
	computer.card.Display()
}

func NewComputer(cpu CPU, mem Memory, card Card) *Computer{
	return &Computer{
		cpu:cpu,
		mem:mem,
		card:card,
	}
}

//------  實現層 -----
//intel
type IntelCPU struct {
	//CPU                //這裏你可能有疑問,只要實現Calculate() 方法就可以被認爲是 CPU 了,然而寫了也沒錯,因爲抽象層和實現層都是你一個人
					//寫的,你很清楚你寫的IntelCPU 就是一款CPU ,相當於其他語言如PHP 顯式地 extends 或 implements
}

func (this *IntelCPU) Calculate() {
	fmt.Println("Intel CPU 開始計算了...")
}

type IntelMemory struct {
	Memory  //這裏你可能有疑問,只要實現Storage() 方法就可以被認爲是 Memory 了,然而寫了也沒錯,因爲抽象層和實現層都是你一個人
	//寫的,你很清楚你寫的IntelMemory 就是一款Memory ,相當於其他語言如PHP 顯式地 extends 或 implements; 這樣還有一個好處就是不管以後接口(抽象層)
	//又增加了一個方法(你不知道,抽象層也沒有義務非要告訴你),這時候顯式好處是 程序依然認爲你的 實現層IntelMemory 依然實現了Memory接口。
	//儘管你並沒有實現它(interface接口)的新增的那個方法!
}

func (this *IntelMemory) Storage() {
	fmt.Println("Intel Memory 開始存儲了...")
}

type IntelCard struct {
	Card
}

func (this *IntelCard) Display() {
	fmt.Println("Intel Card 開始顯示了...")
}

//kingston
type KingstonMemory struct {
	Memory
}

func (this *KingstonMemory) Storage() {
	fmt.Println("Kingston memory storage...")
}

//nvidia
type NvidiaCard struct {
	Card
}

func (this *NvidiaCard) Display() {
	fmt.Println("Nvidia card display...")
}
//------  業務邏輯層 -----
func main() {
	//intel系列的電腦
	com1 := NewComputer(&IntelCPU{}, &IntelMemory{}, &IntelCard{})
	com1.DoWork()

	//雜牌子
	com2 := NewComputer(&IntelCPU{}, &KingstonMemory{}, &NvidiaCard{})
	com2.DoWork()
}

 

總結 看來顯示地 實現接口,也是有着一定好處的!
1.這樣還有一個好處就是不管以後接口(抽象層)又增加了一個方法(你不知道,抽象層也沒有義務非要告訴你),這時候顯式好處是 程序依然認爲你的 實現層IntelMemory 依然實現了Memory接口。儘管你並沒有實現它(interface接口)的新增的那個方法!  同樣也可能意味着接口着很多或更多的功能(方法)你子類(struct對象)並沒有使用(實現)!!

2.顯示地 實現接口還有一個好處,就是我不用編輯器提示,我就知道這個struct 實現了該接口!這對大型工程,或者不使用編輯提示功能情況下,顯得尤其 方便和重要!

Go 是面向對象的編程語言嗎?

官方FAQ給出了標準答案: Yes  and No.

還有一種“模擬”產生子類的方法,就是通過在類型中嵌入其它的類型,但是這是一種“組合”的方式,而不是繼承。

嵌入深度。如果沒有嵌入,我們定義嵌入深度爲0,如果有嵌入,並且嵌入的接口沒有嵌入的話,我們稱之爲深度爲1,以此類推。比如下面接口A的嵌入深度爲2:

type A interface {
B // 嵌入
}

type B interface {
C
}
type C interface {
}

【標準庫中接口中嵌入接口的數量】:  絕大部分的接口(131 個)都不會嵌入其它接口的,嵌入最多的是mime/multipart.File[2],嵌入了四個接口:

其它 6 個項目(Docker、etcd、grpc-go、prometheus、consul、influxdb )使用接口的情況:可以看到同樣大部分接口嵌入數量都在 0 個或者 1 個,嵌入最多的是 kubernetes 的CoreV1Interface[3]接口,嵌入了 16 個接口,可以說是一個巨無霸嵌入接口了:

【嵌入的接口深度】:準庫中不使用太深的嵌入方式,比較多的也就是嵌入一次。

精選項目中使用接口的方式也一樣,很少使用嵌入深度很長的方式,最長也就是 2,而且只有兩個接口:kubernetes/.../Stream[4]和moby/.../WriteCommitCloser[5],而且主要是因爲嵌入io.ReadWriteCloserio.WriteCloser導致。

【接口中直接定義的方法的數量】:

準庫中接口定義的直接方法的數量都很少,不會設置很多的方法,接口都比較精巧幹練(7個以內),比較特殊的是reflect/Type[6]接口,定義了 31 個直接方法。 

同樣,精選項目中的接口直接定義的方法也比較少,超過 10 個方法的接口少之又少,最多的是influxdata/.../TSMFile[7]接口,定義了足足 42 個方法。

【接口中總的方法的數量】:

標準庫中 1 接口的總方法數量基本都在 8 個以下。

精選項目中接口定義的總方法基本都在 12 個以下。

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章