Skip to content

Latest commit

 

History

History
380 lines (260 loc) · 11.4 KB

File metadata and controls

380 lines (260 loc) · 11.4 KB

Giriş

Go Nedir?

Go basit, güvenilir ve verimli yazılımlar tasarlamamızı sağlayan açık kaynak bir yazılım dilidir. Google'daki bir takım ve birçok açık kaynak destekçisinin katılımıyla ortaya çıkmıştır.

Tasarlayanlar:

  • Rob Pike
  • Robert Griesemer
  • Ken Thompson

Go'nun Ortaya Çıkışı

Google ağırlıklı olarak C++, Java ve Python kullanırken zaman içerisinde her birinin farklı dezavantajlarının var olduğu görülmüş ve type-safe, hızlı derlenen ve yüksek performanslı bir dil ihtiyacı sonucunda ortaya çıkmıştır.

Aynı zamanda temelde task automation yani iş otomasyonu ihtiyacı da Go'nun çıkışında etkili olmuştur.

Go'yu Ön Plana Çıkaran Etkenler

  • Basit
  • Hızlı
  • Kolay geliştirme yapabilme
  • İstediğin gibi referans modellerini, value objelerini yönetebilme
  • Varsayılan olarak concurrent çalışması yani paralelleştirme bulundurması
  • Gelişmiş standart kütüphaneleri
  • Pointer kullanım kolaylığı
  • Kolay adapte olabilme
  • Garbage colection yapması
  • İçinde C kodu yazabilme

Araçlar

Editör: Visual Studio Code

Eklenti: Go (Go Team at Google)

Ortam ve Kurulum

https://golang.org/dl/ adresine giderek Go'nun işletim sisteminiz için uygun olan versiyonunu indirerek Go'yu kullanmaya başlayabilirsiniz.

Başlangıç

Bilgisayarınızda bir klasör açıp bu klasörü VsCode üzerinde açarak çalışmaya başlayabiliriz.(Klasör yapısı ilerleyen süreçte incelenecektir.)

Klasörümüz içinde örneğin main.go isimli bir file oluşturalım. İlk yazacağımız satır paketimizin ismi olacak.

package main

Go packagelar ile çalışıyor yani dosyada package olmak zorunda. Main package ise diğer packagelardan farklı olarak bizim default yani varsayılan olarak çalıştırmak istediğimiz kısmı ifade ediyor. Bu, C'deki main ile benzer bir durum.

Ardından kütüphaneleri dahil ediyoruz ve varsayılan fonksiyonumuz olan main'i ekliyoruz. Fonksiyon içinde fmt kütüphanesindeki Println metodu ile Hello Security! yazdırıyoruz.

package main

import (
	"fmt"
)

func main(){
    fmt.Println("Hello Security!")
}

Bu noktada önemli bir ayrıntı var. Örneğin bizim main package'ımız altında iki ayrı read ve Read fonksiyonu olsaydı, küçük harfle başlayan private, büyük harfle başlayan public olacaktı. Dolayısıyla private olanı sadece main package kullanabilirken, public olanı diğer packagelar da kullanabilecekti. Bu durum aynı şekilde değişkenler için de söz konusu.

Şimdi programı çalıştırabiliriz. Bunun için terminali açıyoruz.

Programımızı go run . veya go run main.go ile doğrudan çalıştırabiliriz. Nokta ile main package'ı arayıp çalıştıracaktır. go run komutu sayesinde derlenmeden çalışabiliyor ve bu da geliştirme hızımızın artmasını sağlıyor.

Programımızı go build . komutu ile derleyebiliriz. Bu derleme sonucunda bir .exe dosyası çıkacaktır. Bu dosyayı .\main.exe komutu ile çalıştırabiliriz.

Burada aynı zamanda Go'nun dezavantajlarından biriyle karşılaşıyoruz. Oluşan exe dosyasının boyutunu incelediğimiz zaman 2mb civarında bir şeyle karşılaşıyoruz. Bunun nedeni Go'nun fmt kütüphanesinin tamamını statik bir şekilde exe dosyasına dahil etmiş olması.

Primitive Veri Yapıları

Değişken tanımlama kısmında Go bize farklı alternatifler sunuyor.

var i int
i=42
println(i)

var f float32 = 3.14
println(f)

name := "MDI"
println(name)

NOT.1: Değişkeni := ile tanımladığımız zaman derleyici derleme esnasında değişkenin yapısına karar veriyor. Fakat biz tanımlarken yapısını belirtirsek doğrudan sınır çizmiş oluyoruz.

