diff --git a/tr/02.6.md b/tr/02.6.md new file mode 100644 index 00000000..85aff9bc --- /dev/null +++ b/tr/02.6.md @@ -0,0 +1,412 @@ +# 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)