2016-08-23 43 views
16

Golang'da, alıcı yöntemleriyle yapıları kullanıyoruz. her şey burada mükemmeldir.
Ancak, hangi arabirimlerin olduğundan emin değilim. Yapılardaki yöntemleri tanımlarız ve bir yapıda bir metot uygulamak istiyorsak, yine başka bir yapıda tekrar yazarız.
Bu, arayüzlerin sadece metod tanımları olarak göründüğü ve sayfamızdaki gereksiz yere fazla yer olmadığı anlamına gelir.Golang'da neden arayüz gerekli?

Neden bir arabirime ihtiyacım olduğunu açıklayan bir örnek var mı?

+0

bilinmeyen yapı JSON unzshall nasıl olacak? ya da orada olmasa fmt.Printf nasıl çalışır? – YOU

+0

iyi çalışmıyor, eğer orada değilse, nasıl çalışacağını kastediyorsunuz? fmt – nikoss

+0

'dan dışa aktarılır. [Git: Arayüzün anlamı nedir?} (http://stackoverflow.com/questions/23148812/go-whats-the-meaning-of-interface) – molivier

cevap

30

Arabirimler, bu konuyla ilgili derinlemesine bir yanıt vermek için çok büyük bir konudur, ancak bazı şeyleri kullanımlarını açık hale getirmektedir.

Arabirimler, aracı aracıdır. Bunları kullanmak ya da kullanmamak size kalmış, ancak kodları daha açık hale getirebilir ve paketler ya da istemciler (kullanıcılar) ve sunucular (sağlayıcılar) arasında iyi bir API sağlayabilirler.

Evet, kendi struct türü oluşturabilir, ve örneğin, kendisine yöntemleri "attach" olabilir: Biz zaten yukarıdaki kodda tekrar geçmeye görebilirsiniz

type Cat struct{} 

func (c Cat) Say() string { return "meow" } 

type Dog struct{} 

func (d Dog) Say() string { return "woof" } 

func main() { 
    c := Cat{} 
    fmt.Println("Cat says:", c.Say()) 
    d := Dog{} 
    fmt.Println("Dog says:", d.Say()) 
} 

: Her iki Cat yaparken ve Dog bir şey söyle. Hayvan ile aynı türde varlık olarak ele alabilir miyiz?Pek sayılmaz. Her ikisini de interface{} olarak ele alabildiğimizden emin olabiliriz, ancak bunu yaparsak,türünde bir değer tanımlayamadığı için Say() yöntemini kullanamıyoruz. Yukarıdaki iki türde de benzeri var: Say() aynı imzaya sahip (parametre ve sonuç türleri). Biz bir arayüz ile bu yakalayabilir:

type Sayer interface { 
    Say() string 
} 

arayüzü onların uygulanmasını sadece imza yöntemlerden içerir, ancak değil.

Git yönteminde, türünde bir türünün, arabirimi, arabirimin bir üst kümesi ise, bir arabirimi uygulayacağını unutmayın. Niyet beyanı yoktur. Ne anlama geliyor? Önceki Cat ve Dog türlerimiz bu Sayer ara yüzünü zaten uygulamış olsalar bile, bu arayüz tanımı daha önce yazdığımızda bile mevcut değildi ve bunları işaretlemek için bunlara dokunmadık. Sadece yaparlar.

Arabirimler davranışı olarak belirtir. Bir arabirimi uygulayan bir tür, bu türün, arabirimin "reçete ettiği" tüm yöntemlere sahip olduğu anlamına gelir. Her ikisi de Sayer uygulandığından beri, her ikisi de Sayer değeri olarak ele alabilir, bunların ortak noktaları vardır. birlik içinde hem işleyebilir Bakın nasıl:

animals := []Sayer{c, d} 
for _, a := range animals { 
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say()) 
} 

(bölümünü yansıtan tür adı almak için tek olduğunu, şu andan itibaren çok onun yapmazlar.)

önemli bir parçasıdır Cat ve Dog'u aynı tür (bir arabirim türü) olarak ele alabildiğimiz ve onlarla çalışabileceğimiz/kullanabileceğimiz. Eğer bir Say() yöntemle ek türleri oluşturmak için hızlı bir şekilde olsaydı, bunlar Cat ve Dog yanında hizaya olabilir:

type Horse struct{} 

func (h Horse) Say() string { return "neigh" } 

animals = append(animals, Horse{}) 
for _, a := range animals { 
    fmt.Println(reflect.TypeOf(a).Name(), "says:", a.Say()) 
} 

Diyelim ki bu tür ile çalışır diğer kod yazmak istediğinizi varsayalım. Bir yardımcı işlevi:

func MakeCatTalk(c Cat) { 
    fmt.Println("Cat says:", c.Say()) 
} 

Evet, yukarıdaki fonksiyon Cat ile ve başka bir şey ile çalışır. Benzer bir şey istiyorsanız, her tür için yazmanız gerekir. Bunun ne kadar kötü olduğunu söylemeye gerek yok.

Evet, interface{} bir argüman almak ve yardımcı fonksiyonların sayısını azaltmak, ama yine de gerçekten çirkin görünüyor ediyorum type assertion veya type switches kullanımı yazabilirim.

