Go接口與反射

1、接口node

1.什麼是interfacegolang

在面向對象編程中,能夠這麼說:「接口定義了對象的行爲」, 那麼具體的實現行爲就取決於對象了。算法

在Go中,接口是一組方法簽名。當一個類型爲接口中的全部方法提供定義時,它被稱爲實現該接口。它與oop很是類似。接口指定類型應具備的方法,類型決定如何實現這些方法。編程

2.定義app

Interface類型能夠定義一組方法,可是這些不須要實現,而且interface不能包含任何變量負載均衡

type 標識符 interface{
    Method1(參數列表) 返回值列表
    Method2(參數列表) 返回值列表
    ...
}

3.接口的實現dom

golang中的接口,不須要顯式的實現,只要一個變量,含有接口類型中的全部方法,那麼這個變量就實現這個接口。所以,golang中沒有implement相似的關鍵字ide

若是一個變量含有多個interface類型的方法,那麼這個變量就實現了多個接口
若是一個變量只含有一個interface的部分方法,那麼這個變量沒有實現這個函數

package main

import "fmt"

//定義一個薪資計算的接口,該接口有CalculateSalary方法,並無具體實現,只是一種規範
type SalaryCalculate interface{
    CalculateSalary() int
}

type Manager struct{
    basicpay int
    bonus int
}

type General struct{
    basicpay int
}

//類型去具體實現接口中的方法
func (this Manager) CalculateSalary() int{
    return this.basicpay + this.bonus
}

func (this General) CalculateSalary() int{
    return this.basicpay
}

func totalExpense(p []SalaryCalculate){
    total := 0
    for _, v := range p{
        //使用者不須要關心底層,只須要調用接口
        total += v.CalculateSalary()
    }
    fmt.Printf("總開支 %d", total)
}

func main(){
    Manager1 := Manager{10000, 6000}
    Manager2 := Manager{8000,7500}
    General1 := General{5000}
    //值類型實現接口
    employees := []SalaryCalculate{Manager1, Manager2, General1}
    totalExpense(employees)
}
接口實現

4.多態:一種事物的多種形態,均可以按照統一的接口進行操做oop

例如:咱們定義一個車的接口,該接口擁有各類車共同擁有的方法,而該接口能夠表明各類車,咱們只須要讓具體的車去實現這個接口。

例如:Linux中的一切皆文件的思想,不管是磁盤,內存,仍是...都是文件,咱們只須要定義一個操做文件的接口,而各個具體的東西去實現接口的方法,在調用的時候只須要經過接口進行操做

例如:GO中sort包下Sort方法爲咱們提供了排序的原語,咱們只須要去實現接口的方法便可,該包具備很強的擴展性,不須要爲每一個類型都定義一個方法,只須要提供一個排序的接口,而對於使用者也是友好的,咱們不須要去記住每一個類型所應該對應的排序方法。

type Interface interface {
    // Len is the number of elements in the collection.
    // Len 爲集合內元素的總數
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    //
    // Less 返回索引爲 i 的元素是否應排在索引爲 j 的元素以前。
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    // Swap 交換索引爲 i 和 j 的元素
    Swap(i, j int)
}

package main

import (
    "sort"
    "fmt"
    "math/rand"
)

type Student struct{
    ID string
    Name string
}

type StudentArry []Student

func (this StudentArry) Len() int{
    return len(this)
}

func (this StudentArry) Less(i, j int) bool{
    return this[i].Name < this[j].Name
}

func (this StudentArry) Swap(i, j int){
    this[i], this[j] = this[j], this[i]
}

func trans(n StudentArry){
    for _, v := range n{
        fmt.Println(v)
    }
}

func main(){
    var stus StudentArry
    for i := 0; i < 10; i++{
        stu := Student{
            ID: fmt.Sprintf("154%d", rand.Int()),
            Name: fmt.Sprintf("stu%d", rand.Intn(100)),
        }
        stus = append(stus,stu)
    }
    trans(stus)
    fmt.Printf("\n")
    sort.Sort(stus)
    trans(stus)
}
經過排序接口實現排序

5.接口嵌套

package main

import "fmt"

type Read interface{
    Read()
}

type Write interface{
    Write()
}

type ReadWrite interface{
    Read
    Write
}

type File struct{

}

func (this File) Read(){
    fmt.Println("read data")
}

func (this File) Write(){
    fmt.Println("write data")
}

func Test(v ReadWrite)(){
    v.Read()
    v.Write()
}

func main(){
    var f File
    Test(f)
}
View Code

6.空接口,interface{}

空接口沒有任何方法,因此全部類型都實現了空接口,也就是任何變量均可以賦值給空接口

