Files
build-web-application-with-…/tr/02.5.md
Murat b5167c40bb Update 02.5.md
02.5 devam
2019-07-07 23:35:53 +03:00

9.6 KiB
Raw Blame History

Nesne-Yönelim

Son iki bölüm de fonksiyonlar ve struct hakkında konuştuk, ancak fonksiyonları struct ın bir alanı olarak kullanmayı düşündünüz mü? Bu bölümde, alıcısı olan method olarak adlandırılan başka bir fonksiyon biçimi tanıtacağım.

method

Bir "Dikdortgen" struct tanımladığınızı ve onun alanını hesaplamak istediğinizi varsayalım. Bu amaca ulaşmak için genellikle aşağıdaki kodu kullanırdık.

package main

import "fmt"

type Dikdortgen struct {
	genislik, yukseklik float64
}

func alan(r Dikdortgen) float64 {
	return r.genislik * r.yukseklik
}

func main() {
	d1 := Dikdortgen{12, 2}
	d2 := Dikdortgen{9, 4}
	fmt.Println("d1 in alanı: ", alan(d1))
	fmt.Println("d2 nin alanı: ", alan(d2))
}

Yukarıdaki örnek bir Dikdortgen'in alanını hesaplayabilir. Burada alan isimli fonksiyonu kullanıyoruz, fakat bu fonksiyon Dikdortgen struct'ının bir method u değil(klasik nesne yönelimli dillerdeki sınıf nethodları gibi). Fark edebileceğiniz gibi fonksiyon ve struct iki bağımsız şeydir.

Şimdiye kadar sorun değil. Bununla birlikte, bir dairenin, karenin, beşgenin veya başka herhangi bir şeklin alanını da hesaplamanız gerekiyorsa, çok benzer adlara sahip ek fonksiyonlar eklemeniz gerekecektir.

Figure 2.8 Fonksiyon ve struct arasındaki ilişki

ıkçası bu hiç hoş değil. Ayrıca, alan fonksiyonu bir dairenin veya dikdörtgenin özelliği olmalıdır.

İşte method un devreye gireceği nokta burasıdır. method, bir türe/tipe bağlı fonksiyondur. func anahtar sözcüğünün, bu methodun ana gövdesi olan receiver adlı bit parametreye sahip olması haricinde, fonksiyonlar benzer bir sözdizimi vardır.

Aynı örneği kullanarak, Dikdortgen.Alan() çevresel bir fonksiyon yerine doğrudan Dikdortgen'e aittir. Daha spesifik olarak, yukseklik, genislik ve Alan() hepsi Dikdortgen'e aittir.

Rob Pike'ın dediği gibi.

"Bir method, alıcı olarak adlandırılan ilk örtülü argüman olan bir fonksiyondur."

method sözdimi.

func (r ReceiverType) funcName(parameters) (results)

Örneğimizi method kullanarak değiştirelim.

package main

import (
	"fmt"
	"math"
)

type Daire struct {
	yaricap float64
}

type Dikdortgen struct {
	genislik, yukseklik float64
}

// method
func (daire Daire) Alan() float64 {
	return daire.yaricap * daire.yaricap * math.Pi
}

// method
func (dikdortgen Dikdortgen) Alan() float64 {
	return dikdortgen.genislik * dikdortgen.yukseklik
}

func main() {
	daire1 := Daire{10}
	daire2 := Daire{25}
	dikdortgen1 := Dikdortgen{9, 4}
	dikdortgen2 := Dikdortgen{12, 2}

	fmt.Println("daire1 in alanı: ", daire1.Alan())
	fmt.Println("daire2 nin alanı: ", daire2.Alan())
	fmt.Println("dikdortgen1 in alanı: ", dikdortgen1.Alan())
	fmt.Println("dikdortgen2 nin alanı: ", dikdortgen2.Alan())
}

Methodları kullanma notları.

  • Methodların adı aynıysa ancak aynı alıcıları paylaşmıyorlarsa, aynı değildirler.
  • Methodlar alıcılardaki alanlara erişebilirler.
  • Bir struct da method çağırmak için . kullanın, aynı şekilde alanları da çağırabilirsiniz.

Figure 2.9 Methodlar farklı struct larda farklıdırlar.

Yukarıdaki örnakte Alan() methodu hem Dikdortgen hem de Daire'de mevcuttur, bu nedenle alıcıları Dikdortgen ve Daire'dir.

One thing that's worth noting is that the method with a dotted line means the receiver is passed by value, not by reference. The difference between them is that a method can change its receiver's values when the receiver is passed by reference, and it gets a copy of the receiver when the receiver is passed by value.

Alıcı sadece bir struct'mı olmalıdır? Tabii ki değil. Herhangibir tür/tip methodun alıcısı olabilir. Özelleştirilmiş türler/tipler hakkında kafanız karışabilir. struct özel bir tür/tip dir - daha birçok özelleştirilmiş tür/tip yer almaktadır.

Özelleştirilmiş bir tür/tip tanımlamak için aşağıdaki formatı kullanın.

type typeName typeLiteral

Özelleştirilmiş türler/tipler e örnekler:

type yas int
type fiyat float32
type aylar map[string]int

a := aylar {
	"Ocak":31,
	"Şubat":28,
	...
	"Aralık":31,
}

Özelleştirilmiş türlerin/tiplerin artık nasıl kullanılacağını biliyorsunuz. C programlama dilindeki typedef ifadesine benzer bir şekilde, yukarıdaki örnekte int yerine yas kullanıyoruz.

method hakkında konuşmaya geri dönelim.

