Herança
Herança é um dos pilares da programação orientada a objetos, e é uma das formas de reutilização de código. A herança é um mecanismo que permite que uma classe herde atributos e métodos de outra classe, chamada de superclasse ou classe base. A classe que herda os atributos e métodos é chamada de subclasse ou classe derivada.
Principais Conceitos
- Superclasse (Classe Base): A classe cujos atributos e métodos são herdados por outras classes. É a classe “pai”.
- Subclasse (Classe Derivada): A classe que herda atributos e métodos da superclasse. É a classe “filha”.
- Herança Simples: Quando uma subclasse herda de uma única superclasse.
- Herança Múltipla: Quando uma subclasse herda de mais de uma superclasse. Nem todas as linguagens de programação suportam herança múltipla devido à sua complexidade.
- Sobrescrita de Método: A subclasse pode fornecer uma implementação específica de um método que já existe na superclasse.
Herança & Composição não são a mesma coisa
Composição e herança são duas formas de reutilização de código em programação orientada a objetos. A herança é uma forma de reutilização de código que permite que uma classe herde atributos e métodos de outra classe. A composição é uma forma de reutilização de código que permite que uma classe contenha objetos de outras classes. A composição é geralmente preferida à herança, pois é mais flexível e menos propensa a problemas de design.
Como funciona em Go
Go não possui herança como em linguagens orientadas a objetos clássicas. Ao invés de herança, Go utiliza composição e interfaces para alcançar o mesmo comportamento. Geralmente, a composição é feita através de structs (estruturas) e interfaces.
package main import "fmt" // Veiculo é uma interface que define um método dados que retorna uma string type Veiculo interface { dados() string } // Carro é uma struct que representa um carro type Carro struct { marca string modelo string } // dados é um método que retorna uma string com os dados do carro func (c Carro) dados() string { return fmt.Sprintf("Marca: %s, Modelo: %s", c.marca, c.modelo) } // Hatch é uma struct que representa um carro do tipo hatch type Hatch struct { Carro portas int } // dados é um método que retorna uma string com os dados do carro hatch func (h Hatch) dados() string { return fmt.Sprintf("Marca: %s, Modelo: %s, Portas: %d", h.marca, h.modelo, h.portas) } // Sedan é uma struct que representa um carro do tipo sedan type Sedan struct { Carro portaMalas int } // dados é um método que retorna uma string com os dados do carro sedan func (s Sedan) dados() string { return fmt.Sprintf("Marca: %s, Modelo: %s, Porta Malas: %d", s.marca, s.modelo, s.portaMalas) } type Conversivel struct { Carro capota bool } func (c Conversivel) dados() string { return fmt.Sprintf("%s, Capota: %t", c.Carro.dados(), c.capota) } // imprimirDados é uma função que recebe um veículo e imprime os dados do veículo func imprimirDados(v Veiculo) { fmt.Println(v.dados()) } func main() { // Acessando atributos hatch := Hatch{Carro{"Chevrolet", "Onix"}, 4} sedan := Sedan{Carro{"Honda", "Civic"}, 500} // Acessando métodos dados da struct Carro de forma explícita conversivel := Conversivel{Carro{"Fiat", "Spyder"}, true} imprimirDados(hatch) imprimirDados(sedan) imprimirDados(conversivel) } heranca main 29m ➜ go run main.go Marca: Chevrolet, Modelo: Onix, Portas: 4 Marca: Honda, Modelo: Civic, Porta Malas: 500 Marca: Fiat, Modelo: Spyder, Capota: true Neste exemplo, temos uma interface Veiculo que define um método dados que retorna uma string. Temos também uma struct Carro que representa um carro e um método dados que retorna uma string com os dados do carro. As structs Hatch e Sedan representam carros do tipo hatch e sedan, respectivamente. Ambas as structs Hatch e Sedan incorporam a struct Carro através da composição. Cada uma das structs Hatch e Sedan tem um método dados que retorna uma string com os dados do carro do tipo hatch ou sedan. A função imprimirDados recebe um veículo e imprime os dados do veículo.
Com a composição você pode acessar tantos os atributos quanto os métodos da struct incorporada. No entanto, se houver um método com o mesmo nome ele será sobrescrito.Você pode acessar o método da struct incorporada de forma explícita c.Carro.dados().
func (h Hatch) dados() string { return fmt.Sprintf("%s, Portas: %d", h.Carro.dados(), h.portas) } Conclusão
A herança é um mecanismo importante da programação orientada a objetos que permite a reutilização de código. No entanto, a herança pode levar a problemas de design, como acoplamento excessivo e hierarquias de classes profundas. Em Go, a linguagem não possui herança como em linguagens orientadas a objetos clássicas. Em vez disso, Go utiliza composição e interfaces para alcançar o mesmo comportamento. A composição é geralmente preferida à herança, pois é mais flexível e menos propensa a problemas de design.
Projeto
Referências
Wikipédia (Herança)
Wikipédia (Composição, herança e delegação)
Go: Composição vs Herança (Vinicius Pacheco)
Effective Go
The Go Programming Language Specification
Go by Example
Top comments (0)