NOT.2: Veri yapısını belirtirken sadece float ya da int yazabileceğimiz gibi float32, float64 ve int8, int16, int32, int64 gibi daha spesifik şekilde de yazabiliriz.

Kullanılabilecek bütün veri yapılarına ulaşabilmek için: https://golangbyexample.com/all-data-types-in-golang-with-examples/

Pointerlar

var lastName *string = new(string)
*lastName = "Demir"

fmt.Println(lastName) //lastName'in adresini yazar.

fmt.Println(*lastName) //Demir değerini yazar.

Pointerlar bellekteki adresi işaret eder. Dolayısıyla yukarıda lastName ile işaret edilen bir adresi new(string) ile oluşturuyoruz ve buna Demir değerini atıyoruz.

Pointerları dinamik bellek yönetimi yapmak için kullanırız. Yani birden fazla paket aynı veri üzerinde çalışacaksa, bu verileri kopyalamak yerine verilerin adresine işaret ederek tek veri üzerinden bellek kullanımında verim sağlamış oluruz. Elbette bu aynı zamanda verinin değişmesi halinde bütün paketlerde verinin değişmesi demektir.

name := "MDI"

var lastName *string = &name

fmt.Println(*lastName, lastname, &name) //Çıktısı: MDI, lastname'in adresi, name'in adresi olur.

Yukarıdaki kodda name değişkeninin adresini alarak lastName'e veriyoruz. Dolayısıyla artık lastName, name'in değerine sahip oluyor.

NOT: &, 'address of' demek oluyor.

Iota ve Constant

const c int = 3
println(c)

Constant derleme zamanında kullanılıyor. Derlenmiş bir çıktı alınıyor ve artık sabit bir şekilde kalıyor. Bir daha değiştirilemiyor.

package main

func main(){
    println(first,second,third) //Çıktısı: 0 1 2
}
const(
	first = iota
    second
    third
)

iota kullandığımız takdirde artan bir yapı oluşturuyor.

const(
	first = iota + 1
    second = 2 << iota
    third
)

Bu durumda çıktımız: 1 4 8 olur. << shift yapıyor ve işlemci aritmetiği yapıyoruz.

Array, Slice, Map ve Struct

Array
var arr [3]int
arr[0] = 1

fmt.Println(arr)

Array'in diğer elemanlarına atama yapmadığımız takdirde çıktımız: [1 0 0] şeklinde oluyor.

Golang arrayle sabit boyutludur. Yani yukarıdaki array'e arr[3] diyerek 4. bir eleman atayamayız. Dinamik array diye bir şey yok. Bunun için farklı bir yapı var.

var arr [3]int
arr[0] = 1

arr2 := [3]int{1,2,3}
fmt.Println(arr, arr2)

Array aynı zamanda := işaretiyle de tanımlanabilmektedir.

Slice
slice := []int{1,2}

fmt.Println(slice)

Slicelar bir nevi dinamik arraylerdir. Yani boyut belirtmek zorunda değiliz.

NOT: Normal şartlarda tavsiye edilen boyutu belirlenmiş arrayler kullanmaktır. Çünkü sonrasında yapılacak işlerdeki performans açısından bakılınca boyutu belli olan arrayler, boyutu belli olmayan slicelara göre çok ciddi farklar yaratıyor.

slice2 := arr[1:2]
fmt.Println(slice2)

Slice ile arrayde bölme işlemi yapıyoruz. Örneğin, yukarıda arrayin 1. elemanından ikinci elemanına kadar alıyoruz.

Map
m := map[string]int{"orhun",42}
fmt.Println(m["orhun"]) //Çıktı: 42

Burada bir map tanımlıyoruz. Bu map'in key'inin string, value'sunun int olacağını belirtiyoruz. Aynı key'den sadece bir tane olabilir.

Struct
type person struct{
    id		int
    Name	string
}

p := person{
    id: 1,
    Name: "Orhun"
}

fmt.Println(p) //Çıktısı: {1 Orhun} olur.

Şimdi dosyalarımızı biraz daha düzenleyip farklı bir paket üzerinden çalışıyoruz.

Bunun için önce bir models klasörü açıyoruz..

Models klasörü altında person.go dosyamızı oluşturuyoruz. person.go dosyası içerisinde bir Person struct'ı oluşturuyoruz.

package models

type Person struct{
    Id			int64
    FirstName	string
    LastName	string
}

