Skip to content

Latest commit

 

History

History
412 lines (321 loc) · 12.8 KB

File metadata and controls

412 lines (321 loc) · 12.8 KB

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 () veBorcPara ()olarak üç methodu, Calisan'ın ise Merhaba () ,SarkiSoyle () veMaasHarca () 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.

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.

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.

// 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.

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.

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.

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, fmtError () ' 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.

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.

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 durumdavirgü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:

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.

type Interface interface {
	Len() int
	Less(i, j int) bool
	Swap(i, j int)
}

Başka bir örneği de io paketi içinde bulunanio.ReadWriter 'de görebiliriz.

// 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ı 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).

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.

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.

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.

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