var a int
var b interface{}
b = a

判斷一個變量是否實現了指定接口

if sv, ok := v.(Stringer); ok{
    fmt.Println("ok")
}

7.類型斷言,因爲接口是通常類型,不知道具體類型,若是要轉成具體類型,能夠採用如下方法進行轉換

var t int
var x inteface{}
x = t
y = x.(int) //轉成int
package main

import "fmt"

type Student struct{
    Name string
}

func CheckType(items ...interface{}){
    for _, v := range items{
        switch v.(type) {
        case bool:
            fmt.Printf("%v params is bool\n", v)
        case int:
            fmt.Printf("%v params is int\n", v)
        case Student:
            fmt.Printf("%v params is Studen\nt", v)
        case *Student:
            fmt.Printf("%v params is *Student\n", v)
        }
    }
}

func main(){
    stu := Student{
        Name: "bob",
    }

    CheckType(false, 1, stu, &stu)
}
使用.(type)判斷變量的類型

實現一個通用的鏈表類

//link.go
package main

import "fmt"

type Node struct{
    Data interface{}
    Next *Node
}

type Link struct{
    Head *Node
    Tail *Node
}

func (this *Link) IsEmpty() bool{
    return this.Head == nil
}

func (this *Link) Init(node *Node){
    this.Head = node
    this.Tail = node
}

func (this *Link) Insert(data interface{}){
    node := &Node{
        Data: data,
    }

    if this.IsEmpty(){
        this.Init(node)
        return
    }
    node.Next = this.Head
    this.Head = node
}

func (this *Link) Trans(){
    cur := this.Head
    for cur != nil{
        fmt.Println(cur.Data)
        cur = cur.Next
    }
}

func (this *Link) Append(data interface{}){
    node := &Node{
        Data: data,
    }

    if this.IsEmpty(){
        this.Init(node)
        return
    }

    this.Tail.Next = node
    this.Tail = node
}


//main.go
package main

import "fmt"

func main(){
    var link Link
    for i := 0; i < 10; i++{
        link.Insert(fmt.Sprintf("stu%d",i))
    }
    for i := 0; i < 10; i++{
        link.Append(fmt.Sprintf("stu%d",i))
    }
    link.Trans()
}
View Code

變量slice和接口slice之間的賦值操做,for range

2、反射

能夠在運行時動態獲取變量的相關信息

1.兩個函數

reflect.TypeOf,獲取變量的類型,返回reflect.Type類型
reflect.ValueOf,獲取變量的值,返回reflect.Value類型
import (
    "fmt"
    "reflect"
)
type Student struct{
    Name string
    Age int
    Score float32
}

func Test(n interface{}){
    a := reflect.TypeOf(n)
    fmt.Println(a)
    v := reflect.ValueOf(n)
    fmt.Println(v)
    fmt.Printf("a type is %T; v type is %T",a, v)
}


func main(){
    var stu Student
    Test(stu)
}

/*
main.Student
{ 0 0}
a type is *reflect.rtype; v type is reflect.Value
*/
View Code

經過reflect.Value類型咱們能夠分析變量的各類屬性

其中,reflect.Value.Kind,獲取變量的類別,返回一個常量

const (
    Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
    Uintptr
    Float32
    Float64
    Complex64
    Complex128
    Array
    Chan
    Func
    Interface
    Map
    Ptr
    Slice
    String
    Struct
    UnsafePointer
)
type Student struct{
    Name string
    Age int
    Score float32
}

func Test(n interface{}){
    a := reflect.TypeOf(n)
    fmt.Println(a)
    v := reflect.ValueOf(n)
    k := v.Kind()
    fmt.Println(k)
}


func main(){
    var stu Student
    Test(stu)
}

/*
main.Student
struct
能夠看出stu這個變量的類型是main包下的Student
而其類別是結構體
*/
類型和類別

固然咱們也能夠將reflect.Value類型逆轉成interface{}類型

reflect.Value.Interface() //轉換成interface{}類型

其轉換關係爲

變量 <------> interface{} <------> reflect.Value

獲取變量的值

reflect.ValueOf(x).Float()
reflect.ValueOf(x).Int()
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()

經過反射來改變變量的值

type Student struct{
    Name string
    Age int
    Score float32
}

func Test(n interface{}){
    v := reflect.ValueOf(n)
    //注意v是指針類型,因此須要經過Elem()轉換,其本質和*v同樣
    v.Elem().SetInt(99)    
}


func main(){
    b := 100
    //注意得傳指針類型,若是傳值類型會panic
    Test(&b)
    fmt.Println(b)
}
View Code

