GoSuda

Go Arayüzleri Kalıtım Değildir

By Yunjin Lee
views ...

Genel Bakış

Go Interface'leri, aynı argümanlara ve dönüş değerlerine sahip fonksiyonların birden fazla struct'ta kolayca bulunmasına olanak tanırken, Java'nın extends anahtar kelimesi gibi içsel fonksiyonların davranışını uygun şekilde genişletme ve override etme yönteminden farklıdır. Go'nun yapısal kod tekrar kullanımını doğru anlamak, kalıtımla karıştırılmasını önleyecektir; ancak başlangıçtan itibaren teorik olarak mükemmel bir anlayışa sahip olmak zordur. Hata yapmaya uygun senaryolarla birlikte inceleyelim.

Sık Yapılan Hatalar

Yeni başlayanlar aşağıdaki hataları yapabilirler:

 1package main
 2import (
 3	"fmt"
 4	"strings"
 5)
 6
 7type Fruits interface {
 8	GetBrix() float64
 9	GetName() string
10	SetLabel()
11	GetLabel(string) string
12	PrintAll()
13}
14
15type Apple struct {
16	Label string
17	Name  string
18	Brix  float64
19}
20
21type Watermelon struct {
22	Label string
23	Name  string
24	Brix  float64
25}
26
27func (a *Apple) PrintAll() {
28	fmt.Printf("Fruit: %s, Label: %s, Brix: %v\n", a.Name, a.Label, a.Brix)
29}
30
31const (
32	NO_LABEL = "EMPTY LABEL"
33)
34
35func (a *Apple) SetLabel(lbl string) {
36	a.Brix 	= 14.5;
37	a.Name 	= "apple";
38	lbl_lower := strings.ToLower(lbl)
39	if strings.Contains(lbl_lower, a.Name) {
40		fmt.Println("Succeed: Label was ", lbl)
41		a.Label = lbl;
42	} else {
43		fmt.Println("Failed: Label was ", lbl)
44		a.Label = NO_LABEL;
45	}
46}
47
48func (w *Watermelon) SetLabel(lbl string) {
49	w.Brix = 10;
50	w.Name = "watermelon";
51	lbl_lower := strings.ToLower(lbl)
52	if strings.Contains(lbl_lower, w.Name) {
53		w.Label = lbl;
54	} else {
55		w.Label = NO_LABEL;
56	}
57}
58
59func main() {
60	fmt.Println("Inheritance test #1")
61	apple := new(Apple)
62	watermelon := apple
63	apple.SetLabel("Apple_1")
64	fmt.Println("Apple, before copied to Watermelon")
65	apple.PrintAll()
66	watermelon.SetLabel("WaterMelon_2")
67	fmt.Println("Apple, after copied to Watermelon")
68	apple.PrintAll()
69	fmt.Println("Watermelon, which inherited Apple's Method")
70	watermelon.PrintAll()
71}

Bu tür bir kod, Go'nun geleneksel kalıtımı takip ettiğini varsaydığınızda sorunsuz görünebilir. Ancak, çıktısı aşağıdaki gibidir:

1Inheritance test #1
2Succeed: Label was  Apple_1
3Apple, before copied to Watermelon
4Fruit: apple, Label: Apple_1, Brix: 14.5
5Failed: Label was  WaterMelon_2
6Apple, after copied to Watermelon
7Fruit: apple, Label: EMPTY LABEL, Brix: 14.5
8Watermelon, which inherited Apple's Method
9Fruit: apple, Label: EMPTY LABEL, Brix: 14.5

Burada Go'nun davranışı açıkça ortaya çıkmaktadır.

1watermelon := apple

Bu kod, Apple'ı Watermelon sınıfına doğrudan dönüştürmez. Watermelon sadece apple'a bir işaretçidir.

Burada tekrar vurgulamak gerekir ki, Go geleneksel kalıtım kavramını takip etmez.

Bu yanlış anlama ile kod yazıldığında, anlamsız işaretçi oluşturma, beklenmedik şekilde başka struct'lar için fonksiyon kopyalama gibi kritik hatalar meydana gelir.

Peki, örnek bir kod nasıl olmalıdır?

Go Dilinde Uygun Örnek

 1package main
 2import (
 3	"fmt"
 4	"strings"
 5)
 6
 7type Fruits interface {
 8	GetBrix() float64
 9	GetName() string
10	SetLabel()
11	GetLabel(string) string
12	PrintAll()
13}
14
15type BaseFruit struct {
16	Name  string
17	Brix  float64
18}
19
20type Apple struct {
21	Label string
22	Fruit BaseFruit
23}
24
25type Watermelon struct {
26	Label string
27	Fruit BaseFruit
28
29}
30
31func (b *BaseFruit) PrintAll() {
32	fmt.Printf("Fruit: %s, Brix: %v\n", b.Name, b.Brix)
33}
34
35
36const (
37	NO_LABEL = "EMPTY LABEL"
38)
39
40func (a *Apple) SetLabel(lbl string) {
41	a.Fruit.Brix 	= 14.5;
42	a.Fruit.Name 	= "apple";
43	lbl_lower := strings.ToLower(lbl)
44	if strings.Contains(lbl_lower, a.Fruit.Name) {
45		fmt.Println("Succeed: Label was ", lbl)
46		a.Label = lbl;
47	} else {
48		fmt.Println("Failed: Label was ", lbl)
49		a.Label = NO_LABEL;
50	}
51	fmt.Printf("Fruit %s label set to %s\n", a.Fruit.Name, a.Label);
52	a.Fruit.PrintAll()
53}
54
55func (w *Watermelon) SetLabel(lbl string) {
56	w.Fruit.Brix = 10;
57	w.Fruit.Name = "Watermelon";
58	lbl_lower := strings.ToLower(lbl)
59	if strings.Contains(lbl_lower, w.Fruit.Name) {
60		w.Label = lbl;
61	} else {
62		w.Label = NO_LABEL;
63	}
64	fmt.Printf("Fruit %s label set to %s\n", w.Fruit.Name, w.Label);
65	w.Fruit.PrintAll()
66}
67
68func main() {
69	apple := new(Apple)
70	watermelon := new(Watermelon)
71	apple.SetLabel("Apple_1")
72	watermelon.SetLabel("WaterMelon_2")
73}

