46 / 68

Struct va interfeys ichiga joylashtirish (Embedding)

Goda "meros" (inheritance) yo'q — buning o'rniga nima bor?

Java yoki Python kabi tillarda class'lar bir-biridan meros olishi (inheritance) orqali umumiy xatti-harakatni bo'lishadi. Goda klassik meros olish tushunchasi umuman yo'q — buning o'rniga Go kompozitsiya (composition) falsafasiga tayanadi: kichik, mustaqil bo'laklarni bir-biriga "joylashtirib" (embedding), yangi, kengroq imkoniyatli tur yasaladi.

Bu dars — struct (19-dars) va interface (30-dars) darslarining bevosita davomi bo'lib, ularga tegishli, ammo ilgari ko'rilmagan embedding mexanizmini chuqur yoritadi.


1. Struct'ni struct ichiga joylashtirish

example.go
package main

import "fmt"

type Animal struct {
    Name string
}

func (a Animal) Eat() {
    fmt.Println(a.Name, "ovqatlanmoqda")
}

// Dog o'z ichiga Animal'ni "joylashtirib" (embed qilib) oladi
type Dog struct {
    Animal // e'tibor bering: maydon nomi yo'q, faqat tur nomi!
    Breed  string
}

func main() {
    d := Dog{
        Animal: Animal{Name: "Rex"},
        Breed:  "Ovcharka",
    }

    // Dog'ning o'zida Name maydoni yo'q, lekin Animal'dan "meros" bo'lgandek ishlatiladi
    fmt.Println(d.Name)

    // Xuddi shunday, Animal'ning Eat() metodi ham Dog orqali to'g'ridan-to'g'ri chaqiriladi
    d.Eat()

    // to'liq yo'l bilan ham murojaat qilish mumkin
    fmt.Println(d.Animal.Name)
}

Natija:

kod
Rex
Rex ovqatlanmoqda
Rex

[!IMPORTANT] Animal Animal deb emas, faqat Animal deb yozilganiga e'tibor bering — maydon nomi ko'rsatilmagan, faqat tur nomi yozilgan. Aynan shu — embeddingni oddiy "struct ichida struct" (masalan, Engine Engine kabi nomlangan maydon)dan farqlaydi: embedding qilinganda, ichki turning barcha ochiq (eksport qilingan) maydonlari va metodlari tashqi turga avtomatik "ko'chib o'tadi" (bu — promotion, ko'tarilish deyiladi).


2. Metodni "qayta belgilash" (Override'ga o'xshash xatti-harakat)

Agar tashqi tur ichki tur bilan bir xil nomdagi metodni o'zi ham belgilasa, tashqi turning o'z metodi ustunlik qiladi:

example.go
package main

import "fmt"

type Base struct{}

func (b Base) Greet() string {
    return "Salom, Base"
}

type Derived struct {
    Base
}

// Derived o'zining Greet() metodini belgilaydi
func (d Derived) Greet() string {
    return "Salom, Derived (o'zining metodi)"
}

func main() {
    d := Derived{}

    fmt.Println(d.Greet())      // Derived'ning o'z metodi ishlaydi
    fmt.Println(d.Base.Greet()) // Base'nikini ham alohida chaqirish mumkin
}

Natija:

kod
Salom, Derived (o'zining metodi)
Salom, Base

[!NOTE] Bu klassik ob'ektga yo'naltirilgan tillardagi "override" (qayta belgilash)ga juda o'xshaydi, lekin texnik jihatdan farq bor: bu yerda polimorfizm yo'q — Go kompilyatsiya vaqtida qaysi Greet() chaqirilishini aniq biladi (bu — statik bog'lanish), boshqa tillardagi kabi dinamik (runtime'da aniqlanadigan) emas.


3. Interfeysni interfeys ichiga joylashtirish

Xuddi struct kabi, interfeyslar ham bir-biriga joylashtirilishi mumkin — bu kichik interfeyslardan kattaroq interfeys yasash imkonini beradi:

example.go
package main

import "fmt"

type Reader interface {
    Read() string
}

type Writer interface {
    Write(string)
}

// ReadWriter — Reader va Writer'ning barcha metodlarini birlashtiradi
type ReadWriter interface {
    Reader
    Writer
}

type File struct {
    content string
}

func (f *File) Read() string {
    return f.content
}

func (f *File) Write(s string) {
    f.content = s
}

func main() {
    var rw ReadWriter = &File{}

    rw.Write("Salom")
    fmt.Println(rw.Read())
}

Natija:

kod
Salom

*File turi Read() va Write() metodlarining ikkalasini ham amalga oshirgani uchun, u avtomatik ravishda ReadWriter interfeysini ham qanoatlantiradi — buni alohida e'lon qilishning hojati yo'q (interfeyslar implicit — "yashirin" tarzda amalga oshiriladi, buni Interfeys darsida ko'rgan edik).

[!NOTE] Standart kutubxonadagi io.ReadWriter aynan shu pattern bo'yicha, io.Reader va io.Writerni birlashtirib yasalgan (io darsida ko'rgan edik).


4. Nega bu "meros olish"dan yaxshiroq?

Meros olish (boshqa tillarda)Kompozitsiya / Embedding (Goda)
Qattiq, ierarxik bog'liqlik (Dog extends Animal)Erkin, moslashuvchan birlashtirish
Bir nechta ota-class'dan meros olish ko'pincha muammoliBir nechta turni bemalol birga joylashtirish mumkin
Chuqur ierarxiyalar tushunishni qiyinlashtiradiHar bir bo'lak mustaqil va alohida tushunarli
example.go
// Goda bir nechta turni bemalol birga joylashtirish mumkin — muammosiz:
type Robot struct {
    Animal   // harakat va ovqatlanish xatti-harakati (masalan)
    Engine   // dvigatel xususiyatlari
    Metadata // umumiy ma'lumotlar
}

Xulosa

TushunchaMa'nosi
Struct embeddingBir struct'ni ikkinchisi ichiga (nomsiz) joylashtirish
Promotion (ko'tarilish)Ichki turning maydon/metodlari tashqi tur orqali to'g'ridan-to'g'ri ishlatilishi
Metodni qayta belgilashTashqi tur ichki turdagi bilan bir xil nomli metod yozsa, o'ziniki ustunlik qiladi
Interface embeddingKichik interfeyslarni birlashtirib, kattaroq interfeys yasash

[!IMPORTANT] Embedding — Goning "kichik interfeyslar va aniq tuzilmalar yordamida katta narsalarni qurish" falsafasining markazida turadi. Standart kutubxonaning ko'p qismi (io.ReadWriter, http.Handler va h.k.) aynan shu prinsipga asoslangan.