Skip to content

Latest commit

 

History

History
215 lines (149 loc) · 5.72 KB

File metadata and controls

215 lines (149 loc) · 5.72 KB

Interface

Interfaceler, bizim structlarla ya da packagelarla iletişime geçmemizi sağlayacak fonksiyonlardır ve aslında bütün yapılar interfacelerle iletişime geçiyor.

Interface, gerçek hayattan bir örnek olarak asansördeki tuş takımına benzetilebilir. Biz istediğimiz tuşa basıyoruz ve aslında onun arka planındaki işlemlerle uğraşmadan tuş üzerinden işimizi hallediyoruz.

Bir önceki derste person.go adında bir dosyamız vardı ve package ismi models idi. Şimdi bu package altında bir interface tanımlayacağız.

package models

type Identify interface{
    Id() int64
}

Bu şekilde Identify adında bir interface oluşturmuş olduk ve bu interface Id adlı bir fonksiyon barındırıyor.

Ardından person'ın id'sini dönen Id fonksiyonumuzu oluşturuyoruz.

func (p *Person) Id() int64{
    return p.id
}

Interfacelere neden ihtiyacımız olsun ki gibi bir soru aklımıza gelebilir fakat bu özellikle kod kalabalığı arttığı zaman önemini gösterecektir.

Interface aslında Go'da dile metamorphism katması için üretilmiş bir yapıdır. Interfaceleri bu şekilde blueprint olarak kullanabileceğimiz gibi aynı zamanda değişken tanımlarken de kullanabiliyoruz. Bu değişkenler runtime'da type casting yani çalışma sırasında tip dönüşümü yapmamızı sağlıyor.

Örneğin bir kaynaktan json veri okuyoruz ve unmarshal ediyoruz. Json içerisindeki veri tipleri integer olabilir, string olabilir, object olabilir. Bu değişkeni interface olarak tanımlamamız runtime'da gelen verinin tipini kontrol etmemize olanak sağlıyor

Interface Örneği

Bu örnekte interface'i tam anlamıyla kullanmış olmayacağız. Interface farklı packagelar ile çalışıldığında değer kazanmakta ve package konusunda ilerleyen derslerde daha detaylı değinilecek ve interfaceler de o noktada daha iyi anlaşılacaktır.

Şimdi area.go isimli bir dosya oluşturalım.

package main

import "math"

type Circle struct{
    Radius float64
}

func (c *Circle) Area() float64{
    return math.Pi * math.Pow(c.Radius, 2)
}

type Square struct{
    Width	float64
    Height	float64
}

func (s *Square) Area() float64{
    return s.Width * s.Height
}

type Sizer interface{
    Area() float64
}

func main(){
    c := Circle{Radius: 10}
    fmt.Println(c)
    
    s := Square{Width: 10, Height: 8}
    fmt.Println(s)
    
    if c.Area() < s.Area(){
        println("Circle less")
    }else{
        fmt.Println(c.Area)
    }
}

Burada circle ve square isimli iki struct oluşturduk ve bu structları referans alan iki adet Area fonksiyonu oluşturduk. Aynı zamanda Sizer isimli bir interface oluşturduk fakat aynı package içerisinde olduğumuz için kullanmadık. Eğer interface kullansaydık, örneğin area fonksiyonlarının float64 dönmesi noktasında limit çizebilirdik.

Ayrıca parametre olarak interface alan bir fonksiyon yazabilirdik. Örneğin,

func Eq(s1, s2 Sizer){
    if s1.Area() < s2.Area(){
        println("Circle less")
    }else{
        println("Square less")
    }
}

Bu konulara ilerleyen derslerde daha detaylı değinilecek.

NOT: Çok performans gerektiren durumlarda interface'den kaçınılır. Çünkü arka planda reflection denen bir yapı kullanır.

Branching - If/Else

If else genel yazılım dilleriyle aynı mantığa sahp.

Yukarıdaki örneği alalım ve bir şart daha ekleyelim,

if s1.Area() < s2.Area(){
    println("Circle less")
} else if s2.Area() > s1.Area(){
    println("Square less")
} else{
    println("Equal")
}

Burada hangi şart durumu karşılarsa o şartın altındaki işlem uygulanacak, diğer işlemler uygulanmayacak. Eğer hiçbir şart karşılamazsa else kısmında belirtilen işlem uygulanacak.

Döngüler

Go'da farklı şekillerde for döngüsü yazabiliyoruz.

func main(){
    i := 0
    for i <= 3{
        i = i+1
    }
    
    for j := 7; j <= 10; j++{
        
    }
    
    for{
        
    }
}

Son for döngüsü bir sonsuz döngü oluşturuyor. Go'da while vs. yok. Dolayısıyla neredeyse her şeyi for, switch ile hallediyoruz.

For ile slice üzerinde dönerek elemanlarına ulaşabiliriz.

func main(){
    hocalar := []string{}
    
    hocalar = append(hocalar, "Ege")
    hocalar = append(hocalar, "Orhun")
    
    for i := 0; i < len(hocalar); i++{
        fmt.Println(hocalar[i])
    }
    
    for _, v := range hocalar{
        fmt.Println(v)
    }
}

Burada ilk for döngüsünde klasik mantıkla elemanlara ulaşıyoruz. İkinci for dögüsünde ise range ile ulaşıyoruz.

NOT: _ (alt çizgi), fonksiyondan kullanmak istemediğimiz şeyler dönmesin diye kullanılıyor. Modern dillerde bu kullanım yaygın.

Range aynı zamanda maplerde de kullanılabiliyor.

haritalar := map[string]int{}

haritalar["Ankara"] = 6
haritalar["Bolu"] = 14

for k, v := range haritalar{
    fmt.Printf("Key: %s, value: %d\n", k,v)
}

Eğer mapimizde herhangi bir key'in olup olmadığını kontrol etmek istersek,

if _, ok := haritalar["Bolu"]; ok{
    fmt.Println("Bolu cok guzel")
}

Switch-case

Switch'te bir input, caseler üzerinden kontrol edilir ve girdiği case altındaki işlemler yapılır. Eğer hiçbir case'e girmezse en sonda belirtilen default altındaki işlemi yapar. Bir case tamamlandıktan sonra diğer caselere de bakmasını istiyorsak case sonuna fallthrough yazabiliriz.

Aşağıdaki örnekte hem iota hem de switch case kullanımını görüyoruz.

package main

import "fmt"

const{
    OCAK = iota
    SUBAT
    MART
    NISAN
}

func main(){
    input = 0
    
    switch input{
	case OCAK:
        fmt.Println("Kış kapıdan baktırır")
    }
    fallthrough
    case SUBAT, NISAN:
    	fmt.Println("Kazma kürek yaktırır")
}