---
id: ebf46089667a6469b258b8e52e57571f
author: Yunjin Lee
title: 'Interfețele Go și inserarea structurilor (struct embedding): diferențele față de moștenirea claselor și proiectarea prin compoziție'
description: Compară diferențele dintre moștenirea claselor din Java/C++ și tipizarea structurală a interfețelor din Go și explică, prin exemple de cod, modelul de compoziție bazat pe embedding de structuri și delegarea metodelor.
language: ro
date: 2025-07-17T10:40:02.284435348Z
path: /blog/posts/go-interfaces-are-not-inheritance-zd04c1084
category: language
---

# Prezentare generală

Interfețele din Go permit ca mai multe structuri să aibă cu ușurință funcții cu aceiași argumenți și aceleași valori de returnare, dar acest lucru diferă de modul în care extends din Java prelungește și suprascrie în mod adecvat comportamentul acelor funcții interne. Doar înțelegând corect reutilizarea compozițională a codului în Go nu vom confunda lucrurile cu moștenirea, însă a obține o înțelegere teoretică perfectă de la bun început este dificil. Să le explorăm împreună printr-un scenariu în care se greșește ușor.

## Greșeli frecvente

Începătorii pot face următoarele greșeli.

```go
package main
import (
	"fmt"
	"strings"
)

type Fruits interface {
	GetBrix() float64
	GetName() string
	SetLabel()
	GetLabel(string) string
	PrintAll()
}

type Apple struct {
	Label string
	Name  string
	Brix  float64
}

type Watermelon struct {
	Label string
	Name  string
	Brix  float64
}

func (a *Apple) PrintAll() {
	fmt.Printf("Fruit: %s, Label: %s, Brix: %v\n", a.Name, a.Label, a.Brix)
}

const (
	NO_LABEL = "EMPTY LABEL"
)

func (a *Apple) SetLabel(lbl string) {
	a.Brix 	= 14.5;
	a.Name 	= "apple";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, a.Name) {
		fmt.Println("Succeed: Label was ", lbl)
		a.Label = lbl;
	} else {
		fmt.Println("Failed: Label was ", lbl)
		a.Label = NO_LABEL;
	}
}

func (w *Watermelon) SetLabel(lbl string) {
	w.Brix = 10;
	w.Name = "watermelon";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, w.Name) {
		w.Label = lbl;
	} else {
		w.Label = NO_LABEL;
	}
}

func main() {
	fmt.Println("Inheritance test #1")
	apple := new(Apple)
	watermelon := apple
	apple.SetLabel("Apple_1")
	fmt.Println("Apple, before copied to Watermelon")
	apple.PrintAll()
	watermelon.SetLabel("WaterMelon_2")
	fmt.Println("Apple, after copied to Watermelon")
	apple.PrintAll()
	fmt.Println("Watermelon, which inherited Apple's Method")
	watermelon.PrintAll()
}
```

Dacă gândiți că Go urmează moștenirea tradițională, acest cod pare fără probleme. Totuși, rezultatul său este următorul.

```
Inheritance test #1
Succeed: Label was  Apple_1
Apple, before copied to Watermelon
Fruit: apple, Label: Apple_1, Brix: 14.5
Failed: Label was  WaterMelon_2
Apple, after copied to Watermelon
Fruit: apple, Label: EMPTY LABEL, Brix: 14.5
Watermelon, which inherited Apple's Method
Fruit: apple, Label: EMPTY LABEL, Brix: 14.5
```

Aici comportamentul Go devine doar mai clar prin:

```go
watermelon := apple
```

Această linie nu transformă deloc Apple în clasa Watermelon.
Watermelon este pur și simplu un pointer către apple.

Subliniem din nou: **Go nu urmează conceptul tradițional de moștenire.**

Dacă scriem cod având această înțelegere greșită, apar erori grave precum crearea de pointeri inutili sau copierea neintenționată a funcțiilor pentru alte structuri.

Atunci, care este codul exemplar?

## Un exemplu potrivit în Go

