「Golang成長之路」面向介面篇

yangkuang發表於2021-07-10

一、介面的概念

  • 介面是一種抽象型別,是對其他型別行為的概括與抽象,從語法角度來看,介面是一組方法定義的集合。
  • 很多物件導向的語言都有介面這個概念,但Go語言介面的獨特之處在於它是隱式實現。
  • 對於一個具體的型別,無須宣告它實現了哪些介面,只要提供介面所必需的方法即可。
  • 這種設計讓程式設計人員無須改變已有型別的實現就可以為這些型別建立新的介面——對於那些不能修改包的型別,這一點特別有用。

二、介面的定義和實現

下面我們直接來看看介面是怎樣定義的:

type 介面名 interface {
   方法1
   方法2
   ……
}

這個就是介面的定義
介面是由使用者定義,由實現者實現。
假如現在有一個任務:將 www.imooc.com 的首頁下載下來(假如是一個很大的工程),我們需要先進行測試,然後再產品上線。
1.實現測試部分:
先定義一個介面:

type Retriever interface{
     Get(cur string)string
}

下面來看看介面是如何實現的:
我們在另一個目錄中實現Get()方法(介面的實現)
測試部分:

package mock

//定義一個結構體
type Retrievers struct{
   Context string
}

//實現介面
func (r Retrievers)Get(cur string)string{
   return r.Context
}

2.產品上線部分:
然後我們再去目錄下對Get()方法的實現:

package real

import (
   "net/http"
 "net/http/httputil" "time")

//定義一個結構體
type Retrievers struct{
   UserAgent string
  TimeOut time.Duration
}

//Get介面的實現
func (r Retrievers)Get(cur string)string{
   re, err :=http.Get(cur)
   if err != nil{
      panic(err)
   }

   result, err :=httputil.DumpResponse(re, true)
   if err != nil{
      panic(err)
   }
   return string(result)
}

現在回到主程式中:

package main

import (
   "fmt"
 "interfacetest/retriever/mock" "interfacetest/retriever/real")

type Retriever interface {
   Get(cur string) string
}
//介面由使用者定義,介面的實現其實就是物件函式(方法)的實現
//golang中duck type
func download(r Retriever) string{
   //下載https://www.imooc.com網頁
   return r.Get("https://www.imooc.com")
}
//主函式
func main() {

   //mock.Retrievers{}表示來自於我們實現的mock包
   var q = mock.Retrievers{"my name"}
   fmt.Println(downloda(q))   //此時輸出的是my name

   //當測試部分通過過後就可以產品上線了
   //real.Retrievers{}表示來自於我們實現的real包
   var r Retriever= real.Retrievers{UserAgent: "hhh", TimeOut: 3}
   fmt.Println(download(r))  //這時就可以獲取https://www.imooc.com的首頁內容

三、介面的值型別

判斷介面值的型別有三種方法:

  1. fmt.Printf("%T, %v\n", x, x)

    例如:

    func main() {
    var q = mock.Retrievers{"my name"}
    
    var r Retriever= real.Retrievers{UserAgent: "hhh", TimeOut: 3}
    fmt.Printf("%T, %v\n", r, r)
    fmt.Printf("%T, %v\n", q, q)

    輸出為:

    real.Retrievers, {hhh 3ns}
    mock.Retrievers, {my name}
  2. switch
    例如:

    func inspect(r Retriever){
    fmt.Printf("%T, %v\n", r, r)
    switch v := r.(type){
    
    case mock.Retrievers:
       fmt.Println("mock.Retrievers:",v.Context)
    case real.Retrievers:
       fmt.Println("real.Retrievers:", v.UserAgent)
    case *mock.Retrievers:
       fmt.Println("*mock.Retrievers", v.Context)
     }
    }
  3. assertion
    例如:

    //使用assertion斷言型別
    if note , ok := r.(real.Retrievers);ok {
    fmt.Println(note.UserAgent)
    } else{
    fmt.Println("this not real Retrieers")

    介面也可以是指標

    func (r *Retrievers)Get(cur string)string{
    return r.Context
    }
    func main() {
    //&取地址
    var q = &mock.Retrievers{"my name"}

四、介面的組合

在Go語言中,可以在介面A中組合其它的一個或多個介面(如介面B、C),這種方式等價於在介面A中新增介面B、C中宣告的方法。

程式碼如下:

//介面中可以組合其它介面,這種方式等效於在介面中新增其它介面的方法  
type Reader interface {  
    read()  
}  

type Writer interface {  
    write()  
}  

//定義上述兩個介面的實現類  
type MyReadWrite struct{}  

//read介面的實現
func (mrw *MyReadWrite) read() {  
    fmt.Println("MyReadWrite...read")  
}  

//write介面的實現
func (mrw *MyReadWrite) write() {  
    fmt.Println("MyReadWrite...write")  
}  

//定義一個介面,組合了上述兩個介面  
type ReadWriter interface {  
    Reader  
    Writer  
}  

//上述介面等價於:  
type ReadWriterV2 interface {  
    read()  
    write()  
}  

//ReadWriter和ReadWriterV2兩個介面是等效的,因此可以相互賦值  
func interfaceTest() {  
    mrw := &MyReadWrite{}  
    //mrw物件實現了read()方法和write()方法,因此可以賦值給ReadWriter和ReadWriterV2  
    var rw1 ReadWriter = mrw  
    rw1.read()  
    rw1.write()  

    fmt.Println("------")  
    var rw2 ReadWriterV2 = mrw  
    rw2.read()  
    rw2.write()  

    //同時,ReadWriter和ReadWriterV2兩個介面物件可以相互賦值  
    rw1 = rw2  
    rw2 = rw1  
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章