Şimdi bu package'a main.go üzerinden ulaşmak istiyoruz. Bunun için önce dosya organizasyonuna dikkat ediyoruz.

NOT: Golang için gopath\src\github.com\github_isminiz\hello şeklinde bir dosyalama ile çalışmanız tavsiye ediliyor. Bu şekilde diğer projelerinizdeki paketlere ulaşmanız da mümkün olmuş olacak. GOPATH ayarlama konusunda ve dosya ile kod organizasyonuna dair daha detaylı bilgi için https://golang.org/doc/code linkindeki dökümantasyonu incelemenizi tavsiye ederim.

Ardından bir module oluşturuyoruz. Module bütün packagelarımızın bulunduğu yer oluyor.

Module oluşturmak içingo mod init "github.com/github_isminiz/hello" komutunu kullanıyoruz.

Sonrasında main.go dosyamız içerisinde models package'ımızı import ediyoruz ve ardından bir user yaratıyoruz.

package main

import (
    "fmt"
	"github.com/github_isminiz/models"
)

func main(){
    u := models.Person{
        Id:			2,
        FirstName:	"Mehmet",
        LastName:	"İnce"
    }
    
    fmt.Println(u.FirstName)
}
Go Constructor Çözümü

Burada newPerson adında bir fonksiyon oluşturuyoruz. Bu fonksiyon aldığı parametrelerden bir Person struct'ı oluşturacak ve bunu return edecek.

NOT: Bunu yapmamızın sebebi Golang'de class olmaması ve dolayısıyla constructor olmaması. Object Oriented taktiklerini Golang'de bu şekilde çözüyoruz.

İkinci olarak SetNickName adında ikinci bir fonksiyon oluşturuyoruz ve bu sayede Person struct'ının nickName parametresine ulaşıp değiştirebilmeyi sağlıyoruz.

package models

type Person struct {
	id        int64
	firstName string
	lastName  string
	nickName  string
}

func NewPerson(id int64, firstName, lastName string) Person {
	return Person{
		id:        id,
		firstName: firstName,
		lastName:  lastName,
	}
}

func (p Person) SetNickName(nickName string) {
	p.nickName = nickName
}

Bu durumda main.go dosyasını da şu şekilde düzenlememiz gerekiyor:

package main

import (
    "github.com/github_isminiz/hello/models"
	"fmt"
)

func main() {
	u := models.NewPerson("Mehmet", "İnce")
	u.SetNickName("mdisec")

	fmt.Println(u)
}

Fakat bu şekilde hâlâ user'ımızın nickName değişkenine ulaşamıyoruz. Çünkü SetNickName fonksiyonumuz hangi person'ın nickName'ini değiştirmek istediğimizi bilmiyor bunun için SetNickName fonksiyonumuzu pointer olacak şekilde düzenlememiz gerekiyor.

func (p *Person) SetNickName(nickName string) {
	p.nickName = nickName
}

Artık doğrudan değiştirmek istediğimiz user'ın adresini işaret etmiş oluyoruz.

Aynı zamanda nickName'in hata kontrolünü de yapmak istersek kodumuzu tekrar düzenleyebiliriz.

func (p *Person) SetNickName(nickName string) error{
    if len(nickName) == 0{
        return errors.New("NickName must not be empty")
    }
    p.nickName = nickName
    return nil
}

ve main.go'da da main fonksiyonumuzda değişiklik yaparak hatayı gösterebiliriz.

func main() {
	u := models.NewPerson("Mehmet", "İnce")
    err := u.SetNickName("mdisec")
    if err != nil{
        fmt.Printf(err.Error())
    }
	fmt.Println(u)

}

Fonksiyonlar

Fonksiyonlarımız hiçbir argüman almayabilir veya birden fazla argüman alabilir. https://tour.golang.org/basics/4 linkinde verilen örnek üzerinden gidersek:

package main

import "fmt"

func add(x int, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}
  • add adından bir fonksiyon oluşturuyoruz. Önceden dediğimiz gibi büyük veya küçük harfle başlayarak private veya public olacağına da karar vermiş oluyoruz aynı zamanda.
  • Ardından parantez içerisinde var ise argümanlarımızı ve veri tiplerini yazıyoruz. Önce değişken ismini sonrasında tipini yazdığımıza dikkat ediyoruz.
  • Parantezden sonra eğer return edeceksek return tipini belirtiyoruz. Örneğin add fonksiyonu int tipinde return ediyor. Main fonksiyonu ise herhangi bir return yapmıyor.