413 lines
13 KiB
Markdown
413 lines
13 KiB
Markdown
# 2.6 Interface
|
||
|
||
## Interface (Arabirim)
|
||
|
||
Go' nun göze çarpan özelliklerinden biri interfacelerdir. Türkçeye arabirim diyerek de çevrilebilir. Bu bölümü okuduktan sonra, interfacelerin kullanımı muhtemelen anlamış olacaksınız.
|
||
|
||
### Interface nedir?
|
||
|
||
Interface bir grup methodun özelliklerini, yapması gerekenleri tanımlamak için kullandığımız bir sooyutlama yöntemidir. Nesnelere uygulanacak kontrat ya da şartad olarak düşünebiliriz.
|
||
|
||
Önceki bölümlerdeki gibi, Ogrenci hem de Calisan `Merhaba()` yapabilir, ancak aynı şeyi yapmış olmazlar.
|
||
|
||
Ogrenciye `BorcPara()` , Calisana ise `MaasHarca () ` ve `SarkiSoyle ()` yöntemi ekleyeceğiz.
|
||
|
||
Şimdi, Ogrenci'nin `Merhaba ()`,`SarkiSoyle () `ve` BorcPara () `olarak üç methodu, Calisan'ın ise `Merhaba () `,`SarkiSoyle () `ve` MaasHarca () ` methodları vardır.
|
||
|
||
Bu yöntem topluluğuna interface(arairim) denir ve hem Ogrenci hem de Calisan tarafından uygulanır. Böylece, Ogrenci ve Calisan `Merhaba ()` ve `SarkiSoyle ()` interfacelerini uygulamış olur.
|
||
Bununla birlikte, Calisan `BorcPara ()`, Ogrenci ise `MaasHarca ()` arayüzünü uygulamıyor. Bunun nedeni Calisan'ın `BorcPara ()` , Ogrenci'nin ise `MaasHarca ()` yöntemine sahip olmamasıdır.
|
||
|
||
### Interface Type'ları (türleri)
|
||
|
||
Interface'ler aslında bir dizi methodtur yani eğer type, tüm interface methodlarını uygularsa biz ona interface'i implemente ediyor(uyguluyor) deriz.
|
||
|
||
```Go
|
||
type Insan struct {
|
||
ad string
|
||
yas int
|
||
telefon string
|
||
}
|
||
|
||
type Ogrenci struct {
|
||
Insan
|
||
okul string
|
||
kredi float32
|
||
}
|
||
|
||
type Calisan struct {
|
||
Insan
|
||
sirket string
|
||
para float32
|
||
}
|
||
|
||
// define interfaces
|
||
type Adamlar interface {
|
||
Merhaba()
|
||
SarkiSoyle(sarkiSozu string)
|
||
HunharcaYemek(beerStein string)
|
||
}
|
||
|
||
type Delikanli interface {
|
||
Merhaba()
|
||
SarkiSoyle(song string)
|
||
BorcPara(amount float32)
|
||
}
|
||
|
||
type IhtiyarDelikanli interface {
|
||
Merhaba()
|
||
SarkiSoyle(song string)
|
||
MaasHarca(amount float32)
|
||
}
|
||
|
||
func (i *Insan) Merhaba() {
|
||
fmt.Printf("Selam, Ben %s beni şöyle çağırabilirsin %s\n", h.ad, h.telefon)
|
||
}
|
||
|
||
func (i *Insan) SarkiSoyle(sarkiSozu string) {
|
||
fmt.Println("La la, la la la, la la la la la...", sarkiSozu)
|
||
}
|
||
|
||
func (i *Insan) HunharcaYemek(beerStein string) {
|
||
fmt.Println("Yumm yumm yummm hunharca tüketiyorum...", beerStein)
|
||
}
|
||
|
||
// Calisan overloads Merhaba
|
||
func (e *Calisan) Merhaba() {
|
||
fmt.Printf("Selam, ben %s, %s 'de calısıyorum. Beni söyle cağırabilirsin %s\n", e.ad,
|
||
e.sirket, e.telefon)
|
||
}
|
||
|
||
func (s *Ogrenci) BorcPara(amount float32) {
|
||
s.kredi += amount
|
||
}
|
||
|
||
func (e *Calisan) MaasHarca(amount float32) {
|
||
e.para -= amount
|
||
}
|
||
```
|
||
Artık interface'in herhangi bir type(tür) tarafından uygulanabileceğini ve bir type'ın aynı anda birçok interface'i uygulayabileceğini biliyoruz.
|
||
|
||
Herhangi bir type'ın boş `interface {}` uyguladığını unutmayın, çünkü herhangi bir methodu yoktur ve tüm typeların varsayılan olarak sıfır methodu vardır.
|
||
|
||
### Interface degeri
|
||
|
||
Peki interface' e ne tür değerler koyulabilir? Bir değişkeni, type interface olarak tanımlarsak, interface'i uygulayan herhangi bir type bu değişkene atanabilir.
|
||
|
||
Aşağıdaki örnekte olduğu gibi, Adamlar' a interface olarak bir "m" değişkeni tanımlarsak, Ogrenci, Insan veya Calisan'dan herhangi biri "m" değişkenine atanabilir. Ve elimizde Adamlar' dan bir parça(slice) olabilir. Ve Adamlar' ı implement eden(uygulayan) herhangi bir type'dan bir parça' da bu değişkene atanabilir.
|
||
|
||
|
||
```Go
|
||
package main
|
||
|
||
import "fmt"
|
||
|
||
type Insan struct {
|
||
ad string
|
||
yas int
|
||
telefon string
|
||
}
|
||
|
||
type Ogrenci struct {
|
||
Insan
|
||
okul string
|
||
kredi float32
|
||
}
|
||
|
||
type Calisan struct {
|
||
Insan
|
||
sirket string
|
||
para float32
|
||
}
|
||
|
||
// Interface Adamlar, Insan'dan implemente edildi.
|
||
type Adamlar interface {
|
||
Merhaba()
|
||
SarkiSoyle(sarkiSozu string)
|
||
}
|
||
|
||
// method
|
||
func (h Insan) Merhaba() {
|
||
fmt.Printf("Selam, Ben %s, Beni şu numaradan arayabilrsin: %s\n", h.ad, h.telefon)
|
||
}
|
||
|
||
// method
|
||
func (h Insan) SarkiSoyle(sarkiSozu string) {
|
||
fmt.Println("La la la la...", sarkiSozu)
|
||
}
|
||
|
||
// method
|
||
func (e Calisan) Merhaba() {
|
||
fmt.Printf("Selam, ben %s, %s ' da çalışıyorum. Beni şu numaradan arayabilrsin: %s\n", e.ad,
|
||
e.sirket, e.telefon) //Evet, iki satıra ayırabiliriz.
|
||
}
|
||
|
||
func main() {
|
||
ali := Ogrenci{Insan{"Ali", 20, "222-222-XXX"}, "ODTU", 0.00}
|
||
ahmet := Ogrenci{Insan{"Ahmet", 21, "111-222-XXX"}, "ITU", 100}
|
||
mehmet := Calisan{Insan{"Mehmet", 36, "444-222-XXX"}, "Golang Inc.", 1000}
|
||
veli := Calisan{Insan{"Veli", 36, "444-222-XXX"}, "Things Ltd.", 5000}
|
||
|
||
// i interface'ini tanımlayalım.
|
||
var i Adamlar
|
||
|
||
//Ogrenci' yi tutabilirm
|
||
i = ali
|
||
fmt.Println("Öğrenci the Ali basliyor :D :")
|
||
i.Merhaba()
|
||
i.SarkiSoyle("November rain")
|
||
|
||
//Calisan ' tutabilirim.
|
||
i = veli
|
||
fmt.Println("Veli bir Calisan:")
|
||
i.Merhaba()
|
||
i.SarkiSoyle("Uykusuz her gece, yorgun ölesiye!")
|
||
|
||
// Adamlar 'dan parçalar/dilimler(slice)
|
||
fmt.Println("Adamlar' ı dilimlere ayıralım ve n'olacağını görelim.")
|
||
x := make([]Adamlar, 3)
|
||
// Bu üç element farklı türdeler fakat hepsi Adamlar interface'ini uyguluyor/implement ediyor.
|
||
x[0], x[1], x[2] = ali, ahmet, mehmet
|
||
|
||
for _, value := range x {
|
||
value.Merhaba()
|
||
}
|
||
}
|
||
```
|
||
Interface kendini implement edemez.
|
||
|
||
### Boş interface
|
||
|
||
Boş interface, herhangi bir method içermeyen bir interface'dir. Bu nedenle tüm type'lar boş bir interface uygular. Bu durum, tüm türleri tek noktada saklamak istediğimizde çok kullanışlıdır ve C'deki void * 'e benzer.
|
||
```Go
|
||
// boş interface uygulaması
|
||
var bos interface{}
|
||
|
||
// degiskenler
|
||
i := 5
|
||
s := "Selam dünya"
|
||
|
||
// herhangi bir türden veri tutabilir.
|
||
bos = i
|
||
bos = s
|
||
```
|
||
|
||
Eğer bir fonskiyon değişken türü olarak boş interface kullanıyorsa, herhangi bir türü kabul edebilir; bir fonksiyon dönüş değeri türü olarak boş interface kullanıyorsa, herhangi bir türü döndürebilir.
|
||
|
||
### Interface'in method argümanları
|
||
|
||
Herhangi bir değişken bir arayüzde kullanılabilir. Peki bu özelliği herhangi bir değişkeni bir fonksiyona geçirmek için nasıl kullanabiliriz?
|
||
|
||
Örneğin, fmt.Println'i çok kullanıyoruz, ancak herhangi bir argümanı kabul edebileceğini hiç fark ettiniz mi? "Fmt" nin açık kaynak koduna baktığımızda, aşağıdaki tanımı görüyoruz.
|
||
|
||
```Go
|
||
type Stringer interface {
|
||
String() string
|
||
}
|
||
```
|
||
Bu, Stringer interface'ini uygulayan herhangi bir türün bağımsız değişken olarak fmt.Println'ye geçirilebileceği anlamına gelir. Kanıtlayalım.
|
||
```Go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
)
|
||
|
||
type Insan struct {
|
||
ad string
|
||
yas int
|
||
telefon string
|
||
}
|
||
|
||
// Insan implements fmt.Stringer
|
||
func (h Insan) String() string {
|
||
return "ad:" + h.ad + ", yas:" + strconv.Itoa(h.yas) + ", Contact:" + h.telefon
|
||
}
|
||
|
||
func main() {
|
||
isik := Insan{"Isik", 25, "000-7777-XXX"}
|
||
fmt.Println("Bu kişi: ", isik)
|
||
}
|
||
```
|
||
|
||
Asagıdaki box örneğine baktığımızda, Color'ın Stringer interface'ini de uyguladığını göreceksiniz, böylece yazdırma biçimini özelleştirebiliyoruz. Eğer ki bu interface'i uygulamazsak, fmt.Println türü varsayılan biçimiyle yazdırır.
|
||
```Go
|
||
fmt.Println("En büyük", boxes.BiggestsColor().String())
|
||
fmt.Println("En büyük", boxes.BiggestsColor())
|
||
```
|
||
Uyarı: Eğer type ` error interface'ini uyguladıysa, fmt `Error () ' olarak adlandırır, bu nedenle bu noktada Stringer uygulamasına gerek kalmaz.
|
||
|
||
### Interface' in değişken türü
|
||
|
||
Eğer değişken, interface'i uygulayan type'sa aynı interface'i uygulayan başka herhangi bir type'ın bu değişkene atanabileceğini biliyoruz. Soru, interface'de saklanan belirli türü nasıl bilebiliriz?
|
||
Size göstereceğim iki yol var.
|
||
|
||
- Comma-ok pattern
|
||
|
||
Go syntax'ına uygun olarak şöyle bir şey yapılabilir `deger, ok := element.(T)`.
|
||
Değişkenin beklediğimiz tür olup olmadığını kontrol eder; burada "değer", değişkenin değeri, "ok", boolean türünden bir değişkeni, "element" interface değişkeni ve T, yerleştirmenin türüdür .
|
||
|
||
Element beklediğimiz türse, "ok" true, değilse false olur.
|
||
|
||
Daha iyi anlaşılması için biraz daha örnek verelim.
|
||
```Go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
)
|
||
|
||
type Element interface{}
|
||
type List []Element
|
||
|
||
type Person struct {
|
||
ad string
|
||
yas int
|
||
}
|
||
|
||
func (p Person) String() string {
|
||
return "(ad: " + p.ad + " - yas: " + strconv.Itoa(p.yas) + " years)"
|
||
}
|
||
|
||
func main() {
|
||
list := make(List, 3)
|
||
list[0] = 1 // an int
|
||
list[1] = "Hello" // a string
|
||
list[2] = Person{"Dennis", 70}
|
||
|
||
for index, element := range list {
|
||
if value, ok := element.(int); ok {
|
||
fmt.Printf("list[%d] 'in türü int and degeri: %d\n", index, value)
|
||
} else if value, ok := element.(string); ok {
|
||
fmt.Printf("list[%d] 'in string int and degeri: %s\n", index, value)
|
||
} else if value, ok := element.(Person); ok {
|
||
fmt.Printf("list[%d] 'in türü Person and degeri: %s\n", index, value)
|
||
} else {
|
||
fmt.Printf("list[%d] 'in bambaşka bir türü var... \n", index)
|
||
}
|
||
}
|
||
}
|
||
|
||
```
|
||
Bu kalıbı/pattern'i kullanmak oldukça kolaydır, ancak test etmek için birçok türümüz varsa, `switch` i kullansak iyi olur.
|
||
|
||
- switch test
|
||
|
||
Hadi `switch` kullanarak üstteki örneği yeniden yazalım.
|
||
```Go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
)
|
||
|
||
type Element interface{}
|
||
type List []Element
|
||
|
||
type Person struct {
|
||
ad string
|
||
yas int
|
||
}
|
||
|
||
func (p Person) String() string {
|
||
return "(ad: " + p.ad + " - yas: " + strconv.Itoa(p.yas) + " years)"
|
||
}
|
||
|
||
func main() {
|
||
list := make(List, 3)
|
||
list[0] = 1 //an int
|
||
list[1] = "Selam" //a string
|
||
list[2] = Person{"isik", 25}
|
||
|
||
for index, element := range list {
|
||
switch value := element.(type) {
|
||
case int:
|
||
fmt.Printf("list[%d] bir int and ve değeri %d\n", index, value)
|
||
case string:
|
||
fmt.Printf("list[%d] bir string and ve değeri %s\n", index, value)
|
||
case Person:
|
||
fmt.Printf("list[%d] bir Person and ve değeri %s\n", index, value)
|
||
default:
|
||
fmt.Println("list[%d] bambaşka bir türe(tyoe) sahip", index)
|
||
}
|
||
}
|
||
}
|
||
|
||
```
|
||
|
||
Unutmamalıyız ki `element. (Type)`, `switch` gövdesinin dışında kullanılamaz, bu durumda`virgül-ok` desenini kullanmanız gerektiği anlamına gelir.
|
||
### Embedded interfaces
|
||
|
||
Go' daki güzel şeylerden biri de structlardaki anonim alanlar gibi birçok yerleşik mantık sözdizimine sahip olmasıdır. Şaşırtıcı olmayan bir şekilde, interfaceleri anonim alanlar olarak da kullanabiliriz, ancak onlara `Gömülü(embedded) interface` diyoruz. Burada anonim alanlarla aynı kuralları uygularız. Daha spesifik olarak, bir interface'in içinde gömülü başka bir interface varsa, gömülü interface'in sahip olduğu tüm yöntemlere sahipmiş gibi davranacaktır.
|
||
|
||
`Container / heap` içindeki kaynak kodların aşağıdaki gibi olduğunu görebiliriz:
|
||
```Go
|
||
type Interface interface {
|
||
sort.Interface // embedded sort.Interface
|
||
Push(x interface{}) //a Push method to push elements into the heap
|
||
Pop() interface{} //a Pop method that pops elements from the heap
|
||
}
|
||
```
|
||
`Sort.Interface` öğesinin gömülü bir interface olduğunu görüyoruz, bu nedenle yukarıdaki Interface'de kapalı olarak "sort.Interface" içinde bulunan üç yöntem vardır.
|
||
|
||
```Go
|
||
type Interface interface {
|
||
Len() int
|
||
Less(i, j int) bool
|
||
Swap(i, j int)
|
||
}
|
||
```
|
||
Başka bir örneği de `io` paketi içinde bulunan`io.ReadWriter` 'de görebiliriz.
|
||
```Go
|
||
// io.ReadWriter
|
||
type ReadWriter interface {
|
||
Reader
|
||
Writer
|
||
}
|
||
```
|
||
### Reflection(Yansıma)
|
||
|
||
Go'daki yansıma, çalışma zamanında bilgileri belirlemek için kullanılır. `reflect` paketini, [Yansıma kuralları](http://golang.org/doc/articles/laws_of_reflection.html) yansımanın nasıl calıstıgına baktıktan sonra göreceğiz.
|
||
|
||
Yansıtmayı kullanırken üç adım söz konusudur. İlk olarak, bir interface'i türleri yansıtacak şekilde dönüştürmeliyiz (reflect.Type veya reflect.Value bu duruma bağlıdır).
|
||
|
||
```Go
|
||
t := reflect.TypeOf(i) // i'nin içindeki meta veriyi al ve tüm öğeleri almak için t'yi kullan
|
||
v := reflect.ValueOf(i) // type i 'nin içindeki gerçek değeri al,ve v' yi değiştirmek için değerini kullan.
|
||
```
|
||
Bundan sonra, ihtiyaç duyduğumuz değerleri elde etmek için yansıyan türleri dönüştürebiliriz.
|
||
|
||
```Go
|
||
var x float64 = 3.4
|
||
|
||
t := reflect.TypeOf(x)
|
||
v := reflect.ValueOf(x)
|
||
|
||
fmt.Println("type:", t)
|
||
fmt.Println("value:", v)
|
||
fmt.Println("kind float64 mu?:", v.Kind() == reflect.Float64)
|
||
```
|
||
Son olarak, yansıyan türlerin değerlerini değiştirmek istiyorsak, onu değiştirilebilir yapmamız gerekir. Daha önce tartışıldığı gibi, değere göre geçiş ile referansa göre geçiş arasında bir fark vardır. Aşağıdaki kod derlenmeyecektir.
|
||
```Go
|
||
var x float64 = 3.4
|
||
v := reflect.ValueOf(x)
|
||
v.SetFloat(7.1)
|
||
```
|
||
Bunun yerine, yansıma türlerinden değerleri değiştirmek için aşağıdaki kodu kullanmalıyız.
|
||
|
||
```Go
|
||
var x float64 = 3.4
|
||
p := reflect.ValueOf(&x)
|
||
v := p.Elem()
|
||
v.SetFloat(7.1)
|
||
```
|
||
Yansımanın temellerini az önce tartıştık, ancak daha fazlasını anlamak için daha fazla pratik yapmalısınız.
|
||
|
||
## Links
|
||
|
||
- [Directory](preface.md)
|
||
- Previous section: [Nesne-Yönelim](02.5.md)
|
||
- Next section: [Eş-zamanlılık](02.7.md)
|