Çözüm? Evet, arayüzler. Basitçe onunla yapmak istiyorum davranışı tanımlayan bir arayüz türünde bir değer almak için işlev bildirir ve hepsi bu:

func MakeTalk(s Sayer) { 
    fmt.Println(reflect.TypeOf(s).Name(), "says:", s.Say()) 
} 

Sen Cat değeri ile Dog, Horse veya herhangi Bu fonksiyonu çağırabilirsiniz diğer tür 'şimdiye kadar bilmiyorum, bu bir Say() yöntemine sahip. Güzel.

Bu örnekleri, Go Playground adresinde deneyin.

+0

Hala neden sadece at ya da kedi ya da köpek içine hayvan gömmek değil anlamıyorum anlayamıyorum – nikoss

+0

@nikoss Ne demek istediğini anlıyorum, nasıl bir dilim içine eklemek ve tekrarlamak ve aramak() Her birinde, örneğin. – icza

+0

ah şu anda farklı yapıları birleştiren nokta şu ki hala biraz karmaşık olsa da aklımda netleşemiyorum – nikoss

2

ben git de, burada arayüzleri iki ilginç kullanım durumları gösterir: Bu ilginç büyü yapabilir, bu iki basit arayüzleri kullanarak

type Reader interface { 
    Read(p []byte) (n int, err error) 
} 

type Writer interface { 
    Write(p []byte) (n int, err error) 
} 

:

1- Bu iki basit arayüzleri bakın

package main 

import (
    "bufio" 
    "bytes" 
    "fmt" 
    "io" 
    "os" 
    "strings" 
) 

func main() { 
    file, err := os.Create("log.txt") 
    if err != nil { 
     panic(err) 
    } 
    defer file.Close() 

    w := io.MultiWriter(file, os.Stdout) 
    r := strings.NewReader("You'll see this string twice!!\n") 
    io.Copy(w, r) 

    slice := []byte{33, 34, 35, 36, 37, 38, 39, 10, 13} 
    io.Copy(w, bytes.NewReader(slice)) // !"#$%&' 

    buf := &bytes.Buffer{} 
    io.Copy(buf, bytes.NewReader(slice)) 
    fmt.Println(buf.Bytes()) // [33 34 35 36 37 38 39 10 13] 

    _, err = file.Seek(0, 0) 
    if err != nil { 
     panic(err) 
    } 

    r = strings.NewReader("Hello\nWorld\nThis\nis\nVery\nnice\nInterfacing.\n") 
    rdr := io.MultiReader(r, file) 
    scanner := bufio.NewScanner(rdr) 
    for scanner.Scan() { 
     fmt.Println(scanner.Text()) 
    } 
} 

Çıktı:

You'll see this string twice!! 
!"#$%&' 

[33 34 35 36 37 38 39 10 13] 
Hello 
World 
This 
is 
Very 
nice 
Interfacing. 
You'll see this string twice!! 
!"#$%&' 

Bu kod yeterince açık umut:
strings.NewReader kullanarak dizesinden okur ve sadece io.Copy(w, r) ile io.MultiWriter kullanılarak file ve os.Stdout hem eşzamanlı yazar. Sonra bytes.NewReader(slice) kullanarak dilimden okur ve aynı anda hem file hem de os.Stdout için yazar. Daha sonra io.Copy(buf, bytes.NewReader(slice)) numaralı arabelleğe kopyalayın ve ardından file.Seek(0, 0) kullanarak dosya kaynağına gidin ve sonra strings.NewReader kullanarak dizeden okuma yapın, ardından file ve bufio.NewScanner öğelerini kullanarak okumaya devam edin ve fmt.Println(scanner.Text()) kullanarak tümünü yazdırın.


2- Ve bu arayüzün başka ilginç kullanımıdır:

package main 

import "fmt" 

func main() { 
    i := show() 
    fmt.Println(i) // 0 

    i = show(1, 2, "AB", 'c', 'd', []int{1, 2, 3}, [...]int{1, 2}) 
    fmt.Println(i) // 7 

} 
func show(a ...interface{}) (count int) { 
    for _, b := range a { 
     if v, ok := b.(int); ok { 
      fmt.Println("int: ", v) 
     } 
    } 
    return len(a) 
} 

çıkışı:

0 
int: 1 
int: 2 
7 

Ve güzel bir örnek görmek için: Explain Type Assertions in Go

Ayrıca bakınız : Go: What's the meaning of interface{}?

+0

@nikoss Umarım bu yardımcı olur. –

1

arabirimi, bazı jenerik türleri sağlar. Ördek yazmayı düşün.

type Reader interface{ 
    Read() 
} 

func callRead(r Reader){ 
     r.Read() 
} 

type A struct{ 
} 
func(_ A)Read(){ 
} 

type B struct{ 
} 
func(_ B)Read(){ 
} 

O Okuyucu arabirimini uygulamak hem çünkü callRead için yapı A ve B geçmek Tamam. Ancak, arabirim yoksa A ve B için iki işlev yazmalıyız.

func callRead(a A){ 
    a.Read() 
} 

func callRead2(b B){ 
    b.Read() 
}