Özelleştirilmiş türler/tipler de istediğiniz kadar method kullanabilirsiniz.

package main

import "fmt"

const (
	BEYAZ = iota
	SIYAH
	MAVI
	KIRMIZI
	SARI
)

type Kutu struct {
	genislik, yukseklik, derinlik float64
	renk Renk
}
type Renk byte
type KutuListesi []Kutu //kutu listesi

// method
func (kutu Kutu) Hacim() float64 {
	return kutu.genislik * kutu.yukseklik * kutu.derinlik
}

// işaretçi alıcılı method
func (k *Kutu) RenkAta(r Renk) {
	k.renk = r
}

// method
func (kl KutuListesi) EnBuyuklerininRengi() Color {
	h := 0.00
	r := Renk(BEYAZ)
	for _, k := range kl {
		if k.Hacim() > h {
			h = k.Hacim()
			r = k.renk
		}
	}
	return k
}

// method
func (kl KutuListesi) SiyahaBoya() {
	for i, _ := range kl {
		kl[i].RenkAta(SIYAH)
	}
}

// method
func (r Renk) String() string {
	strings := []string{"BEYAZ", "SIYAH", "MAVI", "KIRMIZI", "SARI"}
	return strings[r]
}

func main() {
	kutular := KutuListesi{
		Kutu{4, 4, 4, KIRMIZI},
		Kutu{10, 10, 1, SARI},
		Kutu{1, 1, 20, SIYAH},
		Kutu{10, 10, 1, MAVI},
		Kutu{10, 30, 1, BEYAZ},
		Kutu{20, 20, 20, SARI},
	}

	fmt.Printf("%d adet kutumuz var\n", len(kutular))
	fmt.Println("İlk kutunun hacmi ", kutular[0].Hacim(), "cm³")
	fmt.Println("Son kutunun rengi", kutular[len(kutular)-1].renk.String())
	fmt.Println("En büyük kutu", kutular.EnBuyuklerininRengi().String())

	// Hepsini siyaha boyayalım
	boxes.SiyahaBoya()

	fmt.Println("İkinci kutunun rengi", kutular[1].renk.String())
	fmt.Println("En büyüğü", kutular.EnBuyuklerininRengi().String())
}

Bazı sabitleri ve özelleştirilmiş tür/tipleri tanımlarız

  • Renk 'i byte 'ın takma adı olarak kullanın.
  • Hacim, yukseklik, genislik, derinlik ve renk alanlarını içeren Kutu struct ını tanımlayın.
  • Kutu yu alanı olarak alan KutuListesi strruct ı tanımlayın.

Daha sonra özelleştirilmiş türlerimiz/tiplerimiz için bazı methodlar tanımladık.

  • Hacim() Kutu yu alıcısı olarak kullanır ve onun hacmini döndürür.
  • RenkAta(r Renk) Kutu'nun rengini değiştirir.
  • EnBuyuklerininRengi() en büyük hacimli Kutu'nun rengini döndürür.
  • SiyahaBoya() KutuListesi içindeki tüm Kutu'ların rengini SIYAH yapar.
  • String() Renk'i alıcısı olarak kullanır ve Renk adını string olarak döndürür.

Gereksinimlerimizi tanımlamak için kelimeler kullandığımız zaman daha açıklayıcı değil mi?

İşaretçiyi alıcı olarak kullanmak

RenkAta metoduna bir göz atalım. Alıcısı Kutunun bir işaretçisidir. Evet, *Kutuyu alıcı olarak kullanabilirsiniz. Neden burada bir işaretçi kullanıyoruz? Çünkü bu methodda Kutu'nun rengini değiştirmek istiyoruz. Dolayısıyla, bir işaretçi kullanmazsak, bu yalnızca Kutu'nun kopyasındaki değeri değiştirir.

Bir alıcının bir methodun ilk argümanı olduğunu görürsek, nasıl çalıştığını anlamak zor değildir.

Neden RenkAta() methodunda k.Renk = r yerine (*k).Renk = r kullanmadığımızı soruyor olabilirsiniz. Her iki kullanımda da sorun yok çünkü Go bu görevi nasıl gerçekleştireceğini bilir. Şimdi Go'nun daha büyüleyici olduğunu düşünmüyor musunuz?

You may also be asking whether we should use (&kl[i]).RenkAta(SIYAH) in SiyahaBoya because we pass a pointer to RenkAta. Again, either one is OK because Go knows how to interpret it!

Inheritance of method

We learned about inheritance of fields in the last section. Similarly, we also have method inheritance in Go. If an anonymous field has methods, then the struct that contains the field will have all the methods from it as well.

package main

import "fmt"

type Human struct {
	name  string
	age   int
	phone string
}

type Student struct {
	Human  // anonymous field
	school string
}

type Employee struct {
	Human
	company string
}

// define a method in Human
func (h *Human) SayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

func main() {
	sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
	mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}

	sam.SayHi()
	mark.SayHi()
}

Method Overriding

If we want Employee to have its own method SayHi, we can define a method that has the same name in Employee, and it will hide SayHi in Human when we call it.

package main

import "fmt"

type Human struct {
	name  string
	age   int
	phone string
}

type Student struct {
	Human
	school string
}

type Employee struct {
	Human
	company string
}

func (h *Human) SayHi() {
	fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

func (e *Employee) SayHi() {
	fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
		e.company, e.phone) //Yes you can split into 2 lines here.
}

func main() {
	sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
	mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}

	sam.SayHi()
	mark.SayHi()
}

You are able to write an Object-oriented program now, and methods use rule of capital letter to decide whether public or private as well.