Почему метод Go Receiving Types не может быть интерфейсом? - PullRequest
5 голосов
/ 04 июня 2011

Из Go документации по объявлениям методов :

Тип получателя должен иметь форму T или * T, где T - имя типа. T называется базовым типом приемника или просто базовым типом. Базовый тип не должен быть указателем или типом интерфейса и должен быть объявлен в том же пакете, что и метод.

Может кто-нибудь дать мне некоторое представление о том, почему это может быть? Существуют ли другие (статически типизированные) языки, которые позволили бы это? Я действительно хочу определить методы в интерфейсе, чтобы я мог рассматривать любой экземпляр данного типа интерфейса как другой. Например (кража примера из статьи Википедии о шаблонном шаблонном методе ), если верно следующее:

type Game interface {
    PlayOneGame(playersCount int)
}

type GameImplementation interface {
    InitializeGame()
    MakePlay(player int)
    EndOfGame() bool
    PrintWinner()
}

func (game *GameImplementation) PlayOneGame(playersCount int) {
    game.InitializeGame()
    for j := 0; !game.EndOfGame(); j = (j + 1) % playersCount {
        game.MakePlay(j)
    }
    game.PrintWinner()
}

Я мог бы использовать любой экземпляр, реализующий «GameImplementation» как «Игру» без какого-либо преобразования:

var newGame Game
newGame = NewMonopolyGame() // implements GameImplementation
newGame.PlayOneGame(2)

ОБНОВЛЕНИЕ: цель этого состояла в том, чтобы попытаться достичь всех преимуществ абстрактных базовых классов без всякой связи, которая идет с явной иерархией. Если бы я хотел определить новое поведение PlayBestOfThreeGames, абстрактные базовые классы потребовали бы от меня самого самого базового класса, тогда как здесь я просто определяю еще один метод поверх интерфейса GameImplementation

Ответы [ 4 ]

3 голосов
/ 04 июня 2011

Вероятно, по той же причине, по которой вы не можете определять методы для интерфейсов в Java.

Интерфейс предназначен для описания части или всего внешнего интерфейса для набора.объектов, а не как они реализуют базовое поведение.В Java вы, вероятно, использовали бы абстрактный класс, если вам нужно, чтобы части поведения были предварительно определены, но я думаю, что единственный способ сделать это в Go - это использовать функции, а не методы.

Я считаю, что дляВ вашем примере более идиоматический код Go будет выглядеть примерно так:

type GameImplementation interface {
    InitializeGame()
    MakePlay(player int)
    EndOfGame() bool
    PrintWinner()
}

func PlayOneGame(game GameImplementation, playersCount int) {
    game.InitializeGame()
    for j := 0; !game.EndOfGame(); j = (j + 1) % playersCount {
        game.MakePlay(j)
    }
    game.PrintWinner()
}

Где PlayOneGame и любая конкретная реализация игры, вероятно, живут в разных пакетах.

Вот некоторые обсужденияgolang гайки

1 голос
/ 05 июня 2011

То, что вы описываете как в Интерфейсе, на самом деле то, что в другом месте может называться абстрактным классом, то есть классом с некоторыми определенными методами, но не всеми, которые должны быть разделены на подклассы для создания экземпляра.

Однако у Go нет понятия иерархии классов - вся структура типов плоская.Каждый метод в классе определен специально для этого класса, а не в каком-либо родительском классе, подклассе или интерфейсе.Это было сознательное проектное решение, а не упущение.

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

Это делаетневозможно использовать такие шаблоны, как Generics with Go, и Роб Пайк сказал на конференции, что это может измениться в будущем, если кто-то может прийти с элегантной реализацией и убедительным вариантом использования.Но это еще предстоит выяснить.

1 голос
/ 05 июня 2011

В ответ на ваш вопрос о том, существуют ли другие статически типизированные языки, которые позволяют это: да, большинство.Любой язык с множественным наследованием позволяет классам иметь произвольные смеси абстрактных и конкретных методов.Также смотрите черты Scala, которые похожи на интерфейсы Java, но могут иметь конкретные методы.Scala также имеет структурные типы, которые на самом деле являются всеми интерфейсами Go.

0 голосов
/ 06 августа 2015

Во-первых, важно отметить, что типы реализуют интерфейсы неявно , то есть интерфейсы являются "типами утки". Любой тип, который предоставляет методы, требуемые интерфейсом, может быть назначен переменной типа интерфейса без какого-либо взаимодействия с исходным типом. Это отличается от, скажем, Java или C #, где класс, реализующий интерфейс, должен объявить свое намерение реализовать интерфейс, в дополнение к фактическому предоставлению методов.

Го также имеет довольно сильную тенденцию против "действия на расстоянии". Например, хотя методы объявляются отдельно от типов, нельзя объявлять метод в пакете, отличном от типа его получателя. Вы не можете просто добавить методы к os.File.

Если интерфейсы могут предоставлять методы (делая их черты / роли ), то любой тип, который реализует интерфейс, получит кучу новых методов из ниоткуда. Кто-то, читающий код и видящий эти методы, вероятно, имеет трудно понять, откуда они пришли.

Существует проблема с хрупкостью - измените сигнатуру метода, который требуется интерфейсу, и куча других методов появляются или исчезают. В случае, когда они исчезли, не очевидно, откуда они «взялись». Если типы должны были объявить о своем намерении реализовать интерфейс, то нарушение контракта вызовет ошибку (а «случайная» реализация интерфейса ничего не даст), но когда интерфейсы удовлетворяются неявным образом, все становится сложнее.

Хуже того, могут быть конфликты имен - интерфейс предоставляет метод с тем же именем, что и метод, предоставленный типом, реализующим этот интерфейс, или два интерфейса предоставляют метод с одинаковым именем, и некоторый тип реализуется оба этих интерфейса. Разрешение этого конфликта - это та сложность, которой Го действительно нравится избегать, и во многих случаях не является неудовлетворительным разрешением.

По сути, было бы действительно здорово, если бы интерфейсы могли предоставлять методы - роли как составные единицы поведения - это круто и хорошо сочетались бы с философией компоновки-наследования Go - но на самом деле делать это было бы слишком сложно и слишком действенно. -a-distance-y для Go, чтобы созерцать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...