Как стать автором
Обновить

Комментарии 15

А чем вас не устраивает перекрытие метода?

type repeater struct {
	speaker
	timesToRepeat int
}

func (r repeater) Speak() {
	for i := 0; i < r.timesToRepeat; i++ {
		r.speaker.Speak()
	}
}

func NewRepeater(m string, c int) Speaker {
	return repeater{speaker: speaker{message: m}, timesToRepeat: c}
}

прям как на hackerrank попал, сначала выстрадать свою реализацию, потом подглядеть в ответах легковесное решение.

  1. Добрый день.
    Спасибо за критику. Мне конечно понятно, что для решения данного учебного примера есть более простое и явное решение - затенение метода. Но я в самом начале написал, что назначение статьи : показать еще один аспект GO.

Я не критикую, лишь описал что сам сталкивался с подобной ситуацией.

Добрый день.
Спасибо за критику. Мне конечно понятно, что для решения данного учебного примера есть более простое и явное решение - затенение метода. Но я в самом начале написал, что назначение статьи : показать еще один аспект GO.

Это была не критика, а вопрос, в первую очередь. Хотелось понять, вдруг есть какая-то необходимость, которую сразу не видно, только "в перспективе". Я допускаю, что ваш подход в каких-то условиях будет отличным решением, но пока не вижу, в каких.

Вы пишете:

GO позволяет хранить функции в структуре!

Выглядит это так, как будто вы нашли какую-то недокументированную "фичу" или какой-то особый, сильно неочевидный случай. Но ведь это не так, в документации Go (как и, пожалуй, в любой книге по языке) говорится, что функции являются объектами первого класса. Не сочтите за занудство, но оперировать функциями в Go как значениями переменных, полей структур, аргументов других функций --- это нормально. Просто хотелось бы вас предостеречь от следующей статьи в духе "В Go можно передавать функции в другие функции! Функциональщине быть!!!" или чего-то подобного )

Ручное управление полиморфностью вызовов --- ИМО, зло ) но, ещё раз повторюсь, допускаю, что в каких-то условиях это будет лучшим решением. К счастью, я с такими условиями не сталкивался )

Кстати, а почему это называется "перекрытие"?

Структуры speaker и repeater не образуют дерево наследования. Вместо этого repeater инкапсулирует в себя speaker, и, реализуя интерфейс Speaker, оборачивает speaker своей логикой.

На мой взгляд, это обычная инкапсуляция, существующая во многих языках программирования. Часто вложенное поле speaker называется delegate.

Кстати, пример можно улучшить, если внутри структуры repeater ссылаться не на структуру speaker, а на интерфейс Speaker. Это позволит вкладывать структуры друг в друга многократно, как матрёшка.

Можно назвать затенение, например. Методы вложенной структуры видны во внешней напрямую, не только через .speaker. Разумеется, ровно до тех пор, пока для внешней структуры не будет задан метод с тем же именем.

Понял, спасибо!

А, собственно, для чего этот оверинжениринг? В чём проблема подхода:

type speaker struct {
	message string
}

func (s speaker) Speak() {
	fmt.Println(s.message)
}

type repeater struct {
	speaker
	repeatTimes int
}

func (r repeater) Speak() {
	for i := 0; i < r.repeatTimes; i++ {
		r.speaker.Speak()
	}
}

В комментариях еще и принцип единственной ответственности не нарушен. А в статье, при изменении требований, возникает необходимость изменять существующие сущности. Ну просто потому что никаким образом нельзя предусмотреть, что реализацию интерфейсного метода потребуется делегировать. Тут уж или сразу закладывать правильное делегирование через введение интерфейса стратегии, или не оверинжинирить, как и рекомендуют комментарии.

Автор молодец, поизобретает велосипеды, а потом, станет писать проще, у всех разный путь😀 мы начинали тоже с велосипедова, да и до сих пор, кажется, пишем, просто колёса всё круглее становятся

Добрый день.

Да, в процессе изучения "изобретение велосипедов" неизбежная вещь. Но согласитесь: статья открывает новые аспекты GO для новичков. Полезно знать, что такая возможность как хранение ссылки на метод в структуре и его полиморфный вызов -есть.

«хипстеры изобрели С++ VMT»

пока что копируется указатель на функцию; а если функций надо 10? каждый объект будет заполнять 10 ссылок? и т.д.
где-то в 1989-1991 году изобрели вмт, до этого слоты в лиспе и виртуальные методы в модуле-2 и клу

Автор почему-то упустил основной смысл такого подхода.

func (s speaker) toSpeak() {
fmt.Println("prepare to speak")
s.callBackFunc() //<- теперь здесь будет вызов той функции, что была сохранена в поле структуры!
fmt.Println("do something after")
}

То есть вынос общей базовой части внутри которой вызывается переопределенный метод потомка. Перекрытие методов такое не даст.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории