Серия "Learn Go Language" - 26-я статья, в которой можно поделиться хорошей статьей
Сегодня я поделюсь с вами хорошей статьей об объектно-ориентированном Go. Первоначальный автор — Уильям Кеннеди, автор блога Go Language in Action.www.ardanlabs.com/blog/хранитель . Большинство китайских сусликов познакомились с Go God через этот блог.
Некоторые предложения, которые не имеют ничего общего с очками знаний, немного неудобно переводить, и все понимают их с первого взгляда. Однако те, кто имеет отношение к очкам знаний, будут стараться быть верными исходному тексту. Кроме того, статья сделана в простой верстке для удобства чтения. Уровень перевода ограничен. Если есть какая-либо ошибка, пожалуйста, оставьте сообщение ниже, чтобы исправить ее.
База
Кто-то спросил меня сегодня на форуме, как получить преимущества наследования без встраивания. Важно то, что все должны думать о языке Go, а не о языке, который они оставили позади. Я не могу сказать вам, сколько кода я удалил из более ранних реализаций Go, потому что это было ненужно. Разработчики языков обладают многолетним опытом и знаниями и помогают создавать язык, который является быстрым, компактным и очень интересным для программирования.
Я считаю Go легким объектно-ориентированным языком программирования. Да, у него есть инкапсуляция и функции-члены типов, но ему не хватает наследования и, следовательно, традиционного полиморфизма. Для меня наследование бесполезно, если вы не хотите добиться полиморфизма. Благодаря тому, как интерфейсы реализованы в Go, наследование не требуется. Go берет лучшие части ООП и игнорирует другие, предоставляя нам лучший способ написания полиморфного кода.
Ниже приведен краткий обзор ООП в Go. Начнем с этих трех структур.
type Animal struct {
Name string
mean bool
}
type Cat struct {
Basics Animal
MeowStrength int
}
type Dog struct {
Animal
BarkStrength int
}
В любом примере ООП вы, вероятно, увидите три приведенные выше структуры. Базовая структура Animal и структуры Cat и Dog, объявленные на основе Animal . Структура Animal обладает свойствами, общими для всех животных.
За исключением среднего члена, все остальные члены общедоступны и доступны извне. Средний член структуры Animal начинается со строчной буквы. В Go регистр первой буквы переменных, структур, членов, функций и т. д. определяет доступ. Начало заглавной буквы указывает на то, что оно является общедоступным и может вызываться извне; начало строчной буквы указывает на то, что оно является частным и не может вызываться извне.
Поскольку в Go нет наследования, единственным вариантом является композиция. Одним из членов структуры Cat является Basics, а тип — Animal. Структура Dog анонимно встраивает структуру Animal. Какая реализация лучше решать вам, я покажу обе реализации.
Создайте отдельные методы для структур Cat и Dog:
func (dog *Dog) MakeNoise() {
barkStrength := dog.BarkStrength
if dog.mean == true {
barkStrength = barkStrength * 5
}
for bark := 0; bark < barkStrength; bark++ {
fmt.Printf("BARK ")
}
fmt.Println("")
}
func (cat *Cat) MakeNoise() {
meowStrength := cat.MeowStrength
if cat.Basics.mean == true {
meowStrength = meowStrength * 5
}
for meow := 0; meow < meowStrength; meow++ {
fmt.Printf("MEOW ")
}
fmt.Println("")
}
Реализуйте соответствующий метод MakeNoise() с приемником указателя. Эти два метода делают одно и то же, если среднее значение верно, каждое животное будет говорить на своем родном языке в зависимости от интенсивности лая или мяуканья. Это скучно, но показывает, как получить доступ к указанному объекту.
Мы можем использовать ссылку Dog для прямого вызова членов структуры Animal, в то время как Cat должен обращаться к членам Animal через элемент Basics.
До сих пор мы обсуждали инкапсуляцию, композицию, спецификации доступа и функции-члены, а остальное — как создать полиморфное поведение.
Полиморфизм достигается через интерфейсы:
type AnimalSounder interface {
MakeNoise()
}
func MakeSomeNoise(animalSounder AnimalSounder) {
animalSounder.MakeNoise()
}
В приведенном выше коде мы добавляем интерфейс AnimalSounder и публичную функцию MakeSomeNoise(), которая принимает значение типа интерфейса. По сути, функция будет ссылаться на значение типа, реализующего этот интерфейс. Интерфейс — это не тип, который может быть создан, это объявление поведения, и другие типы могут реализовать поведение, объявленное интерфейсом.
В Го,Любой тип, который реализует интерфейс через методы, представляет тип интерфейса.. В нашем примере структуры Dog и Cat реализуют интерфейс AnimalSounder с приемниками указателей, поэтому их можно рассматривать как типы AnimalSounder.
Это означает, что указатели Dog и Cat могут быть переданы в качестве параметров функции MakeSomeNoise(). Функция MakeSomeNoise() реализует полиморфное поведение через интерфейс AnimalSounder.
Передовой
Если вы хотите уменьшить дублирование кода в методах MakeNoise() Cat и Dog, вы можете создать метод для типа Animal, чтобы справиться с этим:
func (animal *Animal) PerformNoise(strength int, sound string) {
if animal.mean == true {
strength = strength * 5
}
for voice := 0; voice < strength; voice++ {
fmt.Printf("%s ", sound)
}
fmt.Println("")
}
func (dog *Dog) MakeNoise() {
dog.PerformNoise(dog.BarkStrength, "BARK")
}
func (cat *Cat) MakeNoise() {
cat.Basics.PerformNoise(cat.MeowStrength, "MEOW")
}
Теперь тип Animal имеет метод обработки шума, который может быть вызван его внешними типами, такими как типы Dog, Cat. В качестве бонуса нам не нужно передавать средний элемент в качестве параметра, потому что он принадлежит структуре Animal. Вот полный код:
package main
import (
"fmt"
)
type Animal struct {
Name string
mean bool
}
type AnimalSounder interface {
MakeNoise()
}
type Dog struct {
Animal
BarkStrength int
}
type Cat struct {
Basics Animal
MeowStrength int
}
func main() {
myDog := &Dog{
Animal{
"Rover", // Name
false, // mean
},
2, // BarkStrength
}
myCat := &Cat{
Basics: Animal{
Name: "Julius",
mean: true,
},
MeowStrength: 3,
}
MakeSomeNoise(myDog)
MakeSomeNoise(myCat)
}
func (animal *Animal) PerformNoise(strength int, sound string) {
if animal.mean == true {
strength = strength * 5
}
for voice := 0; voice < strength; voice++ {
fmt.Printf("%s ", sound)
}
fmt.Println("")
}
func (dog *Dog) MakeNoise() {
dog.PerformNoise(dog.BarkStrength, "BARK")
}
func (cat *Cat) MakeNoise() {
cat.Basics.PerformNoise(cat.MeowStrength, "MEOW")
}
func MakeSomeNoise(animalSounder AnimalSounder) {
animalSounder.MakeNoise()
}
вывод:
BARK BARK
MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW MEOW
Встраивание интерфейса в структуру
Кто-то на заднем плане привел пример встраивания интерфейса в структуру:
package main
import (
"fmt"
)
type HornSounder interface {
SoundHorn()
}
type Vehicle struct {
List [2]HornSounder
}
type Car struct {
Sound string
}
type Bike struct {
Sound string
}
func main() {
vehicle := new(Vehicle)
vehicle.List[0] = &Car{"BEEP"}
vehicle.List[1] = &Bike{"RING"}
for _, hornSounder := range vehicle.List {
hornSounder.SoundHorn()
// PressHorn(hornSounder) 这种方式也可以
}
}
func (car *Car) SoundHorn() {
fmt.Println(car.Sound)
}
func (bike *Bike) SoundHorn() {
fmt.Println(bike.Sound)
}
func PressHorn(hornSounder HornSounder) {
hornSounder.SoundHorn()
}
В этом примере структура Vehicle поддерживает список значений, реализующих интерфейс HornSounder. Переменная Vehicle создается в основной функции, и сохраняются указатели на переменную типа Car и переменную Bike. Это назначение возможно, потому что и Car, и Bike реализуют интерфейс. Затем выполняется простая циклическая операция, которая вызывает метод SoundHorn() в цикле.
Все, что вам нужно для реализации объектной ориентации в вашем приложении, доступно в Go. Как я уже говорил, Go берет лучшие части ООП и исключает другие, что дает нам лучший способ написания полиморфного кода.
Несколько статей по теме:
1.Methods, Interfaces and Embedded Types in Go
2.How Packages Work in Go
3.Singleton Design Pattern in Go
Надеюсь, что эти несколько простых примеров помогут вам в программировании на Go!
Рекомендуемое чтение:
1.Учи свою девушку писать (продолжение)
2.Объектно-ориентированное программирование в Go