用反射操做結構體

reflect.Value.NumField() 獲取結構體中字段的個數
reflect.Value.NumMethod() 獲取結構體方法的個數
reflect.Value.Method(n).Call 來調用結構體中的方法

package main

import (
    "fmt"
    "reflect"
)

type Student struct{
    Name string
    Age int
    Score float32
}

func (this Student) Print(){
    fmt.Println(this)
}


func Test(p interface{}){
    val := reflect.ValueOf(p)
    if val.Kind() != reflect.Struct{
        fmt.Println("expect struct")
        return
    }
    fieldNum := val.NumField()
    methodNum := val.NumMethod()
    fmt.Println(fieldNum, methodNum)
    //調用第幾個方法
    val.Method(0).Call(nil)
}


func main(){
    stu := Student{
        Name: "stu100",
        Age: 18,
        Score: 99.5,
    }

    Test(stu)
}
View Code

接口實例

實現一個負載均衡調度算法,支持隨機、輪訓等,思考如何實現如內置sort.Sort()同樣,支持第三方擴展的需求

//balance.go
/*
定義一個負載均衡算法接口,該接口接收一個包含主機實例的切片,經過算法返回一個主機實例
*/
package balance

type Balancer interface{
    DoBalance([] *Instance) (*Instance, error)
}


//instance.go
/*主機實例*/
package balance

import "strconv"

type Instance struct{
    host string
    port int
}

//工廠函數
func NewInstance(host string, port int) *Instance{
    return &Instance{
        host: host,
        port: port,
    }
}

func (this *Instance) GetHost() string{
    return this.host
}

func (this *Instance) GetPort() int{
    return this.port
}

func (this *Instance) String() string{
    return this.host + ":" + strconv.Itoa(this.port)
}


//random.go
/*
咱們寫一個包內置的隨機算法,經過init函數進行初始化註冊,該算法實現了負載均衡算法接口
*/
package balance

import (
    "errors"
    "math/rand"
)

type RandomBalance struct{

}

func init(){
    RegisterBalance("random", &RandomBalance{})
}

func (this *RandomBalance)  DoBalance(insts [] *Instance) (inst *Instance,err error){
    lens := len(insts)

    if lens == 0{
        err = errors.New("no instance")
        return
    }
    index := rand.Intn(lens)
    inst = insts[index]
    return
}


//mgr.go
/*
一個註冊中心,擁有註冊過的算法(不管是咱們內置的仍是用戶擴展的),
*/
package balance

import "fmt"

type BalanceMgr struct{
    allBalancer map[string]Balancer
}

var mgr = BalanceMgr{
    allBalancer: make(map[string]Balancer),
}

func (this *BalanceMgr) registerBalance(name string,b Balancer){
      this.allBalancer[name] = b
}

func RegisterBalance(name string,b Balancer){
    mgr.registerBalance(name, b)
}

func DoBalance(name string, insts []*Instance) (inst *Instance, err error){
    balancer, ok := mgr.allBalancer[name]
    if !ok{
        err = fmt.Errorf("Not found %s banlancer", name)
        return
    }
    inst, err = balancer.DoBalance(insts)
    return
}
balance包
//poll.go
/*
看成用戶擴展的算法,實現負載均衡算法接口,並在註冊中心註冊
*/
package main

import (
    "go_dev/day06/t9/balance"
    "errors"
)

type PollBalance struct{
    ctn int
}

func init(){
    balance.RegisterBalance("poll", &PollBalance{})
}


func (this *PollBalance) DoBalance(insts [] *balance.Instance) (inst *balance.Instance,err error) {
    lens := len(insts)
    if lens == 0{
        err = errors.New("No instance")
        return
    }
    this.ctn = (this.ctn + 1) % lens
    inst = insts[this.ctn]
    return
}


//main.go
package main

import (
    "time"
    "fmt"
    "math/rand"
    "go_dev/day06/t9/balance"
    "os"
)

func main()  {
    var insts []*balance.Instance
    for i := 0; i < 16; i++{
        host := fmt.Sprintf("192.168.%d.%d",rand.Intn(255),rand.Intn(255))
        one := balance.NewInstance(host, 8080    )
        insts = append(insts, one)
    }
        //能夠寫到配置文件中
    var balanceName = "random"
    if len(os.Args) > 1{
        balanceName = os.Args[1]
    }

    for {
        inst, err := balance.DoBalance(balanceName, insts)
        if err != nil{
            fmt.Println(err)
            continue
        }
        fmt.Println(inst)
        time.Sleep(time.Second)
    }
   }
main包
相關文章
相關標籤/搜索