---
id: ebf46089667a6469b258b8e52e57571f
author: Yunjin Lee
title: 'Rozhraní a vložení struktur v Go: rozdíly oproti dědičnosti tříd a kompoziční návrh'
description: Srovnává rozdíly mezi dědičností tříd v Javě/C++ a strukturálním typováním pomocí rozhraní v Go a pomocí příkladů kódu vysvětluje kompozici (Composition) založenou na vnoření struktur (struct embedding) a delegování metod.
language: cs
date: 2025-07-17T10:40:02.284435348Z
path: /blog/posts/go-interfaces-are-not-inheritance-zd04c1084
category: language
---

# Přehled

Rozhraní (interface) v Go umožňuje snadno mít v několika structech funkce se stejnými argumenty a návratovými hodnotami, avšak nejde o způsob, kterým by se, podobně jako klíčové slovo extends v Javě, vhodně rozšiřovalo a přepisovalo chování těchto vnitřních funkcí. Abyste se nezaměnili s dědičností, musíte správně pochopit kompoziční znovupoužití kódu v Go, ale od začátku je obtížné dosáhnout dokonalého teoretického porozumění. Pojďme si to prozkoumat na scénářích, kde se snadno uděláte chybu.

## Časté chyby

Začátečníci mohou udělat následující chyby.

```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()
}
```

Pokud by se na tento kód díval někdo, kdo se domnívá, že Go následuje tradiční dědičnost, zdá se být v pořádku. Jeho výstup je však následující.

```
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
```

Zde se chování Go stává jasným pouze částečně:

```go
watermelon := apple
```

Tento kód vůbec nepřeměňuje Apple na třídu Watermelon.
Proměnná watermelon je pouze ukazatelem (pointerem) na apple.

Zdůrazňuji ještě jednou: **Go nenásleduje koncept tradiční dědičnosti.**

Pokud píšete kód s tímto nedorozuměním, vznikají kritické chyby, jako je zbytečné vytváření ukazatelů nebo neočekávané kopírování funkcí pro jiné structy.

Jak tedy vypadá vzorový kód?

## Vhodný příklad v jazyce 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")
}
```

Go však umožňuje, aby to **vypadalo jako** dědičnost.
Jde o anonymní vkládání (embedding). Vnitřní struct lze deklarovat jako struct bez jména, a tím se tento vzor stane možným.
V takovém případě je možné přistupovat k polím podřízeného structu **i bez explicitního uvedení** jeho názvu.
Tento vzor, při kterém se pole podřízeného structu **povyšují** do nadřazeného structu, může v některých případech zlepšit čitelnost. Pokud je však nutné podřízený struct explicitně zobrazit, doporučujeme jej nepoužívat.

```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")
}
```

V tomto příkladu je tento rozdíl:

```go
w.PrintAll() // Volání provedené automaticky přes anonymní struct, nikoli w.Fruit.PrintAll()
```

Oba příklady mají tyto důležité body:

- main je jednoduchý, funkce jsou rozděleny podle funkcionality
- pro různé structy používejte různé objekty
- pokud je potřeba sdílení, použijte vnitřní struct

Jaké výhody má tato programátorská filozofie?

## Výhody

- Jasné rozlišení metod, které je třeba sdílet, a těch, které nikoli
- Oddělení odpovědnosti jednotlivých structů a metod
- Strukturálně oddělený kód podle specifikace požadovaných funkcí

Zpočátku nemusí být jazyk Go známý, protože se liší od tradičního OOP, ale jakmile si na něj zvyknete, umožní vám explicitní programování.

## Shrnutí

- Izolujte odpovědnost
- Dělte na úrovni jednotlivých structů
- Metody nesmíte chápat jako abstraktní třídy z Javy
- Pište explicitní a konkrétní kód
  Jazyk Go je jednodušší a přímočařejší než tradiční model OOP a je třeba s ním pracovat samostatně. Místo rozšiřujícího programování pište kód krok za krokem a strukturovaně, s jasným oddělením částí.
