1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Golang接口類型-上篇

        共 6025字,需瀏覽 13分鐘

         ·

        2021-10-24 23:17

        目錄

        • 1、概述

        • 2、接口的隱式實(shí)現(xiàn)

        • 3、接口定義和聲明

        • 4、接口類型賦值

        • 5、接口類型對(duì)象

        • 6、接口應(yīng)用舉例


        1、概述

        接口是計(jì)算機(jī)系統(tǒng)中多個(gè)組件共享的邊界,不同的組件能夠在邊界上交換信息。接口的本質(zhì)是引入一個(gè)新的中間層,調(diào)用方可以通過接口與具體實(shí)現(xiàn)分離,解除上下游的耦合,上層的模塊不再需要依賴下層的具體模塊,只需要依賴一個(gè)約定好的接口

        簡(jiǎn)單來說,Go語言中的接口就是一組方法的簽名。接口是Go語言整個(gè)類型系統(tǒng)的基石,其他語言的接口是不同組件之間的契約的存在,對(duì)契約的實(shí)現(xiàn)是強(qiáng)制性的,必須顯式聲明實(shí)現(xiàn)了該接口,這類接口稱之為“侵入式接口”。而Go語言的接口是隱式存在,只要實(shí)現(xiàn)了該接口的所有函數(shù)則代表已經(jīng)實(shí)現(xiàn)了該接口,并不需要顯式的接口聲明

        接口的比喻?

        一個(gè)常見的例子,電腦上只有一個(gè)USB接口。這個(gè)USB接口可以接MP3、數(shù)碼相機(jī)、攝像頭、鼠標(biāo)、鍵盤等。所有的上述硬件都可以公用這個(gè)接口,有很好的擴(kuò)展性,該USB接口定義了一種規(guī)范,只要實(shí)現(xiàn)了該規(guī)范,就可以將不同的設(shè)備接入電腦,而設(shè)備的改變并不會(huì)對(duì)電腦本身有什么影響(低耦合)

        接口表示調(diào)用者和設(shè)計(jì)者的一種約定,在多人合作開發(fā)同一個(gè)項(xiàng)目時(shí),事先定義好相互調(diào)用的接口可以大大提高開發(fā)的效率。接口是用類來實(shí)現(xiàn)的,實(shí)現(xiàn)接口的類必須嚴(yán)格按照接口的聲明來實(shí)現(xiàn)接口提供的所有功能。有了接口,就可以在不影響現(xiàn)有接口聲明的情況下,修改接口的內(nèi)部實(shí)現(xiàn),從而使兼容性問題最小化

        2、接口的隱式實(shí)現(xiàn)

        Java中實(shí)現(xiàn)接口需要顯式地聲明接口并實(shí)現(xiàn)所有方法,而在Go中實(shí)現(xiàn)接口的所有方法就隱式地實(shí)現(xiàn)了接口 定義接口需要使用interface關(guān)鍵字,在接口中只能定義方法簽名,不能包含成員變量,例如

        type?error?interface?{
        ?Error()?string
        }

        如果一個(gè)類型需要實(shí)現(xiàn)error接口,那么它只需要實(shí)現(xiàn)Error() string方法,下面的RPCError結(jié)構(gòu)體就是 error接口的一個(gè)實(shí)現(xiàn)

        type?RPCError?struct?{
        ?Code????int64
        ?Message?string
        }

        func?(e?*RPCError)?Error()?string?{
        ?return?fmt.Sprintf("%s,?code=%d",?e.Message,?e.Code)
        }

        會(huì)發(fā)現(xiàn)上述代碼根本就沒有error接口的影子,這正是因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Go語言中接口的實(shí)現(xiàn)都是隱式的

        3、接口定義和聲明

        接口是自定義類型,是對(duì)其他類型行為的抽象(定義一個(gè)接口類型,把其他類型的值賦值給自定義的接口)

        接口定義使用interface標(biāo)識(shí),聲明了一系列的函數(shù)簽名(函數(shù)名、函數(shù)參數(shù)、函數(shù)返回值)在定義接口時(shí)可以指定接口名稱,在后續(xù)聲明接口變量時(shí)使用

        聲明接口變量只需要定義變量類型為接口名,此時(shí)變量被初始化為nil

        package?main

        import?"fmt"

        type?Sender?interface?{
        ?Send(to?string,?msg?string)?error
        ?SendAll(tos?[]string,?msg?string)?error
        }

        func?main()??{
        ?var?sender?Sender
        ?fmt.Printf("%T?%v\n",?sender,?sender)??//??
        }

        4、接口類型賦值

        為接口類型方法賦值,一般是定義一個(gè)結(jié)構(gòu)體,需要保證結(jié)構(gòu)體方法(方法名、參數(shù))均與接口中定義相同

        package?main

        import?"fmt"

        type?Sender?interface?{
        ?Send(to?string,?msg?string)?error
        ?SendAll(tos?[]string,?msg?string)?error
        }

        type?EmailSender?struct?{
        }

        func?(s?EmailSender)?Send(to,?msg?string)?error?{
        ?fmt.Println("發(fā)送郵件給:",?to,?",消息內(nèi)容是:",?msg)
        ?return?nil
        }

        func?(s?EmailSender)?SendAll(tos?[]string,?msg?string)?error?{
        ?for?_,?to?:=?range?tos?{
        ??s.Send(to,?msg)
        ?}
        ?return?nil
        }

        func?main()?{
        ?var?sender?Sender?=?EmailSender{}
        ?fmt.Printf("%T?%v\n",?sender,?sender)?//??
        ?sender.Send("geek",?"早上好")
        ?sender.SendAll([]string{"aa","bb"},?"中午好")
        }

        使用接口的好處,概念上可能不好理解,來一個(gè)實(shí)際例子

        package?main

        import?"fmt"

        type?Sender?interface?{
        ?Send(to?string,?msg?string)?error
        ?SendAll(tos?[]string,?msg?string)?error
        }

        type?EmailSender?struct?{
        }

        func?(s?EmailSender)?Send(to,?msg?string)?error?{
        ?fmt.Println("發(fā)送郵件給:",?to,?",消息內(nèi)容是:",?msg)
        ?return?nil
        }

        func?(s?EmailSender)?SendAll(tos?[]string,?msg?string)?error?{
        ?for?_,?to?:=?range?tos?{
        ??s.Send(to,?msg)
        ?}
        ?return?nil
        }

        type?SmsSender?struct?{
        }

        func?(s?SmsSender)?Send(to,?msg?string)?error?{
        ?fmt.Println("發(fā)送短信給:",?to,?",?消息內(nèi)容是:",?msg)
        ?return?nil
        }

        func?(s?SmsSender)?SendAll(tos?[]string,?msg?string)?error?{
        ?for?_,?to?:=?range?tos?{
        ??s.Send(to,?msg)
        ?}
        ?return?nil
        }

        //func?do(sender?EmailSender)?{
        func?do(sender?Sender)?{
        ?sender.Send("領(lǐng)導(dǎo)",?"工作日志")
        }

        func?main()?{
        ?var?sender?Sender?=?EmailSender{}
        ?fmt.Printf("%T?%v\n",?sender,?sender)?//??
        ?sender.Send("geek",?"早上好")
        ?sender.SendAll([]string{"aa","bb"},?"中午好")
        ?do(sender)
        ?sender?=?SmsSender{}
        ?do(sender)
        }

        按照上面的示例,最后定義變量sender為接口類型Sender,調(diào)用接口方法時(shí),只需要指定接口類型對(duì)應(yīng)的結(jié)構(gòu)體是什么,因?yàn)樵诙x接口時(shí),已經(jīng)聲明了此接口實(shí)現(xiàn)了Send、SendAll兩個(gè)方法

        var?sender?Sender?=?EmailSender{}
        //?或
        var?sender?Sender?=?SmsSender{}
        //?單獨(dú)定義go函數(shù)調(diào)用
        func?do(sender?Sender)?{
        ?sender.Send("領(lǐng)導(dǎo)",?"工作日志")
        }

        如果沒有接口,那么最終調(diào)用時(shí),還需要對(duì)應(yīng)上其具體的結(jié)構(gòu)體類型,寫法為

        var?sender?EmailSender?=?EmailSender{}
        //?或
        var?sender?SmsSender?=?SmsSender{}
        //?單獨(dú)定義go函數(shù)調(diào)用
        func?do(sender?EmailSender)?{
        //?func?do(sender?SmsSender)?{
        ?sender.Send("領(lǐng)導(dǎo)",?"工作日志")
        }

        很明顯,前者使用接口定義變量,在傳參時(shí)也使用接口類型定義,在使用上更為簡(jiǎn)單,僅僅只需要調(diào)整初始化的結(jié)構(gòu)體類型即可

        5、接口類型對(duì)象

        當(dāng)自定義類型實(shí)現(xiàn)了接口類型中聲明的所有函數(shù)時(shí),則該類型的對(duì)象可以賦值給接口變量,并使用接口變量調(diào)用實(shí)現(xiàn)的接口

        • 方法接收者全為值類型 如上面的例子

        • 方法接收者全為指針類型

        package?main

        import?"fmt"

        type?Sender?interface?{
        ?Send(to?string,?msg?string)?error
        ?SendAll(tos?[]string,?msg?string)?error
        }

        type?SmsSender?struct?{
        }

        func?(s?*SmsSender)?Send(to,?msg?string)?error?{
        ?fmt.Println("發(fā)送短信給:",?to,?",?消息內(nèi)容是:",?msg)
        ?return?nil
        }

        func?(s?*SmsSender)?SendAll(tos?[]string,?msg?string)?error?{
        ?for?_,?to?:=?range?tos?{
        ??s.Send(to,?msg)
        ?}
        ?return?nil
        }

        func?do(sender?Sender)?{
        ?sender.Send("領(lǐng)導(dǎo)",?"工作日志")
        }

        func?main()?{
        ?var?sender?Sender?=?&SmsSender{}??//?指針類型
        ?do(sender)
        }
        • 方法接收者既有值類型又有指針類型

        WechatSendersendsendAll,send有指針和值,sendAll只有指針,因此初始化的時(shí)候只能用指針,不能用值

        package?main

        import?"fmt"

        type?Sender?interface?{
        ?Send(to?string,?msg?string)?error
        ?SendAll(tos?[]string,?msg?string)?error
        }

        type?WechatSender?struct?{
        }

        //?Send?接收者為值對(duì)象
        func?(s?WechatSender)?Send(to,?msg?string)?error?{
        ?fmt.Println("發(fā)送微信給:",?to,?",?消息內(nèi)容是:",?msg)
        ?return?nil
        }

        //?SendAll?接收者為指針對(duì)象
        func?(s?*WechatSender)?SendAll(tos?[]string,?msg?string)?error?{
        ?for?_,?to?:=?range?tos?{
        ??s.Send(to,?msg)
        ?}
        ?return?nil
        }

        //func?do(sender?EmailSender)?{
        func?do(sender?Sender)?{
        ?sender.Send("領(lǐng)導(dǎo)",?"工作日志")
        }

        func?main()?{
        ?var?sender?Sender?=?&WechatSender{}
        ?do(sender)
        }

        當(dāng)接口(A)包含另外一個(gè)接口(B)中聲明的所有函數(shù)時(shí)(A 接口函數(shù)是 B 接口函數(shù)的父集,B 是 A 的子集),接口(A)的對(duì)象也可以賦值給其子集的接口(B)變量

        package?main

        import?"fmt"

        type?SignalSender?interface?{
        ?Send(to,?msg?string)?error
        }

        type?Sender?interface?{
        ?Send(to?string,?msg?string)?error
        ?SendAll(tos?[]string,?msg?string)?error
        }

        ...

        func?main()?{
        ?var?ssender?SignalSender?=?sender??//?以接口的變量初始化另外一個(gè)接口
        ?ssender.Send("aa",?"你好")
        }

        若兩個(gè)接口聲明同樣的函數(shù)簽名,則這兩個(gè)接口完全等價(jià) 當(dāng)類型和父集接口賦值給接口變量時(shí),只能調(diào)用接口變量定義接口中聲明的函數(shù)(方法)

        6、接口應(yīng)用舉例

        實(shí)際的生產(chǎn)例子,可以加深對(duì)接口的理解。例如多個(gè)數(shù)據(jù)源推送和查詢數(shù)據(jù)

        package?main

        import?(
        ?"fmt"
        ?"log"
        )

        /*
        1、多個(gè)數(shù)據(jù)源
        2、query方法查詢數(shù)據(jù)
        3、pushdata方法寫入數(shù)據(jù)
        ?*/


        type?DataSource?interface?{
        ?PushData(data?string)
        ?QueryData(name?string)?string
        }

        type?redis?struct?{
        ?Name?string
        ?Addr?string
        }

        func?(r?*redis)?PushData?(data?string)?{
        ?log.Printf("pushdata,name:%s,data:%s\n",?r.Name,data)
        }
        func?(r?*redis)?QueryData?(name?string)?string?{
        ?log.Printf("querydata,name:%s,data:%s\n",?r.Name,name)
        ?return?name?+?"redis"
        }

        type?kafka?struct?{
        ?Name?string
        ?Addr?string
        }

        func?(k?*kafka)?PushData?(data?string)?{
        ?log.Printf("pushdata,name:%s,data:%s\n",?k.Name,data)
        }
        func?(k?*kafka)?QueryData?(name?string)?string?{
        ?log.Printf("querydata,name:%s,data:%s\n",?k.Name,name)
        ?return?name?+?"kafka"
        }

        var?Dm?=?make(map[string]DataSource)

        func?main()??{
        ?r:=redis{
        ??Name:?"redis",
        ??Addr:?"127.0.0.1",
        ?}
        ?k:=kafka{
        ??Name:"kafka",
        ??Addr:"192.169.0.1",
        ?}
        ?//?注冊(cè)數(shù)據(jù)源到承載的容器中
        ?Dm["redis"]?=?&r
        ?Dm["kafka"]?=?&k
        ?//?推送數(shù)據(jù)
        ?for?i:=0;i<5;i++{
        ??key:=fmt.Sprintf("key_%d",?i)
        ??for?_,ds:=range?Dm{
        ???ds.PushData(key)
        ??}
        ?}
        ?//?查詢數(shù)據(jù)
        ?for?i:=0;i<5;i++{
        ??key:=fmt.Sprintf("key_%d",?i)
        ??//r:=Dm["redis"]
        ??//r.QueryData(key)
        ??for?_,ds:=range?Dm{
        ???res:=ds.QueryData(key)
        ???log.Printf("query_from_ds,res:%s",?res)
        ??}
        ?}
        }

        See you ~

        參考資料

        [1]

        https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-interface/

        ? 歡迎進(jìn)群一起進(jìn)行技術(shù)交流

        ? 加群方式:公眾號(hào)消息私信“加群或加我好友再加群均可

        瀏覽 107
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            成人在线国产 | 娇淫青春之放纵h第二部 | 麻豆蜜桃精品久久午夜福利区二区 | 男女艹逼 | 欧美黄大片 | 成人黄色A| 男人爽女人下面动态图 | 久久丫精品久久 | 国产女人18水真多18精品 | 鲁一鲁A片久久久久 |