Go 語言極速入門5 - 面向接口

一、接口

1.1、定義接口

// notifier 是一個定義了通知類行為的接口
type notifier interface {
    // 接口方法
    notify()
}

1.2、實現(xiàn)接口

使用值接收者實現(xiàn)接口

// notify 是使用值接收者實現(xiàn) notifier interface 接口的方法
// sendNotification(&u) 和 sendNotification(u) 都可
func (u user) notify() {
    fmt.Println("notify", u)
}
  • 對于值接收者,sendNotification(&u) 和 sendNotification(u) 都可

使用指針接收者實現(xiàn)接口

// notify 是使用指針接收者實現(xiàn) notifier interface 接口的方法
// 只能使用 sendNotification(&u)
func (u *user) notify() {
    fmt.Println("notify", *u)
}
  • 對于指針接收者,只能使用 sendNotification(&u)

非常重要的點 - 值接收者與指針接收的對比

type User struct {
    Name  string
    Email string
}
// 注意這個是值接收者
func (u User) Notify() error {
    u.Name = "alimon"
    return nil
}

func main() {
    u := &User{"Damon", "damon@xxoo.com"}
    u.Notify()
    log.Println(u.Name)
}

注意

  • 值接收者(func (u User) Notify() error)操作的是 User 的副本(不管調(diào)用者使用值還是地址),所以不會改變其內(nèi)部的值,這里輸出還是 Damon
  • 指針接收者(func (u *User) Notify() error)操作的是 User 本身,所以其內(nèi)部的值會發(fā)生變化,這里輸出是 alimon

什么時候使用值接收者,什么時候使用指針接收者

https://stackoverflow.com/questions/27775376/value-receiver-vs-pointer-receiver-in-golang 防止文章被刪掉,下邊直接摘抄出來:

  • If the receiver is a map, func or chan, don't use a pointer to it.
  • If the receiver is a slice and the method doesn't reslice or reallocate the slice, don't use a pointer to it.
  • If the method needs to mutate(使 receiver 改變) the receiver, the receiver must be a pointer.
  • If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.
  • If the receiver is a large struct or array, a pointer receiver is more efficient. How large is large? Assume it's equivalent to passing all its elements as arguments to the method. If that feels too large, it's also too large for the receiver.
  • Can function or methods, either concurrently or when called from this method, be mutating the receiver? A value type creates a copy of the receiver when the method is invoked, so outside updates will not be applied to this receiver. If changes must be visible in the original receiver, the receiver must be a pointer.
  • If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.
  • If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used
  • If the receiver is a small array or struct that is naturally a value type (for instance, something like the time.Time type), with no mutable fields and no pointers, or is just a simple basic type such as int or string, a value receiver makes sense.
    A value receiver can reduce the amount of garbage that can be generated; if a value is passed to a value method, an on-stack copy can be used instead of allocating on the heap. (The compiler tries to be smart about avoiding this allocation, but it can't always succeed.) Don't choose a value receiver type for this reason without profiling first.
  • Finally, when in doubt, use a pointer receiver.

總結(jié)一下:

  • 如果需要改變 receiver 內(nèi)部的屬性值,選擇指針接收者;
  • 如果 struct 中的一個方法使用了指針接收者,那么該 struct 內(nèi)的全部方法都是用指針接收者 - 一致性

1.3、使用接口

// sendNotification 接受一個實現(xiàn)了 notifier 接口的值并發(fā)送通知
func sendNotification(n notifier) {
    n.notify()
}

func main() {
    u := user{"nana"}
    sendNotification(&u) // notify {nana}
}

二、實現(xiàn)多態(tài)

image.png

api.go

package api

// 定義接口
type Notifier interface {
    Notify()
}

接口名和接口方法大寫表示 public,小寫表示 java 的 default(即包隔離級別)

user.go

package impl

import "fmt"

// 定義實現(xiàn)類 user
type User struct {
    Name string
}

func (u *User) Notify() {
    fmt.Println("user", *u)
}

admin.go

package impl

import "fmt"

// 定義實現(xiàn)類 Admin,首字母大寫表示 public
type Admin struct {
    Name string
}

func (a *Admin) Notify() {
    fmt.Println("admin", *a)
}

main.go

package main

import (
    // 相對于 GOPATH/src 下的地址
    "github.com/zhaojigang/helloworld/ooi/api"
    "github.com/zhaojigang/helloworld/ooi/impl"
)

func sendNotification(n api.Notifier) {
    n.Notify()
}

func main() {
    u := impl.User{"nana"}
    sendNotification(&u) // user {nana}

    a := impl.Admin{"zhao"}
    sendNotification(&a) // admin {zhao}
}

三、嵌入類型內(nèi)部接口實現(xiàn)提升

基于上述程序修改 admin.go 和 main.go。

admin.go

package impl

// 定義實現(xiàn)類 Admin,首字母大寫表示 public
type Admin struct {
    User // 嵌入類型
    Name string
}

注意:該類沒有實現(xiàn)接口 notifier 的 notify() 方法,但是由于該類的內(nèi)嵌類 User 實現(xiàn)了,內(nèi)嵌類會提升到外部類中,所以 Admin 類也是 notifier 接口的實現(xiàn)類,其實現(xiàn)函數(shù)就是 User#Notify() ,當然,可以 Admin 可以自己實現(xiàn) Notify() 來覆蓋 User#Notify()。

main.go

package main

import (
    // 相對于 GOPATH/src 下的地址
    "github.com/zhaojigang/helloworld/ooi/api"
    "github.com/zhaojigang/helloworld/ooi/impl"
)

func sendNotification(n api.Notifier) {
    n.Notify()
}

func main() {
    u := impl.User{"nana"}
    sendNotification(&u) // user {nana}

    a := impl.Admin{u,"zhao"}
    sendNotification(&a) // user {nana}
}

四、組合接口

=========================== Api1 ===========================
package api

type Api1 interface {
    Api1()
}
=========================== Api2 ===========================
package api

type Api2 interface {
    Api2()
} 
=========================== Api12(組合接口) ===========================
package api

type Api12 interface {
    Api1
    Api2
}
=========================== Api12Impl ===========================
package impl

import "fmt"

type Api12Impl struct {
    Name string
}

func (api12 *Api12Impl) Api1()  {
    fmt.Println("api1", api12.Name)
}

func (api12 *Api12Impl) Api2()  {
    fmt.Println("api2", api12.Name)
}
=========================== main ===========================
package main

import (
    // 相對于 GOPATH/src 下的地址
    "github.com/zhaojigang/helloworld/ooi/api"
    "github.com/zhaojigang/helloworld/ooi/impl"
)

func sendNotification(n api.Api12) {
    n.Api1() // api1 nana
    n.Api2() // api2 nana
}

func main() {
    api12 := impl.Api12Impl{"nana"}
    sendNotification(&api12)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,243評論 0 38
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,841評論 0 10
  • 01 前段時間網(wǎng)上熱議的財務(wù)自由9個階段,對照了一眼,還是決定默默地搬磚去: 第一階段——菜場自由 在菜場,自己愿...
    明月渠閱讀 578評論 0 0
  • 滿腔熱血把師學(xué)會,當了教師吃苦受罪。 急難險重必需到位,教師育人終日疲憊。 學(xué)生告狀回回都對,工資不高還要交稅。 ...
    鈍角閱讀 680評論 0 42
  • 從小到大,他考試基本都不及格。他爸爸說,沒事,學(xué)一半就行了。小學(xué)的時候,語文數(shù)學(xué)最好考過68分。永遠是班里最后一...
    艾小年閱讀 237評論 0 0

友情鏈接更多精彩內(nèi)容