Ancak, Go'da kalıtım gibi görünmesini sağlamak mümkündür. Bu, anonim gömme adı verilen bir örnektir. İçsel struct'ı isimsiz bir struct olarak bildirerek bu mümkündür. Bu gibi durumlarda, alt struct'ın alanlarına açıkça belirtilmeden doğrudan erişilebilir. Alt struct'ın alanlarını üst struct'a yükseltme bu deseni kullanmak, duruma göre okunabilirliği artırabilir. Ancak, alt struct'ın açıkça gösterilmesi gereken durumlarda kullanılmaması tavsiye edilir.

 1package main
 2import (
 3	"fmt"
 4	"strings"
 5)
 6
 7type Fruits interface {
 8	GetBrix() float64
 9	GetName() string
10	SetLabel()
11	GetLabel(string) string
12	PrintAll()
13}
14
15type BaseFruit struct {
16	Name  string
17	Brix  float64
18}
19
20type Apple struct {
21	Label string
22	BaseFruit
23}
24
25type Watermelon struct {
26	Label string
27	BaseFruit
28
29}
30
31func (b *BaseFruit) PrintAll() {
32	fmt.Printf("Fruit: %s, Brix: %v\n", b.Name, b.Brix)
33}
34
35
36const (
37	NO_LABEL = "EMPTY LABEL"
38)
39
40func (a *Apple) SetLabel(lbl string) {
41	a.Brix 	= 14.5;
42	a.Name 	= "apple";
43	lbl_lower := strings.ToLower(lbl)
44	if strings.Contains(lbl_lower, a.Name) {
45		fmt.Println("Succeed: Label was ", lbl)
46		a.Label = lbl;
47	} else {
48		fmt.Println("Failed: Label was ", lbl)
49		a.Label = NO_LABEL;
50	}
51	fmt.Printf("Fruit %s label set to %s\n", a.Name, a.Label);
52	a.PrintAll()
53}
54
55func (w *Watermelon) SetLabel(lbl string) {
56	w.Brix = 10;
57	w.Name = "Watermelon";
58	lbl_lower := strings.ToLower(lbl)
59	if strings.Contains(lbl_lower, w.Name) {
60		w.Label = lbl;
61	} else {
62		w.Label = NO_LABEL;
63	}
64	fmt.Printf("Fruit %s label set to %s\n", w.Name, w.Label);
65	w.PrintAll()
66}
67
68func main() {
69	apple := new(Apple)
70	watermelon := new(Watermelon)
71	apple.SetLabel("Apple_1")
72	watermelon.SetLabel("WaterMelon_2")
73}

Bu örnekte şu farklılıklar bulunmaktadır:

 1w.PrintAll() // w.Fruit.PrintAll() değil, isimsiz struct aracılığıyla otomatik yükseltme çağrısı
 2Her iki örnekte de önemli noktalar şunlardır:
 3- main basit tutulmalı, fonksiyonlar işleve göre ayrılmalı
 4- Farklı struct'lar için farklı nesneler kullanılmalı
 5- Paylaşım gerekiyorsa içsel struct'lar kullanılmalı
 6
 7Bu programlama felsefesinin ne gibi avantajları vardır?
 8
 9## Avantajlar
10
11- Paylaşım gerektiren ve gerektirmeyen metotların net ayrımı
12- Bireysel struct'lar ve metotlar için sorumluluğun ayrılması
13- Gerekli işlevsellik belirtimlerine göre yapısal olarak ayrılmış kod
14
15Başlangıçta Go dili, geleneksel OOP'den farklı olduğu için alışılmadık gelebilir, ancak alıştıkça daha açık bir programlama mümkün hale gelir.
16
17## Özet
18- Sorumlulukları izole edelim.
19- Struct birimleri halinde ayrıntılı olarak bölelim.
20- Metotlar Java'daki soyut sınıflar gibi anlaşılmamalıdır.
21- Açık ve somut programlama yapalım.
22Go dili, geleneksel OOP modelinden daha basit, net ve bireysel olarak ele alınmalıdır. Genişletilebilir programlama yapmak yerine, adım adım ve yapısal olarak ayırarak yazmaya özen gösterelim.
23