SlideShare a Scribd company logo
Fonksiyonlar, Metotlar, Arayüzler ve Yapılar
Bu bölümde programlama dünyasının en kritik parçalarından birkaçını inceleyeceğiz. İnceleyeceğimiz bu nesne
tipleri profesyonel ve modern yazılımlar geliştirmenin temel taşlarını oluşturmaktadır.
Fonksiyonlar
Yazılım geliştirirken kod tekrarı yapmamak dikkat edilmesi gereken en önemli unsurlardan biridir. Bu nedenle
belirli işler konusunda uzmanlaşmış nesneler üretmemiz gerekir.
Örneğin, geliştirilen uygulama içerisinde 10 satırlık bir matematik algoritmasını her seferinde tekrar yazmak
doğru bir yaklaşım mıdır? Hayır! Bu tür işlemlerin tek bir kez yazılması ve bu işlemin de sadece kendi işinde
uzmanlaştırılması gerekir.
İşte bu ihtiyaçlardan dolayı fonksiyon nesneleri doğmuştur. Biz her iş için ayrı ayrı fonksiyonlar geliştirir ve
bunları kullanırız.
Söz dizimi:
func fonksiyon-ad(parametre-listesi) (geri-donus-listesi) {
fonksiyon-govdesi
}
- Fonksiyon isimleri benzersiz olmalıdır.
- Fonksiyon parametre isimleri benzersiz olmalıdır.
- Bir fonksiyon geri dönüş değerlerine sahip olabilir ama bu zorunlu değildir.
- Fonksiyonun geri dönüş değeri birden fazla ise parantez kullanmak zorunludur.
- Geri dönüş tipine sahip bir fonksiyonun gövdesi return komutu ile bitmelidir.
Fonksiyon Tanımlamaları
Fonksiyonlar ile ilgili temel kural ve söz dizimini uygulamak için birkaç örnek yapalım
Örnek:
func topla(x int, y int) int { // isimsiz geri dönüş tipi
return x + y
}
Örneği kullanalım.
res := topla(5, 4)
fmt.Println(res)
Örnek çıktısı:
9
Dikkat: Eğer C# ya da Java gibi programlama dillerini kullandıysanız, Go dilindeki fonksiyonların parametre
tanımlaması size garip gelmiş olabilir. Çünkü diğer dillerde parametre tanımlarken önce veri tipi, sonra
parametre adı şeklinde bir parametre tanımı yapılırken, Go dilinde bu tam tersidir! Go dilinde önce parametrenin
cihanozhan.com
adını, sonra parametrenin veri tipini yazarız. Go dili tasarımcılarının bu konudaki beyanı: “Bu kullanım insan dili
ve düşünce tarzına daha yakın ve doğru olduğunu düşünüyoruz.”
topla isimli fonksiyonda geri dönüş veri tipi olarak integer’ı kullandık. Bu örnekte isimsiz geri dönüş yöntemini
kullandık. Ancak aşağıdaki şekilde isimlendirilmiş bir geri dönüş de kullanabiliriz.
func topla(x int, y int) (r int) { // isimlendirilmiş geri dönüş tipi
return x + y
}
Geri dönüş olarak r adını kullandık. Bu örneğin ilk örnekten bir diğer farkı ise geri dönüş tipinin parantez
içerisinde tanımlanmış olmasıdır. Eğer isimlendirilmiş bir geri dönüş nesnesi kullanıyorsak bunu parantez
içerisinde tanımlamak zorundayız. Aynı şekilde, eğer isimsiz olarak birden fazla geri dönüş değeri
tanımlayacaksak, bu değerleri gene parantez içerisinde tanımlamak zorundayız.
Örnek:
func topla(x int, y int) (int, int) { // Çoklu geri dönüş yapmak!
return 0, x + y // İlk geri dönüşteki 0 değeri sadece bir örnektir.
}
Fonksiyonların herhangi bir geri dönüş tipine sahip olma zorunluluğu yoktur. Bir fonksiyonu geri dönüş tipi
olmadan da aşağıdaki gibi tanımlayabiliriz.
Örnek:
func save(data float32) {
fmt.Println("Saved: ", data)
}
save() fonksiyonunu kullanalım.
save(4.5)
Örnek çıktısı:
Saved: 4.5
Sadece verilen görevi yerine getirmesi gereken ve herhangi bir geri dönüş değerine ihtiyaç duyulmayan
durumlarda, geri dönüş değeri olmayan fonksiyonlar tercih edilebilir.
Her türlü hesaplama ve iş kuralı için fonksiyonları kullanabiliriz. Örneğin, iki sayısal değer içerisinde en büyük
olanı bulalım.
Örnek:
func max(number1, number2 int) int {
var result int
if number1 > number2 {
result = number1
} else {
result = number2
}
return result
cihanozhan.com
}
Örnekteki max fonksiyonunu kullanalım.
var x, y = 30, 32
res := max(x, y)
fmt.Println("Sonuç: ", res)
Örnek çıktısı:
Sonuç: 32
Go diline özel diğer bir geri dönüş yöntemi ise naked return geri dönüş yöntemidir. Bu yöntem isimlendirilmiş
geri dönüş yöntemine benzemekle birlikte bu yöntemler birbirlerinden farklıdırlar.
Örnek:
func main() {
xx, yy := Increase(2, 4)
fmt.Println(xx)
fmt.Println(yy)
}
func Increase(x int, y int) (num1 int, num2 int) {
num1 = x + 1
num2 = y + 1
return
}
Örnek çıktısı:
3
5
Yukarıdaki naked return örneğine dikkat ederseniz return komutunun sonunda herhangi bir dönüş nesnesi
tanımlamadık. Bu yöntemin diğerlerinden temel farkı, return deyiminin herhangi belirli bir nesneyi
dönmemesidir. Bu fonksiyonun geri dönüş tiplerini tanımlarken belirttiğimiz num1 ve num2 nesneleri
fonksiyonun içerisinde kullanılabilir ve değeri değiştirilebilir nesnelerdir. Bu nesnelerle işimiz bittiğinde return
komutunu çalıştırarak bu nesnelerin geriye dönmesini sağlayabiliyoruz.
Yukarıdaki metodun geri dönüş tipini aşağıdaki gibi de tanımlayabilirdik;
func Increase(x int, y int) (num1, num2 int) {
…
}
Birinci Sınıf Fonksiyon Desteği
Birinci sınıf fonksiyonlar, fonksiyon nesnelerinin değişkenlere atanması, argümanlara parametre olarak
gönderilmesi ve bir fonksiyondan geri dönüş tipi olarak kullanılabilmesine denir. Go dili birinci sınıf
fonksiyonları destekler.
cihanozhan.com
Anonim Fonksiyonlar
Anonim değişkenler, belirli durumlarda ve sadece belirli işlemlerde geçerli olacak şekilde işleme özel hazırlanan
metotlardır.
Örnek:
func() {
fmt.Println("Merhaba Mars!")
}()
Örnek çıktısı:
Merhaba Mars!
Yukarıdaki kodu main() metodu içerisinde yazdık. Bu kod bir anonim fonksiyonun kullanımına verilebilecek en
temel örnektir. Anonim metotlar bu şekilde kendi başlarına çalışacak şekilde tanımlanabileceği gibi, bir
değişkene atanarak da kullanılabilirler.
Bir fonksiyonu değişkene atama işlemi için bir örnek yapalım.
Örnek:
x := func() {
fmt.Println("Merhaba Mars!")
}
x()
fmt.Printf("%T", x)
Örnek çıktısı:
Merhaba Mars!
Yukarıdaki örnekte, main() metodu içerisinde bir anonim fonksiyon tanımladık ve bu fonksiyonu x adıyla
oluşturduğumuz değişkene atadık. Bu atama işleminden sonra, artık x nesnesi bir fonksiyon gibi davranır. Ancak
bu durum bu fonksiyonun bir adı olduğu anlamına gelmez. Sadece bu fonksiyonu temsil edebilen bir nesne
olduğunu gösterir. Yani, halen bu fonksiyonun bir adı olmadığı için, bu yönteme anonim fonksiyon diyoruz.
Parametre Alan Anonim Fonksiyonlar
Bir anonim fonksiyonun parametreli olarak tanımlanması mümkündür. Bu için bir örnek yapalım.
Örnek:
func(p string) {
fmt.Println("Merhaba", p)
}("Mars!")
Örnek çıktısı:
Merhaba Mars!
Yukarıdaki örnekte, anonim fonksiyonu parametre alabilecek şekilde tasarladık ve parametrenin adını p, veri
tipini ise string olarak belirledik. Sonrasında, anonim fonksiyonun kapama parantezleri olarak kullandığımız
alanda ise “Mars!” metnini göndererek bu parametreyi fonksiyona sağlamış olduk.
cihanozhan.com
Tip Olarak Tanımlanan Anonim Fonksiyonlar
Bir anonim fonksiyonu type anahtar kelimesiyle bir tip olarak tanımlayabiliriz. Bu sayede, oluşturulan bu yeni
tipin parametrik yapısıyla aynı yapıya sahip fonksiyonlar için bir veri tipi olarak kullanılmasını sağlayabiliriz.
Örnek:
Aşağıdaki type tanımını main() metodunun üzerinde yapalım.
type fourMath func(p1 int, p2 int) int
fourMath adındaki bu type nesnesi bizim için bir şablon niteliği taşıyacak. Dışarıdan iki adet integer veri tipi
alan ve dışarıya bir adet integer veri tipinde veri göndermesi gereken bir fonksiyon şablonu. Bu fourMath isimli
type’ın amacı, matematikte kullandığımız temel 4 işlemi yapacak fonksiyonları temsil etmektir. Şimdi bu
şablonun gereksinimlerini karşılayacak bir fonksiyon oluşturalım.
// Toplama işlemini yapan fonksiyon
var add fourMath = func(x int, y int) int {
return x + y
}
// Çıkarma işlemini yapan fonksiyon
var sub fourMath = func(x int, y int) int {
return x - y
}
// Çarpma işlemini yapan fonksiyon
var multiply fourMath = func(x int, y int) int {
return x * y
}
// Bölme işlemini yapan fonksiyon
var div fourMath = func(x int, y int) int {
return x / y
}
// İşlemler
res_add := add(7, 6)
res_sub := sub(7, 6)
res_multiply := multiply(7, 6)
res_div := div(10, 2)
// İşlem sonuçları
fmt.Println("Sum", res_add)
fmt.Println("Sub", res_sub)
fmt.Println("Multiply", res_multiply)
fmt.Println("Div", res_div)
cihanozhan.com
Örnek çıktısı:
Sum 13
Sub 1
Multiply 42
Div 5
Fonksiyonlara Parametre Olarak Geçilen Anonim Fonksiyonlar
Anonim fonksiyonlar, teknik olarak bir veri tipi gibi yorumlanabildiği için bu fonksiyonları başka fonksiyonlara
bir parametre gibi gönderebiliriz.
Örnek:
f := func(x, y int) int {
return x + y
}
res := calculate(f)
fmt.Println(res)
calculate() fonksiyonunu hazırlayalım.
func calculate(param func(a, b int) int) int {
// Simülasyon : Çeşitli işlemler sonucunda 60 ve 7 değerlerini bulduk!
// Sonra bu değerleri param(...) fonksiyonuna gönderdik.
return param(60, 7)
}
Örnek çıktısı:
67
Bir Fonksiyondan Başka Bir Fonksiyonu Geri Dönmek
Bir fonksiyondan bu fonksiyon içerisinde oluşturulmuş farklı bir fonksiyonu geriye dönmek mümkündür.
Örnek:
func calculate() func(p1, p2 int) int {
f := func(p1, p2 int) int {
return p1 + p2
}
return f
}
Yukarıdaki yeni calculate() fonksiyonunu kullanalım.
res := calculate()
fmt.Println("result: ", res(83, 8))
Örnek çıktısı:
result: 91
cihanozhan.com
calculate() fonksiyonunun yeni halinin biraz açıklamaya ihtiyacı olabilir.
Yukarıda tanımladığımız calculate() fonksiyonu herhangi bir parametre almıyor. Ancak geriye farklı bir
fonksiyon yapısı dönmektedir. Geriye dönülen bu fonksiyon, dışarıdan p1 ve p2 adında iki adet integer
parametre alır. calculate() içerisinde oluşturduğumuz yeni bir isimsiz fonksiyon da dış fonksiyondan alınan p1
ve p2 parametrelerini parametre olarak almaktadır. İçerideki isimsiz fonksiyonda ise, sadece basit bir şekilde
dışarıdan gelen parametrelerin toplama işlemi yapılmaktadır. Tüm bu işlemlerin sonunda geriye bir fonksiyon
dönmek için, isimsiz fonksiyonu atadığımız f adındaki nesneyi return ile geriye dönüyoruz.
Closure
Şu ana kadar fonksiyonlara parametre göndererek onları kullanmayı inceledik. Mevcut genel parametre
kullanımının yaklaşımı değer tipi olarak çalışmak üzerine kuruludur.
Hatırlatma : Değer tipleri hafızada kopyalanarak aktarılırlar. Bu nedenle, normal bir fonksiyon kullanımında
parametre olarak gönderilen değişken ile fonksiyonun parametre olarak aldığı değişken bilgisayar hafızasında
farklı adreslere sahiptir. Yani ikisi birbirinin kopyası olsa da farklı nesnelerdir.
Ancak, istersek bir fonksiyona parametre olarak gönderilecek değişkenlerin bir parametre olarak değil, doğrudan
fonksiyon içerisinde tanınarak kullanılmasını sağlayabiliriz. Biraz karışık gibi görünebilir. Ancak gayet basittir.
Şimdi bir fonksiyonu nasıl bir closure olarak kullanabileceğimize bir bakalım.
Örnek:
Uygulamamızın main() fonksiyonu içerisinde aşağıdaki anonim fonksiyonu tanımlayarak kullanalım.
x := 5
func() {
fmt.Println("x =", x)
}()
Örnek çıktısı:
x = 5
Hatırlarsanız bir anonim fonksiyona parametre gönderebiliyorduk. Ancak yukarıdaki örneğe dikkat ederseniz, bu
anonim fonksiyona herhangi bir parametre göndermediğimiz halde, dışarıda tanımlanan x değişkenine anonim
fonksiyonun referansına anonim fonksiyonun içerisinden ulaşarak kullanabildik.
Not : C# programlama dili kullananlar için, Go dilindeki closure yaklaşımı C#’daki ref ve out parametreleriyle
benzer amaçlar için dil tasarımına eklenmiştir.
Closure bir anonim fonksiyon yaklaşımıdır. Yani aslında closure fonksiyonları da birer anonim fonksiyonlardır.
Ancak closure fonksiyonlarının normal fonksiyonlardan farkı, bir değişkeni parametre olarak gönderip değer
tipi(value type) olarak davranmasını beklemek yerine, bir referans tipi olarak kullanılmasını sağlamaktır. Bu
sayede değişkenlerin hafızadaki adreslerini referans olarak closure içerisinden erişilebilmesini sağlar.
Örnek:
x := 0
counter := func() int {
x++
return x
}
cihanozhan.com
fmt.Println(counter())
fmt.Println(counter())
Örnek çıktısı:
1
2
Yukarıda closure yapısını basit bir şekilde anlatan başka bir örnek daha yaptık. Basit bir sayaç fonksiyonu
tanımlamak için closure tanımladık. Bu closure fonksiyonu bir üst satırdaki x değişkeninin referansına
erişebildiği için içeride x’in değerini her seferinde bir artırabiliyoruz. Artırma işleminden sonra ise, bu değişkeni
closure’dan geri dönüyoruz.
Closure fonksiyonların bir diğer faydası da veri izolasyonu(data isolation) sağlamasıdır. Örneğin, yukarıdaki
kodu main() fonksiyonu içerisinde yazdık. Bu demek oluyor ki, main() metodu içerisinden x değişkenine
erişerek closure’daki işleme dışarıdan müdahale edilebilir. Bu tür, kod ya da veriye kendi işlemi dışından
müdahale edilebilmesine engel olma işlemine veri izolasyonu diyoruz.
Örnek:
// counterProcess adında yeni bir closure fonksiyon tanımlayalım.
func counterProcess() func() int {
x := 0
return func() int {
x++
return x
}
}
// counterProcess fonksiyonunu main() fonksiyonu içerisinde kullanalım.
counter := counterProcess()
fmt.Println(counter())
fmt.Println(counter())
Örnek çıktısı:
1
2
Aslında son örneğimizde bir önceki örneğin aynısını yaptık. Ancak bu sefer closure’ı fonksiyon dönen bir
fonksiyon içerisinde tanımladık. Bu sayede, artık closure içerisinde referansı kullanılan x değişkenine dışarıdan
müdahale edilmesini engelledik.
Şimdi de closure için farklı bir gerçek dünya uygulaması yapalım.
Örnek:
// append() adında fonksiyon dönen yeni bir fonksiyon tanımlıyoruz.
func append() func(string) string {
str := ""
co := func(x string) string {
str = str + " " + x
return str
cihanozhan.com
}
return co
}
// main() fonksiyonu içinde append() fonksiyonu kullanıyoruz.
app := append()
app("Hello")
app("Gophers")
result := app("!")
fmt.Println(result)
Örnek çıktısı:
Hello Gophers !
Yukarıdaki örnekte dışarıdan aldığı metinsel veriyi kendi içindeki metinsel veri ile birleştirerek uç uca ekleyen
append() adında bir closure fonksiyon tanımladık. Bu fonksiyonu kullanırken de, app adında bir değişkenine
append() fonksiyonunu atayarak app değişkeninin append() fonksiyonu gibi davranmasını sağladık. Sonrasında
ise, app() fonksiyonuna sürekli yeni metinsel veriler ekleyerek hepsini uç uca birleştirmesini sağladık. En son
işlemde de app() fonksiyonundan dönen birleştirilmiş metinsel veriyi result adındaki değişkene atayarak ekrana
bastık.
Şu ana kadar birçok örnek yaptık. Ancak bu kitabın temel özelliği, Go dilini hem öğretmek, hem de uygulamalar
yaparak bu özelliklerin nasıl uygulandığını örneklerle göstermektir. Bu nedenle, şimdi closure fonksiyonlarını ve
struct yapısını kullanarak biraz daha detaylı bir gerçek dünya uygulaması yapalım.
Uygulama Amacı: Bir teknoloji firmasının çalışan bilgilerini ve uzmanlık alanlarını tutacak ve bu veriler
üzerinde çeşitli filtre ve hesaplamalar yapacak mini bir uygulama geliştirmek.
Uygulama:
Uygulamanın genel struct yapısını oluşturalım.
type Employee struct {
FirstName string
LastName string
Salary int
Expertises []Expertise
}
type Expertise struct {
ID int
Name string
}
Uygulama içerisinde kullanacağımız struct nesnelerini tanımladık. Her bir çalışanın birden fazla uzmanlık alanı
olabileceği için ilgi alanlarını(Expertises) dizi olarak tutuyoruz.
Uygulamanın fonksiyonlarını hazırlayalım.
// Çok amaçlı filtreleme fonksiyonudur.
func filter(emps []Employee, f func(Employee) bool) []Employee {
var empList []Employee
cihanozhan.com
for _, emp := range emps {
if f(emp) == true {
empList = append(empList, emp)
}
}
return empList
}
// Sadece sayaç görevi için kullanılacak fonksiyondur.
func count(emps []Employee, f func(Employee) bool) int {
var count int
for _, emp := range emps {
if f(emp) == true {
count++
}
}
return count
}
Uygulama içerisinde kullanacağımız genel fonksiyonlarımızı da hazırladık. Ancak bu uygulama için demo
verilere ihtiyacımız olacak! Demo verileri de bilgisayar hafızası üzerinde çalışacak şekilde ayrı bir fonksiyon
olarak hazırlayacağız.
func getData() []Employee {
var val = make([]Employee, 0, 0)
v1 := Employee{
FirstName: "Cihan",
LastName: "Özhan",
Salary: 1100,
Expertises: []Expertise{
Expertise{3, "C#"},
Expertise{5, "Go"},
Expertise{6, "Oracle"},
Expertise{8, "PostgreSQL"},
Expertise{10, "SQL Server"},
},
}
v2 := Employee{
FirstName: "Murtaza",
LastName: "Çalışkan",
Salary: 986,
Expertises: []Expertise{
Expertise{4, "HTML"},
Expertise{7, "JavaScript"},
Expertise{10, "SQL Server"},
},
}
val = append(val, v1)
val = append(val, v2)
cihanozhan.com
return val
}
Yukarıdaki getData() fonksiyonunu sadece uygulama içerisinden veriye erişim için kullanacağız.
Artık main() fonksiyonu içerisinde çalışmaya başlayabiliriz.
Öncelikle, aşağıdaki komutu kullanarak uygulamamızın hafızasına demo verileri dolduracağız.
empList := getData()
// Maaşı 1000 birim üzerinde olan çalışanları getir.
searchTagSalary := 1000
f1 := filter(empList, func(emp Employee) bool {
if emp.Salary >= searchTagSalary {
return true
}
return false
})
fmt.Println(f1)
f1 çıktısı:
[{Cihan Özhan 1100 [{3 C#}{5 Go}{6 Oracle}{8 PostgreSQL}{10 SQL Server}]}]
empList := getData()
// Adı "Murtaza" olan çalışanları getir.
searchTagName := "Murtaza"
f2 := filter(empList, func(emp Employee) bool {
if emp.FirstName == searchTagName {
return true
}
return false
})
fmt.Println(f2)
f2 çıktısı:
[{Murtaza Çalışkan 986 [{4 HTML} {7 JavaScript} {10 SQL Server}]}]
empList := getData()
// HTML uzmanlık alanına sahip çalışanları getir.
searchTagExpertise := "HTML"
f3 := filter(empList, func(emp Employee) bool {
for i := 0; i < len(empList); i++ {
if emp.Expertises[i].Name == searchTagExpertise {
return true
}
}
return false
})
fmt.Println(f3)
cihanozhan.com
f3 çıktısı:
[{Murtaza Çalışkan 986 [{4 HTML} {7 JavaScript} {10 SQL Server}]}]
// 1000 birim ve üzeri maaş alan çalışanları getir. (500 değeriyle de deneyiniz!)
searchTagSalaryForCount := 1000
f4 := count(empList, func(emp Employee) bool {
if emp.Salary >= searchTagSalaryForCount {
return true
}
return false
})
fmt.Println(f4)
f4 çıktısı:
1
Yukarıdaki her bir f değişkeni(f1, f2, f3, f4) ayrı bir işlemin sonucunu tutmaktadır. Bunları ayrı ayrı ekrana
basarak inceleyiniz.
Bonus olarak bu mini uygulama üzerinde aşağıdaki işlemleri de yapabilirsiniz.
Örnek : Çalışanların ad ve soyadlarını birleştirerek ekrana yazdıralım.
for _, v := range getData() {
fmt.Println(v.FirstName + " " + v.LastName)
}
Örnek çıktısı:
Cihan Özhan
Murtaza Çalışkan
Örnek : Tüm çalışanların uzmanlık alanlarını ekrana yazdıralım.
for _, v := range getData() {
for _, e := range v.Expertises {
fmt.Println(strconv.Itoa(e.ID) + " " + e.Name)
}
fmt.Printf("n")
}
Not : Yukarıdaki örnekte strconv built-in paketi kullanılmıştır.
Örnek çıktısı:
3 C#
5 Go
6 Oracle
8 PostgreSQL
10 SQL Server
cihanozhan.com
4 HTML
7 JavaScript
10 SQL Server
Ertelenmiş Fonksiyonlar: Defer
Defer ile tanımlanan fonksiyonlar, kendisini çevreleyen fonksiyon geri dönüş işlemine gelene kadar ertelenen
fonksiyonlardır.
Ertelenmiş bir fonksiyon çağrısının argümanları anında değerlendirilir. Ancak fonksiyon çağrısı kendisini
çevreleyen fonksiyon dönmeden işleme alınmaz.
Bir veritabanı işlemi yaptığımızı düşünelim. Bir veritabanı işlemi yapmanın temel kuralı, veritabanı üzerinde bir
bağlantı açmaktır. Bağlantı açma işleminden sonra gerekli SQL sorguları yapılır ve tüm işlemler bittikten sonra
da veritabanı bağlantısı kapatılmalıdır.
Bu senaryoya göre nasıl bir defer kullanımı yapabileceğimize bir bakalım.
Örnek:
main() fonksiyonu üzerinde isConnected adında bir değişken tanımlayalım.
var isConnected bool = false
Sonrasında bu mini uygulamada kullanmak için paket seviyesindeki temel fonksiyonları hazırlayalım.
func databaseProcessing() {
connect()
fmt.Println("Bağlantıyı kesme işlemi erteleniyor!")
defer disconnect()
fmt.Printf("Bağlantı açık: %vn", isConnected)
fmt.Println("Veritabanı işlemleri yapılıyor!")
}
func connect() {
isConnected = true
fmt.Println("Veritabanına bağlıyız!")
}
func disconnect() {
isConnected = false
fmt.Println("Veritabanı bağlantısı kapalı!")
}
Uygulamanın main() fonksiyonu içerisindeki işlemleri yapalım.
fmt.Printf("Bağlantı açık: %vn", isConnected)
databaseProcessing()
fmt.Printf("Bağlantı açık: %vn", isConnected)
Örnek çıktısı:
cihanozhan.com
Bağlantı açık: false
Veritabanına bağlıyız!
Bağlantıyı kesme işlemi erteleniyor!
Bağlantı açık: true
Veritabanı işlemleri yapılıyor!
Veritabanı bağlantısı kapalı!
Bağlantı açık: false
Yukarıdaki uygulamada neler oldu?
Öncelikle mevcut veritabanı bağlantısının açık olup olmadığını kontrol ettik. Veritabanı bağlantısını henüz
açmadığımız için bağlantının kapalı olduğuna dair false bilgisini aldık. Sonrasında ise databaseProcessing()
fonksiyonunu çalıştırdık. Bu fonksiyon içerisinde ilk olarak connect() fonksiyonu çalıştırılmaktadır. Bu
fonksiyon ise veritabanı bağlantısını açmak için kullanılmaktadır. Ve bu nedenle, ekranda Veritabanına
bağlıyız mesajını gördük. Hemen connect() işleminden sonra ise defer ile birlikte disconnect() fonksiyonunu
çağırdık! İşte bu nokta önemli… Aslında biz o anda veritabanı bağlantısını sonlandırmak istemiyoruz. Sadece
Go derleyicisine söylemek istediğimiz şudur: “Bu fonksiyon içerisinde veritabanı ağlantısı açıldıktan sonra
veritabanı üzerinde birçok işlem yapacağım. Bu işlemler bittikten sonra(fonksiyonun sonunda) mevcut
açık olan veritabanı bağlantısını otomatik olarak kapat”. İşte bu nedenle disconnect() fonksiyonunu tüm
işlemlerden önce defer ile birlikte çağırabiliyoruz.
Not : Veritabanı ya da stream işlemlerinde kaynaklar üzerinde oluşturulan bağlantıların yönetilmesi ve işlem
bittikten sonra kapatılması bazen unutulabilmektedir. Bu durum sistem kaynaklarının tüketimini olumsuz yönde
etkiler. Bu tür durumlarla karşılaşmamak için defer kullanımını alışkanlık haline getirmek işinizi
kolaylaştırabilir.
Metotlar
Metotlar temel anlamda birer fonksiyondur. Metotların fonksiyonlardan temel farkı, metotların sadece
parametrelerinin değil, aynı zamanda alıcılarının da olmasıdır.
Söz dizimi:
func (t Type) methodName(parameter-list) {
}
Yukarıdaki metot söz diziminde fonksiyonlardan farklı olarak dikkatleri çeken nokta (t Type) şeklinde
kullanılan tanım alanıdır. Bu alan metodun hangi alıcı tipiyle çalışacağının belirtildiği alandır. Bir metoda alıcı
olarak atanan nesne çalışma anında bu metot ile aynı scope içerisinde çalıştırılır.
Not : C# ya da Java gibi programlama dillerinde bu yaklaşım bulunmaz. Bunun nedeni bu dillerin metotları
kendi class’ları içerisinde tanımlamaya zorlamasıdır. Ancak Go dilinde struct(class yerine) için özel olarak
belirlenen bir scope parantezi yer almaz. Bu nedenle, Go dilinde bir metodu bir nesneye bağlamak için o
nesnenin tipini ilgili metoda alıcı olarak bağlamamız gerekir. Bu diğerlerine göre daha sade ve kullanışlı bir
yöntemdir.
Metotları genel olarak struct nesneleriyle birlikte kullanacak olsak da, bir metodu herhangi bir Go tipiyle
birlikte kullanabiliriz.
Şimdi integer veri tipine sahip özel bir tip oluşturalım ve bu tipe MultipleBy10 adında yeni bir metot atayarak
kullanalım.
cihanozhan.com
Örnek:
type MyInt int
func (x MyInt) MultiplyBy10() int {
if x <= 0 {
return int(x)
}
return int(x) * 10
}
Örneğin kullanımı:
val := MyInt(10)
fmt.Println(val.MultiplyBy10())
Örneğin çıktısı:
100
Yukarıdaki örnekte MyInt adında arka planda integer tipine sahip özel bir tip kullandık. Bu tür kullanımları
uygulamalarda bolca kullanmak gerekse de, metotların en yaygın kullanılacağı nesne tipi struct’dır.
Şimdi en temel haliyle bir metodun struct nesnesiyle nasıl kullanılacağına bir bakalım.
Örnek:
type T struct {
name string
}
func (x T) PrintName() {
fmt.Println(x.name)
}
Yukarıdaki örneği main() fonksiyonu içerisinde kullanalım.
t := T{name: "Cihan"}
t.PrintName()
Örnek çıktısı:
Cihan
Bir metodun struct ya da diğer tipler ile kullanımı arasında herhangi bir fark yoktur. Hazırladığımız metot,
kendisine alıcı olarak atadığımız tipe ait bir metot olarak çalışmak üzere tasarlanmıştır.
Metotlar konusunda daha birçok örnek yapabiliriz. Ancak kitap içerisinde metot nesnelerini sık sık kullanacağız.
Bu nedenle, metotlar bölümünde özellikle metotları bir uygulama ile açıklamak daha faydalı olabilir.
cihanozhan.com
Örnek:
Bu örnekte, gerçek bir uygulama geliştirirken karşılaşabileceğimiz bir senaryoyu uygulayacağız. Elimizde
kullanıcı ve bu kullanıcılara ait ilgi alanlarıyla birlikte, her kullanıcının sistem üzerinden elde ettiği kazanç gibi
veriler mevcut. Bu kullanıcı verisini metot yapısına uygun şekilde kullanacağız.
Not : Bu uygulama örneğinde fmt ve time paketlerini kullanacağız.
Kullanıcı verisini tutacak User nesnesinden önce, bu nesne ile ilişkili olacak alt struct nesnelerini oluşturalım.
Kullanıcının kazanç bilgilerinin özetini tutmak için RevenueSummary struct’ı:
type RevenueSummary struct {
ID int
TotalRevenue float64
TotalSales int
}
Kullanıcının ilgi alanlarını tutmak için Interest struct’ı:
type Interest struct {
ID int
Name string
}
Kullanıcının temel bilgilerini tutmak için User struct’ı:
type User struct {
ID int
FirstName string
LastName string
UserName string
DoB string
RevenueSummary *RevenueSummary
Interests []Interest
}
Uygulamanın temel struct yapısı hazır. Şimdi uygulamanın çalışması için gereken metotları hazırlayabiliriz.
Kullanıcının ad ve soyad bilgilerini birleştirerek getiren GetFullName() metodu:
func (u User) GetFullName() string {
return u.FirstName + " " + u.LastName
}
Kullanıcının kullanıcı adı bilgisini getiren GetUserName() metodu:
func (u *User) GetUserName() string {
return u.UserName
}
Kullanıcının gelir özet verisini bir RevenueSummary struct’ı olarak getirecek GetRevenueSummary()
metodu:
cihanozhan.com
func (u *User) GetRevenueSummary() RevenueSummary {
return *u.RevenueSummary
}
Kullanıcının doğum tarihine göre yaşını hesaplayarak getirecek GetAge() metodu:
func (u *User) GetAge() int {
t, _ := time.Parse("2006-01-02", u.DoB)
age := time.Now().Year() - t.Year()
return age
}
Kullanıcının ilgi alanlarını bir Interest struct listesi olarak getirecek GetInterests() metodu:
func (u *User) GetInterests() []Interest {
return u.Interests
}
Kullanıcının ilgi alanlarının toplamını hesaplayarak getirecek GetCountOfInterests() metodu:
func (u *User) GetCountOfInterests() int {
return len(u.Interests)
}
Uygulama için gerekli tüm nesneler hazır. Şuan tek eksiğimiz uygulama içerisinde kullanacağımız örnek bir
kullanıcı verisini tutan demo veri yapısıdır. Bunun için de getData() adında ayrı bir fonksiyon tanımlayacağız.
func getData() User {
user := &User{
ID: 1,
FirstName: "Cihan",
LastName: "Özhan",
UserName: "CihanOzhan",
DoB: "1988-01-09",
RevenueSummary: &RevenueSummary{
TotalRevenue: 5300,
TotalSales: 54,
},
Interests: []Interest{
Interest{2, "Databases"},
Interest{5, "Web Programming"},
},
}
return *user
}
Not : getData() nesnesi bir metot değil fonksiyondur.
Artık main() fonksiyonuna geçerek bu nesneleri kullanabiliriz.
// Örnek kullanıcı verisini u adındaki değişkene atayalım.
cihanozhan.com
u := getData()
fmt.Println("Temel Bilgiler:")
fmt.Println("-> Kullanıcının Tam Adı: ", u.GetFullName())
fmt.Println("-> Kullanıcı Adı: ", u.GetUserName())
fmt.Println("-> Yaş: ", strconv.Itoa(u.GetAge())+"n")
fmt.Println("İlgi Alanları: ")
for _, v := range u.GetInterests() {
fmt.Println("--> İlgi Alanı: ", v.Name)
}
countOfInterests := strconv.Itoa(u.GetCountOfInterests())
str := u.FirstName + "'ın İlgi Alanlarının Toplamı: " + countOfInterests
fmt.Println(countAllStr + "n")
fmt.Println("Kazanç Bilgileri:")
totalRev := u.GetRevenueSummary()
fmt.Println("-> Toplam Kazanç: ", totalRev.TotalRevenue)
fmt.Println("-> Toplam Satış Adedi: ", totalRev.TotalSales)
Örnek çıktısı:
Temel Bilgiler:
-> Kullanıcının Tam Adı: Cihan Özhan
-> Kullanıcı Adı: CihanOzhan
-> Yaş: 30
İlgi Alanları:
--> İlgi Alanı: Databases
--> İlgi Alanı: Web Programming
Cihan'ın İlgi Alanlarının Toplamı: 2
Kazanç Bilgileri:
-> Toplam Kazanç: 5300
-> Toplam Satış Adedi: 54
Bir konuya dikkatinizi çekmek istiyorum! GetFullName() metodundaki alıcı tanımlama alanında işaretçi(*)
simgesini kullanmadım. Ancak diğer tüm metotlarda alıcı olarak işaretçi kullandım. Muhtemelen bu konu
dikkatli okuyucuların gözüne çarpmıştır. Şimdi bir metot alıcısı olarak işaretçi kullanıp kullanmama konusunu
inceleyelim.
Metot Alıcısı Olarak İşaretçi Kullanımı
İşaretçiler için küçük bir hatırlatma yapalım. İşaretçiler doğrudan nesne örneğinin hafızadaki adresini temsil
ederler. Bu nedenle, eğer bir işaretçi üzerinde işlem yaparsanız, bu işaretçi hangi nesne örneği tarafından temsil
edilirse edilsin, orijinal veri üzerinde değişiklik yapacaktır.
Metotlar ile işaretçi kullanımını incelemek için önceki uygulamaya bir ekleme yapacağız. Bu eklemeyi
kullanıcının adını değiştirmek amacıyla SetUserName(…) metodu ile yapacağız.
Örnek: İşaretçisiz kullanım
func (u User) SetUserName(uName string) {
u.UserName = uName
}
cihanozhan.com
Oluşturduğumuz bu yeni metodu kullanalım:
u.SetUserName("CO")
fmt.Println(u.GetUserName())
Örnek çıktısı:
CihanOzhan
Kullanıcı adını değiştirme metodunu kullandık ancak kullanıcı adımız değişmedi! Bu durum, bizim gerçek
anlamda o anki kullanıcının kopyası üzerinde bu işlemi yapmamış olmamızdan kaynaklanmaktadır. Eğer o anki
nesne örneğinde bulunan kullanıcı üzerinde bu işlemin yapılmasını istiyorsak, işaretçi ile tanımlı alıcı
kullanmalıyız.
Şimdi SetUserName(..) fonksiyonunu işaretçi alıcısı alacak şekilde yeniden düzenleyip örneği tekrar
inceleyelim.
Örnek: İşaretçi kullanımı
func (u *User) SetUserName(uName string) {
u.UserName = uName
}
Oluşturduğumuz bu yeni metodu kullanalım:
u.SetUserName("CO")
fmt.Println(u.GetUserName())
Örnek çıktısı:
CO
Go ile uygulama geliştirirken en sık kullanılacak nesnelerden biri işaretçilerdir. Bu nedenle struct ile işaretçilerin
nasıl çalıştığını doğru şekilde anlamak gerekmektedir. Eğer metodun alıcısı olduğu nesnenin örneği üzerinde bir
değişiklik yapmak isterseniz işaretçi kullanmalısınız.
cihanozhan.com
Arayüzler
…
Yapılar
Bu bölümde, Go dilinin mevcut programlama dillerinden farklı olan bir bakış açısını daha inceleyeceğiz. Şu ana
kadar kullandığımız programlama dillerinin çoğunda kullanılan temel bir özellik vardı: class(sınıf)
Nesne modelleri oluşturmak için kullandığımız class kavramı Go dilinde bulunmaz. Onun yerine, aynı amaç için
tasarlanan struct(yapı) kullanılır. Yapılar, kullanıcı tanımlı tiplerdir ve genellikle yapısı belli olan(olmayabilir
de) verileri nesnel ortamda temsil etmek için çeşitli field(alan)’lardan oluşan nesnelerdir. Go dilinde
kullandığımız struct(yapı) yapısının diğer dillerdeki class yapılarından bazı farklılıkları da vardır. Şimdi yapı
nesnelerinin tüm özelliklerini detaylıca inceleyelim.
Not : Yapı nesnesi Go ile profesyonel uygulamalar geliştirmek için kullanılan en temel özelliklerden biridir. Bu
nedenle, yapıların kullanımları ve detayları üzerine birçok uygulama çalışması yapacağız.
Not : struct nesnesinin Türkçe’deki karşılığı yapı’dır.
Yapı Tanımlamak ve İlklendirmek
Yeni bir yapı tanımlamak için type anahtar kelimesini kullanıyoruz. En temel haliyle bir yapı aşağıdaki gibi
tanımlanabilir:
Örnek:
type Article struct {
id int
title string
content string
resourceURL string
authorID int
categoryID int
}
Yukarıdaki yapı modelinde bir makale yapısını programatik olarak tasarladık. Herhangi bir makalenin veritabanı
işlemlerini gerçekleştirmek ve makale verilerini uygulamanın hafızasında anlamlı şekilde yönetebilmek için
böyle bir yapıya ihtiyacımız vardı.
Aynı veri tiplerine sahip yapı alanlarını tek satırda da tanımlayabiliriz:
Örnek:
type Article struct {
id, authorID, categoryID int
title, content, resourceURL string
}
Not : Tek satırda yapı alanları tanımlama yöntemini uygularken dikkatli olunmalıdır! Bu kullanımın amacı, kod
kalabalığını azaltmaktır. Ancak, çok fazla alana(field) sahip yapılarda her veri tipine düşen alan adedinin fazla
cihanozhan.com
olması nedeniyle, kodun okunabilirliği azalabilir. Bu tür durumlardan kaçınmak için, yazılım geliştirme
standartlarına uyulmalı ve kendi uygulama kurallarınızı oluşturarak bunlara bağlı kalmalısınız.
Peki bir yapının başlangıç(sıfır) değeri nedir?
Bir yapının nesne örneğini oluşturduğumuzda yapıya ait alanların da hafıza üzerinde nesne kurulumları yapılır.
Yapıya ait nesne alanları kendi tiplerinin başlangıç değerlerine(sıfır değeri) sahip olurlar.
Örnek:
var art3 Article // sıfır değeri oluşturulmuş nesne
fmt.Println("art3", art3)
Örnek çıktısı:
art3 {0 0 0}
Yukarıdaki sonuçta görüldüğü üzere, integer bir alanın başlangıç değeri sıfır(0), string alanın ise boşluk olarak
tanımlanır. Aynı durum diğer veri tipleri için de geçerlidir.
Eğer oluşturduğumuz art3 nesne örneğinin metinsel alanlarına veri girişi yaparsak, integer alanların başlangıç
değerleri korunmakla birlikte, metinsel alanlarda yeni girilen veriler tutulacaktır.
Örnek:
var art3 Article // sıfır değeri oluşturulmuş nesne
art3.content = "Bir makale içeriği."
art3.resourceURL = www.cihanozhan.com
fmt.Println("Article 3", art3)
Örnek çıktısı:
Article 3 {0 Bir makale içeriği. www.cihanozhan.com 0 0}
Nesne ilklendirme ve başlangıç değerlerini incelediğimize göre, artık başlangıç değerleri atayarak nesne örneği
oluşturmak için birkaç örnek inceleyebiliriz.
Örnek:
// Alan isimlerini kullanarak yapı ilklendirmek
art1 := Article{
title: "Go ile XML Veriyi JSON'a Dönüştürme",
content: "Bu örneğimizde bir XML formatlı veriyi JSON …",
resourceURL: "cihanozhan.com/go-ile-xml-veriyi-jsona-donusturme/",
authorID: 7,
categoryID: 9,
}
// Alan isimlerini kullanmadan yapı ilklendirmek
art2 := Article{
0, // id alanı!
"Go ile Şifreleme İşlemleri",
"Bu makalede Go işe şifreleme yapmayı ve ilgili paketleri …",
"cihanozhan.com/category/golang/",
cihanozhan.com
7,
9,
}
Article isimli yapının art1 nesne örneğinde kullandığımız yöntem Go topluluğu tarafından en çok tercih
edilen yöntemdir. Ancak bazı durumlarda daha pratik ve isimlendirme gerekmeyen kullanım yöntemini
kullanmak gerekebilir. Bu durumda da art2 nesne örneğinin yöntemi uygulanabilir. art2’de dikkat ederseniz, id
alanını da göndermek zorunda kaldık! Bu kullanım yönteminde herhangi bir alan adı vermediğimiz için tüm
alanları göndermek zorundayız. Aksi halde, Go derleyicisi hangi verinin hangi alana ait olduğunu
anlayamayacağı için hata verecektir.
Uygulamanın çıktısını görmek için:
fmt.Println("Makale 1: ", art1)
fmt.Println("Makale 2: ", art2)
Bir Yapının Alanlarına Erişmek
Bir yapıya ait alanlara erişmek için nokta(.) operatörü kullanılır. Bu konuyu örneklendirmek için daha önce
oluşturduğumuz art1 isimli Article nesne örneğini kullanacağız.
Örnek:
art1 := Article{
title: "Go ile XML Veriyi JSON'a Dönüştürme",
content: "Bu örneğimizde bir XML formatlı veriyi JSON …",
resourceURL: "cihanozhan.com/go-ile-xml-veriyi-jsona-donusturme/",
authorID: 7,
categoryID: 9,
}
fmt.Println("ID: ", art1.id)
fmt.Println("Title: ", art1.title)
fmt.Println("Content: ", art1.content)
fmt.Println("Resource: ", art1.resourceURL)
fmt.Println("AuthorID: ", art1.authorID)
fmt.Println("CategoryID: ", art1.categoryID)
Örnek çıktısı:
ID: 0
Title: Go ile XML Veriyi JSON'a Dönüştürme
Content: Bu örneğimizde bir XML formatlı veriyi JSON …
Resource: http://www.cihanozhan.com/go-ile-xml-veriyi-jsona-donusturme/
AuthorID: 7
CategoryID: 9
Şu ana kadar oluşturduğumuz Article örneğindeki yapıya named structure(isimlendirilmiş yapı) diyoruz. Bir
diğer yapı oluşturma yöntemiyse anonymous struct(anonim yapı) yaklaşımıdır. Şimdi ona bakalım…
cihanozhan.com
Yapılarda Erişim Belirleyicileri
Eğer C# ya da Java programlama gibi bir tecrübeniz var ise, bu dillerdeki erişim belirleyicilerine(public, private,
protected vb.) dokunmadan orta ya da büyük çaplı bir proje tasarlayamayacağınızı biliyor olmalısınız. Bu tür
tanımlamalar, ilgili nesnenin erişim sınırlarını belirler.
Peki, bir Go paketi içerisinde tanımladığımız herhangi bir nesnenin o paketin dışından erişilebilir olup
olmadığını nasıl kontrol edeceğiz?
Go dili herhangi bir erişim tanımlayıcı anahtar kelime(public, private vb.) kullanmaz. Go derleyicisi sadece
nesnenin baş harfinin büyük ya da küçük olma durumuna göre erişimleri belirler.
- Go paketinde tanımladığınız nesnenin baş harfi büyük ise bu nesneye paketin dışından erişilebilir.
- Go paketinde tanımladığınız nesnenin baş harfi küçük ise bu nesneye paketin dışından erişilemez.
Go dilinin erişim belirleme kuralı sadece aşağıdaki nesneler için değil, paket içerisindeki tüm nesneler için
geçerlidir. Aşağıdakiler sadece örnektir.
Değişken:
- Public :
o var Age int
- private :
o var age int
Yapı:
- Public :
o type User struct { }
- private :
o type user struct { }
Metot:
- Public:
o func IsValid() bool
- private :
o func isValid() bool
Bu kurala göre, eğer oluşturduğunuz bir yapı nesnesinin paket dışından erişilebilir olmasını istiyorsanız, yapı
nesnesinin adı büyük harfle başmalıdır. Aynı şekilde, eğer bir yapı nesnesinin içindeki alanların dışarıdan
erişilebilir olmasını istiyorsanız, bu alanların da baş harfi büyük harfle başlamalıdır.
Anonim Yapı Tanımlamak ve İlklendirmek
Article isimli yapı nesnesinden görüldüğü üzere yeni yapı tanımlama işlemlerinde type anahtar kelimesini
kullanıyoruz. Ancak var ile de değişken tanımlar gibi yeni bir yapı tanımlaması yapabiliriz. Bu yaklaşıma da
anonim yapı(anonymous structure) diyoruz.
Örnek:
var article struct {
id, authorID, categoryID int
title, content string
}
cihanozhan.com
Yapı ile Pointer Kullanımı
Pointer kullanımı ile ilgili bölümde Go dilindeki işaretçi yapısını ve kullanımını örneklemiştik. Birçok
programlama dilinin aksine, Go dilinde işaretçilerin kullanımı çok yaygındır. Go dilinde işaretçiler özellikle yapı
ve metot/fonksiyon gibi nesnelerde çok sık kullanılır.
Şimdi bir yapı ile pointer aracılığıyla nasıl oynayacağımıza bakalım.
Örnek:
// Alanları veri tipine göre gruplayarak yeni bir yapı oluşturduk
type Article struct {
id int
title, content, resourceURL string
authorID, categoryID int
}
article := &Article{
0,
"Makale Başlığı",
"Makale İçeriği",
"www.cihanozhan.com",
7,
9,
}
fmt.Println("Başlık: ", (*article).title)
fmt.Println("İçerik: ", (*article).content)
fmt.Println("Resource: ", (*article).resourceURL)
Örnek çıktısı:
Başlık: Makale Başlığı
İçerik: Makale İçeriği
Resource: www.cihanozhan.com
Go dilinde pointer kullanımının yaygın olmasının nedenlerinden bazıları performans ve kaynak yönetimidir.
Yoğun nesneye sahip ve veri işlemleri gerektiren projelerde, gerçekleştirilen her işlem bilgisayar belleğini ciddi
miktarda tüketir. Bunun nedenlerinden biri, kullanılan değer tipindeki nesnelerdir. Her işlem için on binlerce
değer tipinin tekrar tekrar oluşturulması ve bir diğerine kopyalanması nedeniyle hafızada gereksiz bir yük oluşur.
Ancak işaretçi kullanıldığı takdirde, hafızadaki bu nesnelerin kopyaları alınmaz, sadece hafızadaki adresleri
diğer değişkene gönderilerek aynı hafıza adresi ve alanı üzerinde değişiklik yapılması sağlanır.
Uygulama Ödevi : Yukarıdaki yapı ile pointer kullanımı örneğini, pointer kullanmadan tekrar yazın ve sonucu
gözlemleyin.
cihanozhan.com
Anonim Alanlar
Go dilinde bir yapı içerisinde alanın adını belirtmeden sadece veri tipini belirterek yapı oluşturmak mümkündür.
Bu yaklaşıma anonim alanlar(anonymous fields) diyoruz.
Örnek:
type Book struct {
string // kitap başlıı
int // kitap fiyatı
}
Versiyon 1:
book := Book{"İleri Seviye T-SQL Programlama", 45}
fmt.Println(book)
Versiyon 2:
var book Book
book.int = 45
book.string = "İleri Seviye T-SQL Programlama"
Örnek çıktısı:
{İleri Seviye T-SQL Programlama 45}
Bu kitabın fiyatını artıralım.
book.int += 1
fmt.Println("Kitap Fiyatı: ", book.int)
Anonim alanlar ilginç bir dil özelliğidir! Veri tipi olarak tanımladığımız int, string ya da diğer veri tipleri
anonim alanlar içerisinde kullanıldığında aynı zamanda bir alan adı olarak kullanılırlar. Bu nedenle bir veri tipini
sadece bir kez tanımlayabilirsiniz.
Yapıcı Metotlar
Bir nesnenin örneğini oluşturma işlemi varsayılan olarak derleyici tarafından yürütülen bir süreçtir. Ancak
programcı olarak istersek bu sürece müdahale ederek kendi nesne örneği oluşturma politikamızı uygulayabiliriz.
Bu işlemi yapabilmemizi sağlayan nesnelere de yapıcı metotlar diyoruz.
Go dili C# ve Java gibi varsayılan olarak yapıcı metotları desteklemez. Yani bunun için özel bir metot tanımlama
yöntemi yoktur. Bu bir eksiklik değildir! Aksine, Go dili tasarımcılarına göre yapıcı metot(constructor) gereksiz
bir özelliktir. Çünkü bu işlemi normal metotları kullanarak zaten yapabilmekteyiz. Tıpkı Go dilinde olduğu
gibi...
Şimdi Go dilindeki yapıcı metot yaklaşımını kavrayabilmek için bir örnek yapalım.
cihanozhan.com
Örnek:
type Human struct {
FirstName string
LastName string
Age int
}
// Yapıcı metot olarak kullanılacak metodumuz
func NewHuman(firstName, lastName string) *Human {
human := new(Human)
human.FirstName = firstName
human.LastName = lastName
return human
}
Şimdi bu yapı ve yapıcı metodumuzu kullanalım.
Temel işlemler için belirtmek gerekirse, bir yapının nesne örneğini oluşturmak için yapıcı metotlara ihtiyacımız
yoktur. Ancak profesyonel programlama ve nesne yönelimli programlama yaklaşımları gereği, yapıcı metotların
kullanılması gereken durumların sayısı hiç de az değildir.
Daha önceki yapı oluşturma yöntemlerinde de kullandığımız “ nesne örneğini inşa etme işini derleyiciye
bırakmak” yöntemine tekrar bir göz atalım.
Versiyon 1:
human1 := Human{FirstName: "Cihan"}
human4 := Human{"Cihan", "Özhan", 30}
human5 := Human{FirstName: "Cihan", Age: 30}
fmt.Println(human1.FirstName)
Bu yöntemde nesne örneğinin oluşturulma sürecine aktif olarak müdahale etmedik. Biz sadece başlangıçta
FirstName alanına bir değer ataması yaptık. Bu sayede derleyici arka planda Human nesnesi için bir yapıcı
metot oluşturarak bu nesnenin örneğini oluşturarak bize geri gönderdi. Biz de bu nesne örneğini human1
adındaki değişkenimize atadık.
Versiyon 1 çıktısı:
Cihan
Versiyon 2: new() ile nesne örneği oluşturmak
new() ile nesne örneği oluşturma işlemi, sadece yapılar için değil, daha birçok nesnede kullanılabilen bir
yöntemdir.
Dikkat: NewHuman() isimli yapıcı metot içerisinde de zaten new()’i kullanarak nesnenin yeni bir örneğini
oluşturuyoruz.
human2 := new(Human)
fmt.Println(human2.Age)
Versiyon 2 çıktısı:
0 // Herhangi bir değer ataması yapılmadığı için, sadece integer’ın varsayılan değerini alırız.
cihanozhan.com
Versiyon 3: NewHuman() fonksiyonunu ile nesne örneği oluşturmak
Human yapısı için oluşturduğumuz NewHuman() isimli fonksiyonu aslında bir yapıcı metot olması amacıyla
oluşturduk. Bu metodun kullanımının diğer Go metotlarından herhangi bir farkı yoktur.
human3 := NewHuman("Cihan", "Özhan")
human3.Age = 30
fmt.Println(human3)
Versiyon 3 çıktısı:
&{Cihan Özhan 30}
Yukarıda görüldüğü gibi, Go dilinde bir yapının nesne örneğini oluşturmanın birçok yöntemi vardır.
İç İçe Yapı Kullanımı
Mantıksal olarak birbiriyle ilişkili olan farklı yapıları birbiri içerisinde bir alan olarak kullanabiliriz. Bu yönteme
iç içe yapı kullanımı denir.
Bu gereksinime bir örnek vermek gerekirse, User adında bir yapı ile kullanıcı verisini yönettiğimizi düşünelim.
Bu kullanıcılara ait ilgi alanlarını da, Interest adında oluşturduğumuz yapı ile yönetiyoruz. Bu durumda, bir
kullanıcının birden fazla ilgi alanı olabileceğine göre, Interest dizisi([]Interest) alan Interests adında bir alanı
User yapı nesnesine ekleyebiliriz.
Şimdi farklı bir senaryo ile iç içe yapı kullanımını inceleyeceğiz.
Örnek:
Bu örnekte kullanılacak Go paketleri: fmt, strconv
Daha sonra oluşturacağımız kullanıcının ödemelerini tutan bir yapı oluşturalım.
type Payment struct {
Salary float64
Bonus float64
}
Payment yapısının nesne örneğini oluşturmak için kullanacağımız yapıcı metodu oluşturalım.
func NewPayment() *Payment {
p := new(Payment)
return p
}
Bu uygulamanın ana nesnesi olmak üzere User yapısını oluşturalım.
type User struct {
ID int
FirstName string
LastName string
UserName string
Age int
Pay *Payment // İç içe struct tanımladık!
cihanozhan.com
}
Buraya dikkat! User yapısı içerisinde başka bir Payment yapısını bir alana veri tipi olarak atadık. İşte iç içe
yapı kullanımı bu şekilde tanımlanmaktadır.
User yapı nesnesi için bir yapıcı metot oluşturalım.
func NewUser() *User {
u := new(User)
u.Pay = NewPayment()
return u
}
Uygulamada kullanacağımız yapı ve yapıcı metotları hazırladık. Şimdi bu yapılar ile birlikte kullanacağımız
fonksiyonları oluşturalım.
Kullanıcının ad ve soyadını birleştirerek geriye dönen GetFullName() fonksiyonun:
func (u User) GetFullName() string {
return u.FirstName + " " + u.LastName
}
Kullanıcının kullanıcı adını dönen fonksiyon:
func (u *User) GetUserName() string {
return u.UserName
}
Kullanıcının “maaş + bonus” algoritmasına göre, kullanıcının elde ettiği aylık kazanımımı geri döner.
func (u *User) GetPayment() float64 {
pay := u.Pay.Salary + u.Pay.Bonus
return pay
}
Şu ana kadar kullanacağımız genel uygulama yapısı hazır. Şimdi bu uygulamayı main() metodu içerisinde
kullanarak inceleyelim.
Versiyon 1:
fmt.Println("Kullanıcı oluşturma v1")
u1 := &User{
ID: 1,
FirstName: "Cihan",
LastName: "Özhan",
UserName: "CihanOzhan",
Age: 30,
Pay: &Payment{
Salary: 2550,
Bonus: 700,
},
cihanozhan.com
}
fmt.Println(u1.Pay)
fmt.Println(u1.GetUserName())
fmt.Println(u1.GetFullName())
fmt.Println("Maaş: " + strconv.FormatFloat(u1.GetPayment(), 'g', -1, 64))
// Not : Maaşı gösterebilmek için float64 veri tipini string'e dönüştürdük.
Versiyon 1 çıktısı:
Kullanıcı oluşturma v1
&{2550 700}
CihanOzhan
Cihan Özhan
Maaş : 3250
Versiyon 1 ile sık kullanılan bir yöntemi inceledik. Bu kodlama tarzını profesyonel Go uygulamalarında sık sık
görebilirsiniz.
Şimdi de farklı bir modelle aynı işlemleri gerçekleştirelim.
Versiyon 2:
fmt.Println("Kullanıcı oluşturma v2")
u2 := NewUser()
u2.FirstName = "Cihan"
u2.LastName = "Özhan"
u2.Age = 30
u2.UserName = "Gopher"
Versiyon 2 için Payment nesnesini oluşturma yöntemi 1:
u2.Pay.Salary = 5600
u2.Pay.Bonus = 580
Versiyon 2 için Payment nesnesini oluşturma yöntemi 2:
u2.Pay = &Payment{Salary: 5600, Bonus: 580}
Ve artık hazır olan u2 nesne örneğini kullanabiliriz.
fmt.Println(u2.Pay)
fmt.Println(u2.GetUserName())
fmt.Println(u2.GetFullName())
fmt.Println("Maaş : " + strconv.FormatFloat(u2.GetPayment(), 'g', -1, 64))
// Not : Maaşı gösterebilmek için float64 veri tipini string'e dönüştürdük.
cihanozhan.com
Versiyon 2 çıktısı:
Kullanıcı oluşturma v2
&{5600 580}
GODER
Cihan Özhan
Maaş : 6180
cihanozhan.com

More Related Content

What's hot

Introduction to Serverless
Introduction to ServerlessIntroduction to Serverless
Introduction to Serverless
Amazon Web Services
 
Flask
FlaskFlask
Yaml
YamlYaml
[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨
[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨
[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨
Amazon Web Services Korea
 
Cracking Pseudorandom Sequences Generators in Java Applications
Cracking Pseudorandom Sequences Generators in Java ApplicationsCracking Pseudorandom Sequences Generators in Java Applications
Cracking Pseudorandom Sequences Generators in Java Applications
Positive Hack Days
 
AWS Serverless Introduction (Lambda)
AWS Serverless Introduction (Lambda)AWS Serverless Introduction (Lambda)
AWS Serverless Introduction (Lambda)
Ashish Kushwaha
 
Serverless Architecture and Best Practices
Serverless Architecture and Best PracticesServerless Architecture and Best Practices
Serverless Architecture and Best Practices
Amazon Web Services
 
Golang - Overview of Go (golang) Language
Golang - Overview of Go (golang) LanguageGolang - Overview of Go (golang) Language
Golang - Overview of Go (golang) Language
Aniruddha Chakrabarti
 
Introduction to Python Celery
Introduction to Python CeleryIntroduction to Python Celery
Introduction to Python Celery
Mahendra M
 
[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육
[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육
[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육
Ji-Woong Choi
 
Serverless Computing: build and run applications without thinking about servers
Serverless Computing: build and run applications without thinking about serversServerless Computing: build and run applications without thinking about servers
Serverless Computing: build and run applications without thinking about servers
Amazon Web Services
 
Fargate 를 이용한 ECS with VPC 1부
Fargate 를 이용한 ECS with VPC 1부Fargate 를 이용한 ECS with VPC 1부
Fargate 를 이용한 ECS with VPC 1부
Hyun-Mook Choi
 
ONNX - The Lingua Franca of Deep Learning
ONNX - The Lingua Franca of Deep LearningONNX - The Lingua Franca of Deep Learning
ONNX - The Lingua Franca of Deep Learning
Hagay Lupesko
 
Test your microservices with REST-Assured
Test your microservices with REST-AssuredTest your microservices with REST-Assured
Test your microservices with REST-Assured
Michel Schudel
 
Exploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservicesExploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservices
💡 Tomasz Kogut
 
Serverless Framework Intro
Serverless Framework IntroServerless Framework Intro
Serverless Framework Intro
Nikolaus Graf
 
Azure aws비교
Azure aws비교Azure aws비교
Azure aws비교
Youshin Kim
 
Python Programlama Dili Eğitimi
Python Programlama Dili EğitimiPython Programlama Dili Eğitimi
Python Programlama Dili Eğitimi
Enes Ateş
 
Laravel로 스타트업 기술 스택 구성하기
Laravel로 스타트업 기술 스택 구성하기Laravel로 스타트업 기술 스택 구성하기
Laravel로 스타트업 기술 스택 구성하기
KwangSeob Jeong
 
Go Programming Language (Golang)
Go Programming Language (Golang)Go Programming Language (Golang)
Go Programming Language (Golang)
Ishin Vin
 

What's hot (20)

Introduction to Serverless
Introduction to ServerlessIntroduction to Serverless
Introduction to Serverless
 
Flask
FlaskFlask
Flask
 
Yaml
YamlYaml
Yaml
 
[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨
[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨
[Gaming on AWS] AWS에서 실시간 멀티플레이 게임 구현하기 - 넥슨
 
Cracking Pseudorandom Sequences Generators in Java Applications
Cracking Pseudorandom Sequences Generators in Java ApplicationsCracking Pseudorandom Sequences Generators in Java Applications
Cracking Pseudorandom Sequences Generators in Java Applications
 
AWS Serverless Introduction (Lambda)
AWS Serverless Introduction (Lambda)AWS Serverless Introduction (Lambda)
AWS Serverless Introduction (Lambda)
 
Serverless Architecture and Best Practices
Serverless Architecture and Best PracticesServerless Architecture and Best Practices
Serverless Architecture and Best Practices
 
Golang - Overview of Go (golang) Language
Golang - Overview of Go (golang) LanguageGolang - Overview of Go (golang) Language
Golang - Overview of Go (golang) Language
 
Introduction to Python Celery
Introduction to Python CeleryIntroduction to Python Celery
Introduction to Python Celery
 
[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육
[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육
[오픈소스컨설팅] Ansible을 활용한 운영 자동화 교육
 
Serverless Computing: build and run applications without thinking about servers
Serverless Computing: build and run applications without thinking about serversServerless Computing: build and run applications without thinking about servers
Serverless Computing: build and run applications without thinking about servers
 
Fargate 를 이용한 ECS with VPC 1부
Fargate 를 이용한 ECS with VPC 1부Fargate 를 이용한 ECS with VPC 1부
Fargate 를 이용한 ECS with VPC 1부
 
ONNX - The Lingua Franca of Deep Learning
ONNX - The Lingua Franca of Deep LearningONNX - The Lingua Franca of Deep Learning
ONNX - The Lingua Franca of Deep Learning
 
Test your microservices with REST-Assured
Test your microservices with REST-AssuredTest your microservices with REST-Assured
Test your microservices with REST-Assured
 
Exploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservicesExploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservices
 
Serverless Framework Intro
Serverless Framework IntroServerless Framework Intro
Serverless Framework Intro
 
Azure aws비교
Azure aws비교Azure aws비교
Azure aws비교
 
Python Programlama Dili Eğitimi
Python Programlama Dili EğitimiPython Programlama Dili Eğitimi
Python Programlama Dili Eğitimi
 
Laravel로 스타트업 기술 스택 구성하기
Laravel로 스타트업 기술 스택 구성하기Laravel로 스타트업 기술 스택 구성하기
Laravel로 스타트업 기술 스택 구성하기
 
Go Programming Language (Golang)
Go Programming Language (Golang)Go Programming Language (Golang)
Go Programming Language (Golang)
 

Similar to Go Book - Fonksiyonlar, Metotlar, Arayüzler ve Yapılar

Ileri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer KoculuIleri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer Koculu
mustafa sarac
 
C dilindeki strtok, remove, rename ve system fonksiyonları.
C dilindeki strtok, remove, rename ve system fonksiyonları. C dilindeki strtok, remove, rename ve system fonksiyonları.
C dilindeki strtok, remove, rename ve system fonksiyonları.
MehmetKelepce
 
Programlama Temelleri Hazır Metodlar
Programlama Temelleri Hazır MetodlarProgramlama Temelleri Hazır Metodlar
Programlama Temelleri Hazır Metodlar
kadirolmez
 
Fonksiyonlarpowerpoint
FonksiyonlarpowerpointFonksiyonlarpowerpoint
Fonksiyonlarpowerpoint
Osman Sabır
 
Bas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamamiBas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamami
muratgulenc
 
LR0 Parser Proje Raporu
LR0 Parser Proje RaporuLR0 Parser Proje Raporu
LR0 Parser Proje Raporu
Mustafa Cantürk
 
İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14
Cihan Özhan
 
php nin yapı taşları
php nin yapı taşlarıphp nin yapı taşları
php nin yapı taşları
forummsn
 
Ders1-Deği̇şkenler-C#
Ders1-Deği̇şkenler-C#Ders1-Deği̇şkenler-C#
Ders1-Deği̇şkenler-C#
Nezahat Kara COŞKUN
 

Similar to Go Book - Fonksiyonlar, Metotlar, Arayüzler ve Yapılar (11)

Ileri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer KoculuIleri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer Koculu
 
Ite
IteIte
Ite
 
C dilindeki strtok, remove, rename ve system fonksiyonları.
C dilindeki strtok, remove, rename ve system fonksiyonları. C dilindeki strtok, remove, rename ve system fonksiyonları.
C dilindeki strtok, remove, rename ve system fonksiyonları.
 
Programlama Temelleri Hazır Metodlar
Programlama Temelleri Hazır MetodlarProgramlama Temelleri Hazır Metodlar
Programlama Temelleri Hazır Metodlar
 
Fonksiyonlarpowerpoint
FonksiyonlarpowerpointFonksiyonlarpowerpoint
Fonksiyonlarpowerpoint
 
Bas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamamiBas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamami
 
LR0 Parser Proje Raporu
LR0 Parser Proje RaporuLR0 Parser Proje Raporu
LR0 Parser Proje Raporu
 
İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14
 
php nin yapı taşları
php nin yapı taşlarıphp nin yapı taşları
php nin yapı taşları
 
Ders1-Deği̇şkenler-C#
Ders1-Deği̇şkenler-C#Ders1-Deği̇şkenler-C#
Ders1-Deği̇şkenler-C#
 
Templates
Templates Templates
Templates
 

More from Cihan Özhan

MongoDB Overview
MongoDB OverviewMongoDB Overview
MongoDB Overview
Cihan Özhan
 
MongoDB - NoSQL Overview
MongoDB - NoSQL OverviewMongoDB - NoSQL Overview
MongoDB - NoSQL Overview
Cihan Özhan
 
MongoDB - JSON'a Genel Bakış
MongoDB - JSON'a Genel BakışMongoDB - JSON'a Genel Bakış
MongoDB - JSON'a Genel Bakış
Cihan Özhan
 
AI and Machine Learning - Today’s Implementation Realities
AI and Machine Learning - Today’s Implementation RealitiesAI and Machine Learning - Today’s Implementation Realities
AI and Machine Learning - Today’s Implementation Realities
Cihan Özhan
 
Mobil Uygulama Güvenliği (Mobile Security)
Mobil Uygulama Güvenliği (Mobile Security)Mobil Uygulama Güvenliği (Mobile Security)
Mobil Uygulama Güvenliği (Mobile Security)
Cihan Özhan
 
Blockchain : Decentralized Application Development (Turkish)
Blockchain : Decentralized Application Development (Turkish)Blockchain : Decentralized Application Development (Turkish)
Blockchain : Decentralized Application Development (Turkish)
Cihan Özhan
 
Golang Book - Genel Bakış
Golang Book - Genel BakışGolang Book - Genel Bakış
Golang Book - Genel Bakış
Cihan Özhan
 
Golang Book - Giriş
Golang Book - GirişGolang Book - Giriş
Golang Book - Giriş
Cihan Özhan
 
MLaaS - Presenting & Scaling Machine Learning Models as Microservices
MLaaS - Presenting & Scaling Machine Learning Models as MicroservicesMLaaS - Presenting & Scaling Machine Learning Models as Microservices
MLaaS - Presenting & Scaling Machine Learning Models as Microservices
Cihan Özhan
 
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Cihan Özhan
 
Endüstriyel Yapay Zeka ve Otonom Sistemler
Endüstriyel Yapay Zeka ve Otonom SistemlerEndüstriyel Yapay Zeka ve Otonom Sistemler
Endüstriyel Yapay Zeka ve Otonom Sistemler
Cihan Özhan
 
AI Security : Machine Learning, Deep Learning and Computer Vision Security
AI Security : Machine Learning, Deep Learning and Computer Vision SecurityAI Security : Machine Learning, Deep Learning and Computer Vision Security
AI Security : Machine Learning, Deep Learning and Computer Vision Security
Cihan Özhan
 
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Cihan Özhan
 
Python Programlama Dili
Python Programlama DiliPython Programlama Dili
Python Programlama Dili
Cihan Özhan
 
İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21
Cihan Özhan
 
İleri Seviye T-SQL Programlama - Chapter 20
İleri Seviye T-SQL Programlama - Chapter 20İleri Seviye T-SQL Programlama - Chapter 20
İleri Seviye T-SQL Programlama - Chapter 20
Cihan Özhan
 
İleri Seviye T-SQL Programlama - Chapter 19
İleri Seviye T-SQL Programlama - Chapter 19İleri Seviye T-SQL Programlama - Chapter 19
İleri Seviye T-SQL Programlama - Chapter 19
Cihan Özhan
 
İleri Seviye T-SQL Programlama - Chapter 18
İleri Seviye T-SQL Programlama - Chapter 18İleri Seviye T-SQL Programlama - Chapter 18
İleri Seviye T-SQL Programlama - Chapter 18
Cihan Özhan
 
İleri Seviye T-SQL Programlama - Chapter 17
İleri Seviye T-SQL Programlama - Chapter 17İleri Seviye T-SQL Programlama - Chapter 17
İleri Seviye T-SQL Programlama - Chapter 17
Cihan Özhan
 
İleri Seviye T-SQL Programlama - Chapter 16
İleri Seviye T-SQL Programlama - Chapter 16İleri Seviye T-SQL Programlama - Chapter 16
İleri Seviye T-SQL Programlama - Chapter 16
Cihan Özhan
 

More from Cihan Özhan (20)

MongoDB Overview
MongoDB OverviewMongoDB Overview
MongoDB Overview
 
MongoDB - NoSQL Overview
MongoDB - NoSQL OverviewMongoDB - NoSQL Overview
MongoDB - NoSQL Overview
 
MongoDB - JSON'a Genel Bakış
MongoDB - JSON'a Genel BakışMongoDB - JSON'a Genel Bakış
MongoDB - JSON'a Genel Bakış
 
AI and Machine Learning - Today’s Implementation Realities
AI and Machine Learning - Today’s Implementation RealitiesAI and Machine Learning - Today’s Implementation Realities
AI and Machine Learning - Today’s Implementation Realities
 
Mobil Uygulama Güvenliği (Mobile Security)
Mobil Uygulama Güvenliği (Mobile Security)Mobil Uygulama Güvenliği (Mobile Security)
Mobil Uygulama Güvenliği (Mobile Security)
 
Blockchain : Decentralized Application Development (Turkish)
Blockchain : Decentralized Application Development (Turkish)Blockchain : Decentralized Application Development (Turkish)
Blockchain : Decentralized Application Development (Turkish)
 
Golang Book - Genel Bakış
Golang Book - Genel BakışGolang Book - Genel Bakış
Golang Book - Genel Bakış
 
Golang Book - Giriş
Golang Book - GirişGolang Book - Giriş
Golang Book - Giriş
 
MLaaS - Presenting & Scaling Machine Learning Models as Microservices
MLaaS - Presenting & Scaling Machine Learning Models as MicroservicesMLaaS - Presenting & Scaling Machine Learning Models as Microservices
MLaaS - Presenting & Scaling Machine Learning Models as Microservices
 
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
 
Endüstriyel Yapay Zeka ve Otonom Sistemler
Endüstriyel Yapay Zeka ve Otonom SistemlerEndüstriyel Yapay Zeka ve Otonom Sistemler
Endüstriyel Yapay Zeka ve Otonom Sistemler
 
AI Security : Machine Learning, Deep Learning and Computer Vision Security
AI Security : Machine Learning, Deep Learning and Computer Vision SecurityAI Security : Machine Learning, Deep Learning and Computer Vision Security
AI Security : Machine Learning, Deep Learning and Computer Vision Security
 
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
Yapay Zeka Güvenliği : Machine Learning & Deep Learning & Computer Vision Sec...
 
Python Programlama Dili
Python Programlama DiliPython Programlama Dili
Python Programlama Dili
 
İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21
 
İleri Seviye T-SQL Programlama - Chapter 20
İleri Seviye T-SQL Programlama - Chapter 20İleri Seviye T-SQL Programlama - Chapter 20
İleri Seviye T-SQL Programlama - Chapter 20
 
İleri Seviye T-SQL Programlama - Chapter 19
İleri Seviye T-SQL Programlama - Chapter 19İleri Seviye T-SQL Programlama - Chapter 19
İleri Seviye T-SQL Programlama - Chapter 19
 
İleri Seviye T-SQL Programlama - Chapter 18
İleri Seviye T-SQL Programlama - Chapter 18İleri Seviye T-SQL Programlama - Chapter 18
İleri Seviye T-SQL Programlama - Chapter 18
 
İleri Seviye T-SQL Programlama - Chapter 17
İleri Seviye T-SQL Programlama - Chapter 17İleri Seviye T-SQL Programlama - Chapter 17
İleri Seviye T-SQL Programlama - Chapter 17
 
İleri Seviye T-SQL Programlama - Chapter 16
İleri Seviye T-SQL Programlama - Chapter 16İleri Seviye T-SQL Programlama - Chapter 16
İleri Seviye T-SQL Programlama - Chapter 16
 

Go Book - Fonksiyonlar, Metotlar, Arayüzler ve Yapılar

  • 1. Fonksiyonlar, Metotlar, Arayüzler ve Yapılar Bu bölümde programlama dünyasının en kritik parçalarından birkaçını inceleyeceğiz. İnceleyeceğimiz bu nesne tipleri profesyonel ve modern yazılımlar geliştirmenin temel taşlarını oluşturmaktadır. Fonksiyonlar Yazılım geliştirirken kod tekrarı yapmamak dikkat edilmesi gereken en önemli unsurlardan biridir. Bu nedenle belirli işler konusunda uzmanlaşmış nesneler üretmemiz gerekir. Örneğin, geliştirilen uygulama içerisinde 10 satırlık bir matematik algoritmasını her seferinde tekrar yazmak doğru bir yaklaşım mıdır? Hayır! Bu tür işlemlerin tek bir kez yazılması ve bu işlemin de sadece kendi işinde uzmanlaştırılması gerekir. İşte bu ihtiyaçlardan dolayı fonksiyon nesneleri doğmuştur. Biz her iş için ayrı ayrı fonksiyonlar geliştirir ve bunları kullanırız. Söz dizimi: func fonksiyon-ad(parametre-listesi) (geri-donus-listesi) { fonksiyon-govdesi } - Fonksiyon isimleri benzersiz olmalıdır. - Fonksiyon parametre isimleri benzersiz olmalıdır. - Bir fonksiyon geri dönüş değerlerine sahip olabilir ama bu zorunlu değildir. - Fonksiyonun geri dönüş değeri birden fazla ise parantez kullanmak zorunludur. - Geri dönüş tipine sahip bir fonksiyonun gövdesi return komutu ile bitmelidir. Fonksiyon Tanımlamaları Fonksiyonlar ile ilgili temel kural ve söz dizimini uygulamak için birkaç örnek yapalım Örnek: func topla(x int, y int) int { // isimsiz geri dönüş tipi return x + y } Örneği kullanalım. res := topla(5, 4) fmt.Println(res) Örnek çıktısı: 9 Dikkat: Eğer C# ya da Java gibi programlama dillerini kullandıysanız, Go dilindeki fonksiyonların parametre tanımlaması size garip gelmiş olabilir. Çünkü diğer dillerde parametre tanımlarken önce veri tipi, sonra parametre adı şeklinde bir parametre tanımı yapılırken, Go dilinde bu tam tersidir! Go dilinde önce parametrenin cihanozhan.com
  • 2. adını, sonra parametrenin veri tipini yazarız. Go dili tasarımcılarının bu konudaki beyanı: “Bu kullanım insan dili ve düşünce tarzına daha yakın ve doğru olduğunu düşünüyoruz.” topla isimli fonksiyonda geri dönüş veri tipi olarak integer’ı kullandık. Bu örnekte isimsiz geri dönüş yöntemini kullandık. Ancak aşağıdaki şekilde isimlendirilmiş bir geri dönüş de kullanabiliriz. func topla(x int, y int) (r int) { // isimlendirilmiş geri dönüş tipi return x + y } Geri dönüş olarak r adını kullandık. Bu örneğin ilk örnekten bir diğer farkı ise geri dönüş tipinin parantez içerisinde tanımlanmış olmasıdır. Eğer isimlendirilmiş bir geri dönüş nesnesi kullanıyorsak bunu parantez içerisinde tanımlamak zorundayız. Aynı şekilde, eğer isimsiz olarak birden fazla geri dönüş değeri tanımlayacaksak, bu değerleri gene parantez içerisinde tanımlamak zorundayız. Örnek: func topla(x int, y int) (int, int) { // Çoklu geri dönüş yapmak! return 0, x + y // İlk geri dönüşteki 0 değeri sadece bir örnektir. } Fonksiyonların herhangi bir geri dönüş tipine sahip olma zorunluluğu yoktur. Bir fonksiyonu geri dönüş tipi olmadan da aşağıdaki gibi tanımlayabiliriz. Örnek: func save(data float32) { fmt.Println("Saved: ", data) } save() fonksiyonunu kullanalım. save(4.5) Örnek çıktısı: Saved: 4.5 Sadece verilen görevi yerine getirmesi gereken ve herhangi bir geri dönüş değerine ihtiyaç duyulmayan durumlarda, geri dönüş değeri olmayan fonksiyonlar tercih edilebilir. Her türlü hesaplama ve iş kuralı için fonksiyonları kullanabiliriz. Örneğin, iki sayısal değer içerisinde en büyük olanı bulalım. Örnek: func max(number1, number2 int) int { var result int if number1 > number2 { result = number1 } else { result = number2 } return result cihanozhan.com
  • 3. } Örnekteki max fonksiyonunu kullanalım. var x, y = 30, 32 res := max(x, y) fmt.Println("Sonuç: ", res) Örnek çıktısı: Sonuç: 32 Go diline özel diğer bir geri dönüş yöntemi ise naked return geri dönüş yöntemidir. Bu yöntem isimlendirilmiş geri dönüş yöntemine benzemekle birlikte bu yöntemler birbirlerinden farklıdırlar. Örnek: func main() { xx, yy := Increase(2, 4) fmt.Println(xx) fmt.Println(yy) } func Increase(x int, y int) (num1 int, num2 int) { num1 = x + 1 num2 = y + 1 return } Örnek çıktısı: 3 5 Yukarıdaki naked return örneğine dikkat ederseniz return komutunun sonunda herhangi bir dönüş nesnesi tanımlamadık. Bu yöntemin diğerlerinden temel farkı, return deyiminin herhangi belirli bir nesneyi dönmemesidir. Bu fonksiyonun geri dönüş tiplerini tanımlarken belirttiğimiz num1 ve num2 nesneleri fonksiyonun içerisinde kullanılabilir ve değeri değiştirilebilir nesnelerdir. Bu nesnelerle işimiz bittiğinde return komutunu çalıştırarak bu nesnelerin geriye dönmesini sağlayabiliyoruz. Yukarıdaki metodun geri dönüş tipini aşağıdaki gibi de tanımlayabilirdik; func Increase(x int, y int) (num1, num2 int) { … } Birinci Sınıf Fonksiyon Desteği Birinci sınıf fonksiyonlar, fonksiyon nesnelerinin değişkenlere atanması, argümanlara parametre olarak gönderilmesi ve bir fonksiyondan geri dönüş tipi olarak kullanılabilmesine denir. Go dili birinci sınıf fonksiyonları destekler. cihanozhan.com
  • 4. Anonim Fonksiyonlar Anonim değişkenler, belirli durumlarda ve sadece belirli işlemlerde geçerli olacak şekilde işleme özel hazırlanan metotlardır. Örnek: func() { fmt.Println("Merhaba Mars!") }() Örnek çıktısı: Merhaba Mars! Yukarıdaki kodu main() metodu içerisinde yazdık. Bu kod bir anonim fonksiyonun kullanımına verilebilecek en temel örnektir. Anonim metotlar bu şekilde kendi başlarına çalışacak şekilde tanımlanabileceği gibi, bir değişkene atanarak da kullanılabilirler. Bir fonksiyonu değişkene atama işlemi için bir örnek yapalım. Örnek: x := func() { fmt.Println("Merhaba Mars!") } x() fmt.Printf("%T", x) Örnek çıktısı: Merhaba Mars! Yukarıdaki örnekte, main() metodu içerisinde bir anonim fonksiyon tanımladık ve bu fonksiyonu x adıyla oluşturduğumuz değişkene atadık. Bu atama işleminden sonra, artık x nesnesi bir fonksiyon gibi davranır. Ancak bu durum bu fonksiyonun bir adı olduğu anlamına gelmez. Sadece bu fonksiyonu temsil edebilen bir nesne olduğunu gösterir. Yani, halen bu fonksiyonun bir adı olmadığı için, bu yönteme anonim fonksiyon diyoruz. Parametre Alan Anonim Fonksiyonlar Bir anonim fonksiyonun parametreli olarak tanımlanması mümkündür. Bu için bir örnek yapalım. Örnek: func(p string) { fmt.Println("Merhaba", p) }("Mars!") Örnek çıktısı: Merhaba Mars! Yukarıdaki örnekte, anonim fonksiyonu parametre alabilecek şekilde tasarladık ve parametrenin adını p, veri tipini ise string olarak belirledik. Sonrasında, anonim fonksiyonun kapama parantezleri olarak kullandığımız alanda ise “Mars!” metnini göndererek bu parametreyi fonksiyona sağlamış olduk. cihanozhan.com
  • 5. Tip Olarak Tanımlanan Anonim Fonksiyonlar Bir anonim fonksiyonu type anahtar kelimesiyle bir tip olarak tanımlayabiliriz. Bu sayede, oluşturulan bu yeni tipin parametrik yapısıyla aynı yapıya sahip fonksiyonlar için bir veri tipi olarak kullanılmasını sağlayabiliriz. Örnek: Aşağıdaki type tanımını main() metodunun üzerinde yapalım. type fourMath func(p1 int, p2 int) int fourMath adındaki bu type nesnesi bizim için bir şablon niteliği taşıyacak. Dışarıdan iki adet integer veri tipi alan ve dışarıya bir adet integer veri tipinde veri göndermesi gereken bir fonksiyon şablonu. Bu fourMath isimli type’ın amacı, matematikte kullandığımız temel 4 işlemi yapacak fonksiyonları temsil etmektir. Şimdi bu şablonun gereksinimlerini karşılayacak bir fonksiyon oluşturalım. // Toplama işlemini yapan fonksiyon var add fourMath = func(x int, y int) int { return x + y } // Çıkarma işlemini yapan fonksiyon var sub fourMath = func(x int, y int) int { return x - y } // Çarpma işlemini yapan fonksiyon var multiply fourMath = func(x int, y int) int { return x * y } // Bölme işlemini yapan fonksiyon var div fourMath = func(x int, y int) int { return x / y } // İşlemler res_add := add(7, 6) res_sub := sub(7, 6) res_multiply := multiply(7, 6) res_div := div(10, 2) // İşlem sonuçları fmt.Println("Sum", res_add) fmt.Println("Sub", res_sub) fmt.Println("Multiply", res_multiply) fmt.Println("Div", res_div) cihanozhan.com
  • 6. Örnek çıktısı: Sum 13 Sub 1 Multiply 42 Div 5 Fonksiyonlara Parametre Olarak Geçilen Anonim Fonksiyonlar Anonim fonksiyonlar, teknik olarak bir veri tipi gibi yorumlanabildiği için bu fonksiyonları başka fonksiyonlara bir parametre gibi gönderebiliriz. Örnek: f := func(x, y int) int { return x + y } res := calculate(f) fmt.Println(res) calculate() fonksiyonunu hazırlayalım. func calculate(param func(a, b int) int) int { // Simülasyon : Çeşitli işlemler sonucunda 60 ve 7 değerlerini bulduk! // Sonra bu değerleri param(...) fonksiyonuna gönderdik. return param(60, 7) } Örnek çıktısı: 67 Bir Fonksiyondan Başka Bir Fonksiyonu Geri Dönmek Bir fonksiyondan bu fonksiyon içerisinde oluşturulmuş farklı bir fonksiyonu geriye dönmek mümkündür. Örnek: func calculate() func(p1, p2 int) int { f := func(p1, p2 int) int { return p1 + p2 } return f } Yukarıdaki yeni calculate() fonksiyonunu kullanalım. res := calculate() fmt.Println("result: ", res(83, 8)) Örnek çıktısı: result: 91 cihanozhan.com
  • 7. calculate() fonksiyonunun yeni halinin biraz açıklamaya ihtiyacı olabilir. Yukarıda tanımladığımız calculate() fonksiyonu herhangi bir parametre almıyor. Ancak geriye farklı bir fonksiyon yapısı dönmektedir. Geriye dönülen bu fonksiyon, dışarıdan p1 ve p2 adında iki adet integer parametre alır. calculate() içerisinde oluşturduğumuz yeni bir isimsiz fonksiyon da dış fonksiyondan alınan p1 ve p2 parametrelerini parametre olarak almaktadır. İçerideki isimsiz fonksiyonda ise, sadece basit bir şekilde dışarıdan gelen parametrelerin toplama işlemi yapılmaktadır. Tüm bu işlemlerin sonunda geriye bir fonksiyon dönmek için, isimsiz fonksiyonu atadığımız f adındaki nesneyi return ile geriye dönüyoruz. Closure Şu ana kadar fonksiyonlara parametre göndererek onları kullanmayı inceledik. Mevcut genel parametre kullanımının yaklaşımı değer tipi olarak çalışmak üzerine kuruludur. Hatırlatma : Değer tipleri hafızada kopyalanarak aktarılırlar. Bu nedenle, normal bir fonksiyon kullanımında parametre olarak gönderilen değişken ile fonksiyonun parametre olarak aldığı değişken bilgisayar hafızasında farklı adreslere sahiptir. Yani ikisi birbirinin kopyası olsa da farklı nesnelerdir. Ancak, istersek bir fonksiyona parametre olarak gönderilecek değişkenlerin bir parametre olarak değil, doğrudan fonksiyon içerisinde tanınarak kullanılmasını sağlayabiliriz. Biraz karışık gibi görünebilir. Ancak gayet basittir. Şimdi bir fonksiyonu nasıl bir closure olarak kullanabileceğimize bir bakalım. Örnek: Uygulamamızın main() fonksiyonu içerisinde aşağıdaki anonim fonksiyonu tanımlayarak kullanalım. x := 5 func() { fmt.Println("x =", x) }() Örnek çıktısı: x = 5 Hatırlarsanız bir anonim fonksiyona parametre gönderebiliyorduk. Ancak yukarıdaki örneğe dikkat ederseniz, bu anonim fonksiyona herhangi bir parametre göndermediğimiz halde, dışarıda tanımlanan x değişkenine anonim fonksiyonun referansına anonim fonksiyonun içerisinden ulaşarak kullanabildik. Not : C# programlama dili kullananlar için, Go dilindeki closure yaklaşımı C#’daki ref ve out parametreleriyle benzer amaçlar için dil tasarımına eklenmiştir. Closure bir anonim fonksiyon yaklaşımıdır. Yani aslında closure fonksiyonları da birer anonim fonksiyonlardır. Ancak closure fonksiyonlarının normal fonksiyonlardan farkı, bir değişkeni parametre olarak gönderip değer tipi(value type) olarak davranmasını beklemek yerine, bir referans tipi olarak kullanılmasını sağlamaktır. Bu sayede değişkenlerin hafızadaki adreslerini referans olarak closure içerisinden erişilebilmesini sağlar. Örnek: x := 0 counter := func() int { x++ return x } cihanozhan.com
  • 8. fmt.Println(counter()) fmt.Println(counter()) Örnek çıktısı: 1 2 Yukarıda closure yapısını basit bir şekilde anlatan başka bir örnek daha yaptık. Basit bir sayaç fonksiyonu tanımlamak için closure tanımladık. Bu closure fonksiyonu bir üst satırdaki x değişkeninin referansına erişebildiği için içeride x’in değerini her seferinde bir artırabiliyoruz. Artırma işleminden sonra ise, bu değişkeni closure’dan geri dönüyoruz. Closure fonksiyonların bir diğer faydası da veri izolasyonu(data isolation) sağlamasıdır. Örneğin, yukarıdaki kodu main() fonksiyonu içerisinde yazdık. Bu demek oluyor ki, main() metodu içerisinden x değişkenine erişerek closure’daki işleme dışarıdan müdahale edilebilir. Bu tür, kod ya da veriye kendi işlemi dışından müdahale edilebilmesine engel olma işlemine veri izolasyonu diyoruz. Örnek: // counterProcess adında yeni bir closure fonksiyon tanımlayalım. func counterProcess() func() int { x := 0 return func() int { x++ return x } } // counterProcess fonksiyonunu main() fonksiyonu içerisinde kullanalım. counter := counterProcess() fmt.Println(counter()) fmt.Println(counter()) Örnek çıktısı: 1 2 Aslında son örneğimizde bir önceki örneğin aynısını yaptık. Ancak bu sefer closure’ı fonksiyon dönen bir fonksiyon içerisinde tanımladık. Bu sayede, artık closure içerisinde referansı kullanılan x değişkenine dışarıdan müdahale edilmesini engelledik. Şimdi de closure için farklı bir gerçek dünya uygulaması yapalım. Örnek: // append() adında fonksiyon dönen yeni bir fonksiyon tanımlıyoruz. func append() func(string) string { str := "" co := func(x string) string { str = str + " " + x return str cihanozhan.com
  • 9. } return co } // main() fonksiyonu içinde append() fonksiyonu kullanıyoruz. app := append() app("Hello") app("Gophers") result := app("!") fmt.Println(result) Örnek çıktısı: Hello Gophers ! Yukarıdaki örnekte dışarıdan aldığı metinsel veriyi kendi içindeki metinsel veri ile birleştirerek uç uca ekleyen append() adında bir closure fonksiyon tanımladık. Bu fonksiyonu kullanırken de, app adında bir değişkenine append() fonksiyonunu atayarak app değişkeninin append() fonksiyonu gibi davranmasını sağladık. Sonrasında ise, app() fonksiyonuna sürekli yeni metinsel veriler ekleyerek hepsini uç uca birleştirmesini sağladık. En son işlemde de app() fonksiyonundan dönen birleştirilmiş metinsel veriyi result adındaki değişkene atayarak ekrana bastık. Şu ana kadar birçok örnek yaptık. Ancak bu kitabın temel özelliği, Go dilini hem öğretmek, hem de uygulamalar yaparak bu özelliklerin nasıl uygulandığını örneklerle göstermektir. Bu nedenle, şimdi closure fonksiyonlarını ve struct yapısını kullanarak biraz daha detaylı bir gerçek dünya uygulaması yapalım. Uygulama Amacı: Bir teknoloji firmasının çalışan bilgilerini ve uzmanlık alanlarını tutacak ve bu veriler üzerinde çeşitli filtre ve hesaplamalar yapacak mini bir uygulama geliştirmek. Uygulama: Uygulamanın genel struct yapısını oluşturalım. type Employee struct { FirstName string LastName string Salary int Expertises []Expertise } type Expertise struct { ID int Name string } Uygulama içerisinde kullanacağımız struct nesnelerini tanımladık. Her bir çalışanın birden fazla uzmanlık alanı olabileceği için ilgi alanlarını(Expertises) dizi olarak tutuyoruz. Uygulamanın fonksiyonlarını hazırlayalım. // Çok amaçlı filtreleme fonksiyonudur. func filter(emps []Employee, f func(Employee) bool) []Employee { var empList []Employee cihanozhan.com
  • 10. for _, emp := range emps { if f(emp) == true { empList = append(empList, emp) } } return empList } // Sadece sayaç görevi için kullanılacak fonksiyondur. func count(emps []Employee, f func(Employee) bool) int { var count int for _, emp := range emps { if f(emp) == true { count++ } } return count } Uygulama içerisinde kullanacağımız genel fonksiyonlarımızı da hazırladık. Ancak bu uygulama için demo verilere ihtiyacımız olacak! Demo verileri de bilgisayar hafızası üzerinde çalışacak şekilde ayrı bir fonksiyon olarak hazırlayacağız. func getData() []Employee { var val = make([]Employee, 0, 0) v1 := Employee{ FirstName: "Cihan", LastName: "Özhan", Salary: 1100, Expertises: []Expertise{ Expertise{3, "C#"}, Expertise{5, "Go"}, Expertise{6, "Oracle"}, Expertise{8, "PostgreSQL"}, Expertise{10, "SQL Server"}, }, } v2 := Employee{ FirstName: "Murtaza", LastName: "Çalışkan", Salary: 986, Expertises: []Expertise{ Expertise{4, "HTML"}, Expertise{7, "JavaScript"}, Expertise{10, "SQL Server"}, }, } val = append(val, v1) val = append(val, v2) cihanozhan.com
  • 11. return val } Yukarıdaki getData() fonksiyonunu sadece uygulama içerisinden veriye erişim için kullanacağız. Artık main() fonksiyonu içerisinde çalışmaya başlayabiliriz. Öncelikle, aşağıdaki komutu kullanarak uygulamamızın hafızasına demo verileri dolduracağız. empList := getData() // Maaşı 1000 birim üzerinde olan çalışanları getir. searchTagSalary := 1000 f1 := filter(empList, func(emp Employee) bool { if emp.Salary >= searchTagSalary { return true } return false }) fmt.Println(f1) f1 çıktısı: [{Cihan Özhan 1100 [{3 C#}{5 Go}{6 Oracle}{8 PostgreSQL}{10 SQL Server}]}] empList := getData() // Adı "Murtaza" olan çalışanları getir. searchTagName := "Murtaza" f2 := filter(empList, func(emp Employee) bool { if emp.FirstName == searchTagName { return true } return false }) fmt.Println(f2) f2 çıktısı: [{Murtaza Çalışkan 986 [{4 HTML} {7 JavaScript} {10 SQL Server}]}] empList := getData() // HTML uzmanlık alanına sahip çalışanları getir. searchTagExpertise := "HTML" f3 := filter(empList, func(emp Employee) bool { for i := 0; i < len(empList); i++ { if emp.Expertises[i].Name == searchTagExpertise { return true } } return false }) fmt.Println(f3) cihanozhan.com
  • 12. f3 çıktısı: [{Murtaza Çalışkan 986 [{4 HTML} {7 JavaScript} {10 SQL Server}]}] // 1000 birim ve üzeri maaş alan çalışanları getir. (500 değeriyle de deneyiniz!) searchTagSalaryForCount := 1000 f4 := count(empList, func(emp Employee) bool { if emp.Salary >= searchTagSalaryForCount { return true } return false }) fmt.Println(f4) f4 çıktısı: 1 Yukarıdaki her bir f değişkeni(f1, f2, f3, f4) ayrı bir işlemin sonucunu tutmaktadır. Bunları ayrı ayrı ekrana basarak inceleyiniz. Bonus olarak bu mini uygulama üzerinde aşağıdaki işlemleri de yapabilirsiniz. Örnek : Çalışanların ad ve soyadlarını birleştirerek ekrana yazdıralım. for _, v := range getData() { fmt.Println(v.FirstName + " " + v.LastName) } Örnek çıktısı: Cihan Özhan Murtaza Çalışkan Örnek : Tüm çalışanların uzmanlık alanlarını ekrana yazdıralım. for _, v := range getData() { for _, e := range v.Expertises { fmt.Println(strconv.Itoa(e.ID) + " " + e.Name) } fmt.Printf("n") } Not : Yukarıdaki örnekte strconv built-in paketi kullanılmıştır. Örnek çıktısı: 3 C# 5 Go 6 Oracle 8 PostgreSQL 10 SQL Server cihanozhan.com
  • 13. 4 HTML 7 JavaScript 10 SQL Server Ertelenmiş Fonksiyonlar: Defer Defer ile tanımlanan fonksiyonlar, kendisini çevreleyen fonksiyon geri dönüş işlemine gelene kadar ertelenen fonksiyonlardır. Ertelenmiş bir fonksiyon çağrısının argümanları anında değerlendirilir. Ancak fonksiyon çağrısı kendisini çevreleyen fonksiyon dönmeden işleme alınmaz. Bir veritabanı işlemi yaptığımızı düşünelim. Bir veritabanı işlemi yapmanın temel kuralı, veritabanı üzerinde bir bağlantı açmaktır. Bağlantı açma işleminden sonra gerekli SQL sorguları yapılır ve tüm işlemler bittikten sonra da veritabanı bağlantısı kapatılmalıdır. Bu senaryoya göre nasıl bir defer kullanımı yapabileceğimize bir bakalım. Örnek: main() fonksiyonu üzerinde isConnected adında bir değişken tanımlayalım. var isConnected bool = false Sonrasında bu mini uygulamada kullanmak için paket seviyesindeki temel fonksiyonları hazırlayalım. func databaseProcessing() { connect() fmt.Println("Bağlantıyı kesme işlemi erteleniyor!") defer disconnect() fmt.Printf("Bağlantı açık: %vn", isConnected) fmt.Println("Veritabanı işlemleri yapılıyor!") } func connect() { isConnected = true fmt.Println("Veritabanına bağlıyız!") } func disconnect() { isConnected = false fmt.Println("Veritabanı bağlantısı kapalı!") } Uygulamanın main() fonksiyonu içerisindeki işlemleri yapalım. fmt.Printf("Bağlantı açık: %vn", isConnected) databaseProcessing() fmt.Printf("Bağlantı açık: %vn", isConnected) Örnek çıktısı: cihanozhan.com
  • 14. Bağlantı açık: false Veritabanına bağlıyız! Bağlantıyı kesme işlemi erteleniyor! Bağlantı açık: true Veritabanı işlemleri yapılıyor! Veritabanı bağlantısı kapalı! Bağlantı açık: false Yukarıdaki uygulamada neler oldu? Öncelikle mevcut veritabanı bağlantısının açık olup olmadığını kontrol ettik. Veritabanı bağlantısını henüz açmadığımız için bağlantının kapalı olduğuna dair false bilgisini aldık. Sonrasında ise databaseProcessing() fonksiyonunu çalıştırdık. Bu fonksiyon içerisinde ilk olarak connect() fonksiyonu çalıştırılmaktadır. Bu fonksiyon ise veritabanı bağlantısını açmak için kullanılmaktadır. Ve bu nedenle, ekranda Veritabanına bağlıyız mesajını gördük. Hemen connect() işleminden sonra ise defer ile birlikte disconnect() fonksiyonunu çağırdık! İşte bu nokta önemli… Aslında biz o anda veritabanı bağlantısını sonlandırmak istemiyoruz. Sadece Go derleyicisine söylemek istediğimiz şudur: “Bu fonksiyon içerisinde veritabanı ağlantısı açıldıktan sonra veritabanı üzerinde birçok işlem yapacağım. Bu işlemler bittikten sonra(fonksiyonun sonunda) mevcut açık olan veritabanı bağlantısını otomatik olarak kapat”. İşte bu nedenle disconnect() fonksiyonunu tüm işlemlerden önce defer ile birlikte çağırabiliyoruz. Not : Veritabanı ya da stream işlemlerinde kaynaklar üzerinde oluşturulan bağlantıların yönetilmesi ve işlem bittikten sonra kapatılması bazen unutulabilmektedir. Bu durum sistem kaynaklarının tüketimini olumsuz yönde etkiler. Bu tür durumlarla karşılaşmamak için defer kullanımını alışkanlık haline getirmek işinizi kolaylaştırabilir. Metotlar Metotlar temel anlamda birer fonksiyondur. Metotların fonksiyonlardan temel farkı, metotların sadece parametrelerinin değil, aynı zamanda alıcılarının da olmasıdır. Söz dizimi: func (t Type) methodName(parameter-list) { } Yukarıdaki metot söz diziminde fonksiyonlardan farklı olarak dikkatleri çeken nokta (t Type) şeklinde kullanılan tanım alanıdır. Bu alan metodun hangi alıcı tipiyle çalışacağının belirtildiği alandır. Bir metoda alıcı olarak atanan nesne çalışma anında bu metot ile aynı scope içerisinde çalıştırılır. Not : C# ya da Java gibi programlama dillerinde bu yaklaşım bulunmaz. Bunun nedeni bu dillerin metotları kendi class’ları içerisinde tanımlamaya zorlamasıdır. Ancak Go dilinde struct(class yerine) için özel olarak belirlenen bir scope parantezi yer almaz. Bu nedenle, Go dilinde bir metodu bir nesneye bağlamak için o nesnenin tipini ilgili metoda alıcı olarak bağlamamız gerekir. Bu diğerlerine göre daha sade ve kullanışlı bir yöntemdir. Metotları genel olarak struct nesneleriyle birlikte kullanacak olsak da, bir metodu herhangi bir Go tipiyle birlikte kullanabiliriz. Şimdi integer veri tipine sahip özel bir tip oluşturalım ve bu tipe MultipleBy10 adında yeni bir metot atayarak kullanalım. cihanozhan.com
  • 15. Örnek: type MyInt int func (x MyInt) MultiplyBy10() int { if x <= 0 { return int(x) } return int(x) * 10 } Örneğin kullanımı: val := MyInt(10) fmt.Println(val.MultiplyBy10()) Örneğin çıktısı: 100 Yukarıdaki örnekte MyInt adında arka planda integer tipine sahip özel bir tip kullandık. Bu tür kullanımları uygulamalarda bolca kullanmak gerekse de, metotların en yaygın kullanılacağı nesne tipi struct’dır. Şimdi en temel haliyle bir metodun struct nesnesiyle nasıl kullanılacağına bir bakalım. Örnek: type T struct { name string } func (x T) PrintName() { fmt.Println(x.name) } Yukarıdaki örneği main() fonksiyonu içerisinde kullanalım. t := T{name: "Cihan"} t.PrintName() Örnek çıktısı: Cihan Bir metodun struct ya da diğer tipler ile kullanımı arasında herhangi bir fark yoktur. Hazırladığımız metot, kendisine alıcı olarak atadığımız tipe ait bir metot olarak çalışmak üzere tasarlanmıştır. Metotlar konusunda daha birçok örnek yapabiliriz. Ancak kitap içerisinde metot nesnelerini sık sık kullanacağız. Bu nedenle, metotlar bölümünde özellikle metotları bir uygulama ile açıklamak daha faydalı olabilir. cihanozhan.com
  • 16. Örnek: Bu örnekte, gerçek bir uygulama geliştirirken karşılaşabileceğimiz bir senaryoyu uygulayacağız. Elimizde kullanıcı ve bu kullanıcılara ait ilgi alanlarıyla birlikte, her kullanıcının sistem üzerinden elde ettiği kazanç gibi veriler mevcut. Bu kullanıcı verisini metot yapısına uygun şekilde kullanacağız. Not : Bu uygulama örneğinde fmt ve time paketlerini kullanacağız. Kullanıcı verisini tutacak User nesnesinden önce, bu nesne ile ilişkili olacak alt struct nesnelerini oluşturalım. Kullanıcının kazanç bilgilerinin özetini tutmak için RevenueSummary struct’ı: type RevenueSummary struct { ID int TotalRevenue float64 TotalSales int } Kullanıcının ilgi alanlarını tutmak için Interest struct’ı: type Interest struct { ID int Name string } Kullanıcının temel bilgilerini tutmak için User struct’ı: type User struct { ID int FirstName string LastName string UserName string DoB string RevenueSummary *RevenueSummary Interests []Interest } Uygulamanın temel struct yapısı hazır. Şimdi uygulamanın çalışması için gereken metotları hazırlayabiliriz. Kullanıcının ad ve soyad bilgilerini birleştirerek getiren GetFullName() metodu: func (u User) GetFullName() string { return u.FirstName + " " + u.LastName } Kullanıcının kullanıcı adı bilgisini getiren GetUserName() metodu: func (u *User) GetUserName() string { return u.UserName } Kullanıcının gelir özet verisini bir RevenueSummary struct’ı olarak getirecek GetRevenueSummary() metodu: cihanozhan.com
  • 17. func (u *User) GetRevenueSummary() RevenueSummary { return *u.RevenueSummary } Kullanıcının doğum tarihine göre yaşını hesaplayarak getirecek GetAge() metodu: func (u *User) GetAge() int { t, _ := time.Parse("2006-01-02", u.DoB) age := time.Now().Year() - t.Year() return age } Kullanıcının ilgi alanlarını bir Interest struct listesi olarak getirecek GetInterests() metodu: func (u *User) GetInterests() []Interest { return u.Interests } Kullanıcının ilgi alanlarının toplamını hesaplayarak getirecek GetCountOfInterests() metodu: func (u *User) GetCountOfInterests() int { return len(u.Interests) } Uygulama için gerekli tüm nesneler hazır. Şuan tek eksiğimiz uygulama içerisinde kullanacağımız örnek bir kullanıcı verisini tutan demo veri yapısıdır. Bunun için de getData() adında ayrı bir fonksiyon tanımlayacağız. func getData() User { user := &User{ ID: 1, FirstName: "Cihan", LastName: "Özhan", UserName: "CihanOzhan", DoB: "1988-01-09", RevenueSummary: &RevenueSummary{ TotalRevenue: 5300, TotalSales: 54, }, Interests: []Interest{ Interest{2, "Databases"}, Interest{5, "Web Programming"}, }, } return *user } Not : getData() nesnesi bir metot değil fonksiyondur. Artık main() fonksiyonuna geçerek bu nesneleri kullanabiliriz. // Örnek kullanıcı verisini u adındaki değişkene atayalım. cihanozhan.com
  • 18. u := getData() fmt.Println("Temel Bilgiler:") fmt.Println("-> Kullanıcının Tam Adı: ", u.GetFullName()) fmt.Println("-> Kullanıcı Adı: ", u.GetUserName()) fmt.Println("-> Yaş: ", strconv.Itoa(u.GetAge())+"n") fmt.Println("İlgi Alanları: ") for _, v := range u.GetInterests() { fmt.Println("--> İlgi Alanı: ", v.Name) } countOfInterests := strconv.Itoa(u.GetCountOfInterests()) str := u.FirstName + "'ın İlgi Alanlarının Toplamı: " + countOfInterests fmt.Println(countAllStr + "n") fmt.Println("Kazanç Bilgileri:") totalRev := u.GetRevenueSummary() fmt.Println("-> Toplam Kazanç: ", totalRev.TotalRevenue) fmt.Println("-> Toplam Satış Adedi: ", totalRev.TotalSales) Örnek çıktısı: Temel Bilgiler: -> Kullanıcının Tam Adı: Cihan Özhan -> Kullanıcı Adı: CihanOzhan -> Yaş: 30 İlgi Alanları: --> İlgi Alanı: Databases --> İlgi Alanı: Web Programming Cihan'ın İlgi Alanlarının Toplamı: 2 Kazanç Bilgileri: -> Toplam Kazanç: 5300 -> Toplam Satış Adedi: 54 Bir konuya dikkatinizi çekmek istiyorum! GetFullName() metodundaki alıcı tanımlama alanında işaretçi(*) simgesini kullanmadım. Ancak diğer tüm metotlarda alıcı olarak işaretçi kullandım. Muhtemelen bu konu dikkatli okuyucuların gözüne çarpmıştır. Şimdi bir metot alıcısı olarak işaretçi kullanıp kullanmama konusunu inceleyelim. Metot Alıcısı Olarak İşaretçi Kullanımı İşaretçiler için küçük bir hatırlatma yapalım. İşaretçiler doğrudan nesne örneğinin hafızadaki adresini temsil ederler. Bu nedenle, eğer bir işaretçi üzerinde işlem yaparsanız, bu işaretçi hangi nesne örneği tarafından temsil edilirse edilsin, orijinal veri üzerinde değişiklik yapacaktır. Metotlar ile işaretçi kullanımını incelemek için önceki uygulamaya bir ekleme yapacağız. Bu eklemeyi kullanıcının adını değiştirmek amacıyla SetUserName(…) metodu ile yapacağız. Örnek: İşaretçisiz kullanım func (u User) SetUserName(uName string) { u.UserName = uName } cihanozhan.com
  • 19. Oluşturduğumuz bu yeni metodu kullanalım: u.SetUserName("CO") fmt.Println(u.GetUserName()) Örnek çıktısı: CihanOzhan Kullanıcı adını değiştirme metodunu kullandık ancak kullanıcı adımız değişmedi! Bu durum, bizim gerçek anlamda o anki kullanıcının kopyası üzerinde bu işlemi yapmamış olmamızdan kaynaklanmaktadır. Eğer o anki nesne örneğinde bulunan kullanıcı üzerinde bu işlemin yapılmasını istiyorsak, işaretçi ile tanımlı alıcı kullanmalıyız. Şimdi SetUserName(..) fonksiyonunu işaretçi alıcısı alacak şekilde yeniden düzenleyip örneği tekrar inceleyelim. Örnek: İşaretçi kullanımı func (u *User) SetUserName(uName string) { u.UserName = uName } Oluşturduğumuz bu yeni metodu kullanalım: u.SetUserName("CO") fmt.Println(u.GetUserName()) Örnek çıktısı: CO Go ile uygulama geliştirirken en sık kullanılacak nesnelerden biri işaretçilerdir. Bu nedenle struct ile işaretçilerin nasıl çalıştığını doğru şekilde anlamak gerekmektedir. Eğer metodun alıcısı olduğu nesnenin örneği üzerinde bir değişiklik yapmak isterseniz işaretçi kullanmalısınız. cihanozhan.com
  • 20. Arayüzler … Yapılar Bu bölümde, Go dilinin mevcut programlama dillerinden farklı olan bir bakış açısını daha inceleyeceğiz. Şu ana kadar kullandığımız programlama dillerinin çoğunda kullanılan temel bir özellik vardı: class(sınıf) Nesne modelleri oluşturmak için kullandığımız class kavramı Go dilinde bulunmaz. Onun yerine, aynı amaç için tasarlanan struct(yapı) kullanılır. Yapılar, kullanıcı tanımlı tiplerdir ve genellikle yapısı belli olan(olmayabilir de) verileri nesnel ortamda temsil etmek için çeşitli field(alan)’lardan oluşan nesnelerdir. Go dilinde kullandığımız struct(yapı) yapısının diğer dillerdeki class yapılarından bazı farklılıkları da vardır. Şimdi yapı nesnelerinin tüm özelliklerini detaylıca inceleyelim. Not : Yapı nesnesi Go ile profesyonel uygulamalar geliştirmek için kullanılan en temel özelliklerden biridir. Bu nedenle, yapıların kullanımları ve detayları üzerine birçok uygulama çalışması yapacağız. Not : struct nesnesinin Türkçe’deki karşılığı yapı’dır. Yapı Tanımlamak ve İlklendirmek Yeni bir yapı tanımlamak için type anahtar kelimesini kullanıyoruz. En temel haliyle bir yapı aşağıdaki gibi tanımlanabilir: Örnek: type Article struct { id int title string content string resourceURL string authorID int categoryID int } Yukarıdaki yapı modelinde bir makale yapısını programatik olarak tasarladık. Herhangi bir makalenin veritabanı işlemlerini gerçekleştirmek ve makale verilerini uygulamanın hafızasında anlamlı şekilde yönetebilmek için böyle bir yapıya ihtiyacımız vardı. Aynı veri tiplerine sahip yapı alanlarını tek satırda da tanımlayabiliriz: Örnek: type Article struct { id, authorID, categoryID int title, content, resourceURL string } Not : Tek satırda yapı alanları tanımlama yöntemini uygularken dikkatli olunmalıdır! Bu kullanımın amacı, kod kalabalığını azaltmaktır. Ancak, çok fazla alana(field) sahip yapılarda her veri tipine düşen alan adedinin fazla cihanozhan.com
  • 21. olması nedeniyle, kodun okunabilirliği azalabilir. Bu tür durumlardan kaçınmak için, yazılım geliştirme standartlarına uyulmalı ve kendi uygulama kurallarınızı oluşturarak bunlara bağlı kalmalısınız. Peki bir yapının başlangıç(sıfır) değeri nedir? Bir yapının nesne örneğini oluşturduğumuzda yapıya ait alanların da hafıza üzerinde nesne kurulumları yapılır. Yapıya ait nesne alanları kendi tiplerinin başlangıç değerlerine(sıfır değeri) sahip olurlar. Örnek: var art3 Article // sıfır değeri oluşturulmuş nesne fmt.Println("art3", art3) Örnek çıktısı: art3 {0 0 0} Yukarıdaki sonuçta görüldüğü üzere, integer bir alanın başlangıç değeri sıfır(0), string alanın ise boşluk olarak tanımlanır. Aynı durum diğer veri tipleri için de geçerlidir. Eğer oluşturduğumuz art3 nesne örneğinin metinsel alanlarına veri girişi yaparsak, integer alanların başlangıç değerleri korunmakla birlikte, metinsel alanlarda yeni girilen veriler tutulacaktır. Örnek: var art3 Article // sıfır değeri oluşturulmuş nesne art3.content = "Bir makale içeriği." art3.resourceURL = www.cihanozhan.com fmt.Println("Article 3", art3) Örnek çıktısı: Article 3 {0 Bir makale içeriği. www.cihanozhan.com 0 0} Nesne ilklendirme ve başlangıç değerlerini incelediğimize göre, artık başlangıç değerleri atayarak nesne örneği oluşturmak için birkaç örnek inceleyebiliriz. Örnek: // Alan isimlerini kullanarak yapı ilklendirmek art1 := Article{ title: "Go ile XML Veriyi JSON'a Dönüştürme", content: "Bu örneğimizde bir XML formatlı veriyi JSON …", resourceURL: "cihanozhan.com/go-ile-xml-veriyi-jsona-donusturme/", authorID: 7, categoryID: 9, } // Alan isimlerini kullanmadan yapı ilklendirmek art2 := Article{ 0, // id alanı! "Go ile Şifreleme İşlemleri", "Bu makalede Go işe şifreleme yapmayı ve ilgili paketleri …", "cihanozhan.com/category/golang/", cihanozhan.com
  • 22. 7, 9, } Article isimli yapının art1 nesne örneğinde kullandığımız yöntem Go topluluğu tarafından en çok tercih edilen yöntemdir. Ancak bazı durumlarda daha pratik ve isimlendirme gerekmeyen kullanım yöntemini kullanmak gerekebilir. Bu durumda da art2 nesne örneğinin yöntemi uygulanabilir. art2’de dikkat ederseniz, id alanını da göndermek zorunda kaldık! Bu kullanım yönteminde herhangi bir alan adı vermediğimiz için tüm alanları göndermek zorundayız. Aksi halde, Go derleyicisi hangi verinin hangi alana ait olduğunu anlayamayacağı için hata verecektir. Uygulamanın çıktısını görmek için: fmt.Println("Makale 1: ", art1) fmt.Println("Makale 2: ", art2) Bir Yapının Alanlarına Erişmek Bir yapıya ait alanlara erişmek için nokta(.) operatörü kullanılır. Bu konuyu örneklendirmek için daha önce oluşturduğumuz art1 isimli Article nesne örneğini kullanacağız. Örnek: art1 := Article{ title: "Go ile XML Veriyi JSON'a Dönüştürme", content: "Bu örneğimizde bir XML formatlı veriyi JSON …", resourceURL: "cihanozhan.com/go-ile-xml-veriyi-jsona-donusturme/", authorID: 7, categoryID: 9, } fmt.Println("ID: ", art1.id) fmt.Println("Title: ", art1.title) fmt.Println("Content: ", art1.content) fmt.Println("Resource: ", art1.resourceURL) fmt.Println("AuthorID: ", art1.authorID) fmt.Println("CategoryID: ", art1.categoryID) Örnek çıktısı: ID: 0 Title: Go ile XML Veriyi JSON'a Dönüştürme Content: Bu örneğimizde bir XML formatlı veriyi JSON … Resource: http://www.cihanozhan.com/go-ile-xml-veriyi-jsona-donusturme/ AuthorID: 7 CategoryID: 9 Şu ana kadar oluşturduğumuz Article örneğindeki yapıya named structure(isimlendirilmiş yapı) diyoruz. Bir diğer yapı oluşturma yöntemiyse anonymous struct(anonim yapı) yaklaşımıdır. Şimdi ona bakalım… cihanozhan.com
  • 23. Yapılarda Erişim Belirleyicileri Eğer C# ya da Java programlama gibi bir tecrübeniz var ise, bu dillerdeki erişim belirleyicilerine(public, private, protected vb.) dokunmadan orta ya da büyük çaplı bir proje tasarlayamayacağınızı biliyor olmalısınız. Bu tür tanımlamalar, ilgili nesnenin erişim sınırlarını belirler. Peki, bir Go paketi içerisinde tanımladığımız herhangi bir nesnenin o paketin dışından erişilebilir olup olmadığını nasıl kontrol edeceğiz? Go dili herhangi bir erişim tanımlayıcı anahtar kelime(public, private vb.) kullanmaz. Go derleyicisi sadece nesnenin baş harfinin büyük ya da küçük olma durumuna göre erişimleri belirler. - Go paketinde tanımladığınız nesnenin baş harfi büyük ise bu nesneye paketin dışından erişilebilir. - Go paketinde tanımladığınız nesnenin baş harfi küçük ise bu nesneye paketin dışından erişilemez. Go dilinin erişim belirleme kuralı sadece aşağıdaki nesneler için değil, paket içerisindeki tüm nesneler için geçerlidir. Aşağıdakiler sadece örnektir. Değişken: - Public : o var Age int - private : o var age int Yapı: - Public : o type User struct { } - private : o type user struct { } Metot: - Public: o func IsValid() bool - private : o func isValid() bool Bu kurala göre, eğer oluşturduğunuz bir yapı nesnesinin paket dışından erişilebilir olmasını istiyorsanız, yapı nesnesinin adı büyük harfle başmalıdır. Aynı şekilde, eğer bir yapı nesnesinin içindeki alanların dışarıdan erişilebilir olmasını istiyorsanız, bu alanların da baş harfi büyük harfle başlamalıdır. Anonim Yapı Tanımlamak ve İlklendirmek Article isimli yapı nesnesinden görüldüğü üzere yeni yapı tanımlama işlemlerinde type anahtar kelimesini kullanıyoruz. Ancak var ile de değişken tanımlar gibi yeni bir yapı tanımlaması yapabiliriz. Bu yaklaşıma da anonim yapı(anonymous structure) diyoruz. Örnek: var article struct { id, authorID, categoryID int title, content string } cihanozhan.com
  • 24. Yapı ile Pointer Kullanımı Pointer kullanımı ile ilgili bölümde Go dilindeki işaretçi yapısını ve kullanımını örneklemiştik. Birçok programlama dilinin aksine, Go dilinde işaretçilerin kullanımı çok yaygındır. Go dilinde işaretçiler özellikle yapı ve metot/fonksiyon gibi nesnelerde çok sık kullanılır. Şimdi bir yapı ile pointer aracılığıyla nasıl oynayacağımıza bakalım. Örnek: // Alanları veri tipine göre gruplayarak yeni bir yapı oluşturduk type Article struct { id int title, content, resourceURL string authorID, categoryID int } article := &Article{ 0, "Makale Başlığı", "Makale İçeriği", "www.cihanozhan.com", 7, 9, } fmt.Println("Başlık: ", (*article).title) fmt.Println("İçerik: ", (*article).content) fmt.Println("Resource: ", (*article).resourceURL) Örnek çıktısı: Başlık: Makale Başlığı İçerik: Makale İçeriği Resource: www.cihanozhan.com Go dilinde pointer kullanımının yaygın olmasının nedenlerinden bazıları performans ve kaynak yönetimidir. Yoğun nesneye sahip ve veri işlemleri gerektiren projelerde, gerçekleştirilen her işlem bilgisayar belleğini ciddi miktarda tüketir. Bunun nedenlerinden biri, kullanılan değer tipindeki nesnelerdir. Her işlem için on binlerce değer tipinin tekrar tekrar oluşturulması ve bir diğerine kopyalanması nedeniyle hafızada gereksiz bir yük oluşur. Ancak işaretçi kullanıldığı takdirde, hafızadaki bu nesnelerin kopyaları alınmaz, sadece hafızadaki adresleri diğer değişkene gönderilerek aynı hafıza adresi ve alanı üzerinde değişiklik yapılması sağlanır. Uygulama Ödevi : Yukarıdaki yapı ile pointer kullanımı örneğini, pointer kullanmadan tekrar yazın ve sonucu gözlemleyin. cihanozhan.com
  • 25. Anonim Alanlar Go dilinde bir yapı içerisinde alanın adını belirtmeden sadece veri tipini belirterek yapı oluşturmak mümkündür. Bu yaklaşıma anonim alanlar(anonymous fields) diyoruz. Örnek: type Book struct { string // kitap başlıı int // kitap fiyatı } Versiyon 1: book := Book{"İleri Seviye T-SQL Programlama", 45} fmt.Println(book) Versiyon 2: var book Book book.int = 45 book.string = "İleri Seviye T-SQL Programlama" Örnek çıktısı: {İleri Seviye T-SQL Programlama 45} Bu kitabın fiyatını artıralım. book.int += 1 fmt.Println("Kitap Fiyatı: ", book.int) Anonim alanlar ilginç bir dil özelliğidir! Veri tipi olarak tanımladığımız int, string ya da diğer veri tipleri anonim alanlar içerisinde kullanıldığında aynı zamanda bir alan adı olarak kullanılırlar. Bu nedenle bir veri tipini sadece bir kez tanımlayabilirsiniz. Yapıcı Metotlar Bir nesnenin örneğini oluşturma işlemi varsayılan olarak derleyici tarafından yürütülen bir süreçtir. Ancak programcı olarak istersek bu sürece müdahale ederek kendi nesne örneği oluşturma politikamızı uygulayabiliriz. Bu işlemi yapabilmemizi sağlayan nesnelere de yapıcı metotlar diyoruz. Go dili C# ve Java gibi varsayılan olarak yapıcı metotları desteklemez. Yani bunun için özel bir metot tanımlama yöntemi yoktur. Bu bir eksiklik değildir! Aksine, Go dili tasarımcılarına göre yapıcı metot(constructor) gereksiz bir özelliktir. Çünkü bu işlemi normal metotları kullanarak zaten yapabilmekteyiz. Tıpkı Go dilinde olduğu gibi... Şimdi Go dilindeki yapıcı metot yaklaşımını kavrayabilmek için bir örnek yapalım. cihanozhan.com
  • 26. Örnek: type Human struct { FirstName string LastName string Age int } // Yapıcı metot olarak kullanılacak metodumuz func NewHuman(firstName, lastName string) *Human { human := new(Human) human.FirstName = firstName human.LastName = lastName return human } Şimdi bu yapı ve yapıcı metodumuzu kullanalım. Temel işlemler için belirtmek gerekirse, bir yapının nesne örneğini oluşturmak için yapıcı metotlara ihtiyacımız yoktur. Ancak profesyonel programlama ve nesne yönelimli programlama yaklaşımları gereği, yapıcı metotların kullanılması gereken durumların sayısı hiç de az değildir. Daha önceki yapı oluşturma yöntemlerinde de kullandığımız “ nesne örneğini inşa etme işini derleyiciye bırakmak” yöntemine tekrar bir göz atalım. Versiyon 1: human1 := Human{FirstName: "Cihan"} human4 := Human{"Cihan", "Özhan", 30} human5 := Human{FirstName: "Cihan", Age: 30} fmt.Println(human1.FirstName) Bu yöntemde nesne örneğinin oluşturulma sürecine aktif olarak müdahale etmedik. Biz sadece başlangıçta FirstName alanına bir değer ataması yaptık. Bu sayede derleyici arka planda Human nesnesi için bir yapıcı metot oluşturarak bu nesnenin örneğini oluşturarak bize geri gönderdi. Biz de bu nesne örneğini human1 adındaki değişkenimize atadık. Versiyon 1 çıktısı: Cihan Versiyon 2: new() ile nesne örneği oluşturmak new() ile nesne örneği oluşturma işlemi, sadece yapılar için değil, daha birçok nesnede kullanılabilen bir yöntemdir. Dikkat: NewHuman() isimli yapıcı metot içerisinde de zaten new()’i kullanarak nesnenin yeni bir örneğini oluşturuyoruz. human2 := new(Human) fmt.Println(human2.Age) Versiyon 2 çıktısı: 0 // Herhangi bir değer ataması yapılmadığı için, sadece integer’ın varsayılan değerini alırız. cihanozhan.com
  • 27. Versiyon 3: NewHuman() fonksiyonunu ile nesne örneği oluşturmak Human yapısı için oluşturduğumuz NewHuman() isimli fonksiyonu aslında bir yapıcı metot olması amacıyla oluşturduk. Bu metodun kullanımının diğer Go metotlarından herhangi bir farkı yoktur. human3 := NewHuman("Cihan", "Özhan") human3.Age = 30 fmt.Println(human3) Versiyon 3 çıktısı: &{Cihan Özhan 30} Yukarıda görüldüğü gibi, Go dilinde bir yapının nesne örneğini oluşturmanın birçok yöntemi vardır. İç İçe Yapı Kullanımı Mantıksal olarak birbiriyle ilişkili olan farklı yapıları birbiri içerisinde bir alan olarak kullanabiliriz. Bu yönteme iç içe yapı kullanımı denir. Bu gereksinime bir örnek vermek gerekirse, User adında bir yapı ile kullanıcı verisini yönettiğimizi düşünelim. Bu kullanıcılara ait ilgi alanlarını da, Interest adında oluşturduğumuz yapı ile yönetiyoruz. Bu durumda, bir kullanıcının birden fazla ilgi alanı olabileceğine göre, Interest dizisi([]Interest) alan Interests adında bir alanı User yapı nesnesine ekleyebiliriz. Şimdi farklı bir senaryo ile iç içe yapı kullanımını inceleyeceğiz. Örnek: Bu örnekte kullanılacak Go paketleri: fmt, strconv Daha sonra oluşturacağımız kullanıcının ödemelerini tutan bir yapı oluşturalım. type Payment struct { Salary float64 Bonus float64 } Payment yapısının nesne örneğini oluşturmak için kullanacağımız yapıcı metodu oluşturalım. func NewPayment() *Payment { p := new(Payment) return p } Bu uygulamanın ana nesnesi olmak üzere User yapısını oluşturalım. type User struct { ID int FirstName string LastName string UserName string Age int Pay *Payment // İç içe struct tanımladık! cihanozhan.com
  • 28. } Buraya dikkat! User yapısı içerisinde başka bir Payment yapısını bir alana veri tipi olarak atadık. İşte iç içe yapı kullanımı bu şekilde tanımlanmaktadır. User yapı nesnesi için bir yapıcı metot oluşturalım. func NewUser() *User { u := new(User) u.Pay = NewPayment() return u } Uygulamada kullanacağımız yapı ve yapıcı metotları hazırladık. Şimdi bu yapılar ile birlikte kullanacağımız fonksiyonları oluşturalım. Kullanıcının ad ve soyadını birleştirerek geriye dönen GetFullName() fonksiyonun: func (u User) GetFullName() string { return u.FirstName + " " + u.LastName } Kullanıcının kullanıcı adını dönen fonksiyon: func (u *User) GetUserName() string { return u.UserName } Kullanıcının “maaş + bonus” algoritmasına göre, kullanıcının elde ettiği aylık kazanımımı geri döner. func (u *User) GetPayment() float64 { pay := u.Pay.Salary + u.Pay.Bonus return pay } Şu ana kadar kullanacağımız genel uygulama yapısı hazır. Şimdi bu uygulamayı main() metodu içerisinde kullanarak inceleyelim. Versiyon 1: fmt.Println("Kullanıcı oluşturma v1") u1 := &User{ ID: 1, FirstName: "Cihan", LastName: "Özhan", UserName: "CihanOzhan", Age: 30, Pay: &Payment{ Salary: 2550, Bonus: 700, }, cihanozhan.com
  • 29. } fmt.Println(u1.Pay) fmt.Println(u1.GetUserName()) fmt.Println(u1.GetFullName()) fmt.Println("Maaş: " + strconv.FormatFloat(u1.GetPayment(), 'g', -1, 64)) // Not : Maaşı gösterebilmek için float64 veri tipini string'e dönüştürdük. Versiyon 1 çıktısı: Kullanıcı oluşturma v1 &{2550 700} CihanOzhan Cihan Özhan Maaş : 3250 Versiyon 1 ile sık kullanılan bir yöntemi inceledik. Bu kodlama tarzını profesyonel Go uygulamalarında sık sık görebilirsiniz. Şimdi de farklı bir modelle aynı işlemleri gerçekleştirelim. Versiyon 2: fmt.Println("Kullanıcı oluşturma v2") u2 := NewUser() u2.FirstName = "Cihan" u2.LastName = "Özhan" u2.Age = 30 u2.UserName = "Gopher" Versiyon 2 için Payment nesnesini oluşturma yöntemi 1: u2.Pay.Salary = 5600 u2.Pay.Bonus = 580 Versiyon 2 için Payment nesnesini oluşturma yöntemi 2: u2.Pay = &Payment{Salary: 5600, Bonus: 580} Ve artık hazır olan u2 nesne örneğini kullanabiliriz. fmt.Println(u2.Pay) fmt.Println(u2.GetUserName()) fmt.Println(u2.GetFullName()) fmt.Println("Maaş : " + strconv.FormatFloat(u2.GetPayment(), 'g', -1, 64)) // Not : Maaşı gösterebilmek için float64 veri tipini string'e dönüştürdük. cihanozhan.com
  • 30. Versiyon 2 çıktısı: Kullanıcı oluşturma v2 &{5600 580} GODER Cihan Özhan Maaş : 6180 cihanozhan.com