```go
package main
import (
	"fmt"
	"strings"
)

type Fruits interface {
	GetBrix() float64
	GetName() string
	SetLabel()
	GetLabel(string) string
	PrintAll()
}

type BaseFruit struct {
	Name  string
	Brix  float64
}

type Apple struct {
	Label string
	Fruit BaseFruit
}

type Watermelon struct {
	Label string
	Fruit BaseFruit

}

func (b *BaseFruit) PrintAll() {
	fmt.Printf("Fruit: %s, Brix: %v\n", b.Name, b.Brix)
}


const (
	NO_LABEL = "EMPTY LABEL"
)

func (a *Apple) SetLabel(lbl string) {
	a.Fruit.Brix 	= 14.5;
	a.Fruit.Name 	= "apple";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, a.Fruit.Name) {
		fmt.Println("Succeed: Label was ", lbl)
		a.Label = lbl;
	} else {
		fmt.Println("Failed: Label was ", lbl)
		a.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", a.Fruit.Name, a.Label);
	a.Fruit.PrintAll()
}

func (w *Watermelon) SetLabel(lbl string) {
	w.Fruit.Brix = 10;
	w.Fruit.Name = "Watermelon";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, w.Fruit.Name) {
		w.Label = lbl;
	} else {
		w.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", w.Fruit.Name, w.Label);
	w.Fruit.PrintAll()
}

func main() {
	apple := new(Apple)
	watermelon := new(Watermelon)
	apple.SetLabel("Apple_1")
	watermelon.SetLabel("WaterMelon_2")
}
```

Totuși, în Go este posibil și să se obțină un aspect **asemănător** moștenirii.
Este vorba de embedding anonim. Acesta se realizează declarând structura internă fără nume.
În acest caz, câmpurile structurii inferioare pot fi accesate direct, **chiar fără a le specifica explicit**.
Folosind acest model de **promovare** a câmpurilor structurii inferioare către structura superioară, lizibilitatea poate fi îmbunătățită în anumite situații. Totuși, în cazurile în care structura inferioară trebuie prezentată explicit, recomandăm să nu se folosească această metodă.

```go
package main
import (
	"fmt"
	"strings"
)

type Fruits interface {
	GetBrix() float64
	GetName() string
	SetLabel()
	GetLabel(string) string
	PrintAll()
}

type BaseFruit struct {
	Name  string
	Brix  float64
}

type Apple struct {
	Label string
	BaseFruit
}

type Watermelon struct {
	Label string
	BaseFruit

}

func (b *BaseFruit) PrintAll() {
	fmt.Printf("Fruit: %s, Brix: %v\n", b.Name, b.Brix)
}


const (
	NO_LABEL = "EMPTY LABEL"
)

func (a *Apple) SetLabel(lbl string) {
	a.Brix 	= 14.5;
	a.Name 	= "apple";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, a.Name) {
		fmt.Println("Succeed: Label was ", lbl)
		a.Label = lbl;
	} else {
		fmt.Println("Failed: Label was ", lbl)
		a.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", a.Name, a.Label);
	a.PrintAll()
}

func (w *Watermelon) SetLabel(lbl string) {
	w.Brix = 10;
	w.Name = "Watermelon";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, w.Name) {
		w.Label = lbl;
	} else {
		w.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", w.Name, w.Label);
	w.PrintAll()
}

func main() {
	apple := new(Apple)
	watermelon := new(Watermelon)
	apple.SetLabel("Apple_1")
	watermelon.SetLabel("WaterMelon_2")
}
```

În acest exemplu există următoarea diferență.

```go
w.PrintAll() // Apelare automată prin promovare, realizată prin structura anonimă, în loc de w.Friut.PrintAll()
```

Pentru ambele exemple, punctele importante sunt următoarele:

- main să fie simplu, funcțiile pe funcționalități
- structuri diferite înseamnă obiecte diferite
- pentru partajare, se folosește structura internă

Ce avantaje are această filozofie de programare?

## Avantaje

- Distincție clară între metodele care necesită partajare și cele care nu
- Separarea responsabilităților pe structuri și metode individuale
- Cod separat structural, în funcție de specificațiile funcționalităților necesare

La început, Go poate părea neobișnuit deoarece diferă de OOP tradițional, dar odată ce vă obișnuiți, devine posibilă programarea explicită.

## Rezumat

- Izolați responsabilitățile
- Împărțiți pe niveluri de structuri
- Metodele nu trebuie înțelese ca clasele abstracte din Java
- Scrieți programe explicite și concrete
  Go trebuie tratat mai simplu și mai clar decât modelul OOP tradițional, componente separate individual. În loc să programăm extensiv, să scriem cod separat în pași și structurat.
