Можно ли определить реализацию анонимного интерфейса в Go? - PullRequest
0 голосов
/ 10 января 2019

Рассмотрим некоторый данный интерфейс и функцию воображаемой библиотеки, которая использует его как

// Binary and Ternary operation on ints
type NumOp interface {
    Binary(int, int) int
    Ternary(int, int, int) int
}

func RandomNumOp(op NumOp) {
    var (
        a = rand.Intn(100) - 50
        b = rand.Intn(100) - 50
        c = rand.Intn(100) - 50
    )
    fmt.Printf("%d <op> %d = %d\n", a, b, op.Binary(a,b))
    fmt.Printf("%d <op> %d <op> %d = %d\n", a, b, c, op.Ternary(a,b,c))
}

Возможный тип, реализующий этот интерфейс:

// MyAdd defines additions on 2 or 3 int variables
type MyAdd struct {}
func (MyAdd) Binary(a, b int) int {return a + b }
func (MyAdd) Ternary(a, b, c int) int {return a + b + c }

Я имею дело со многими различными интерфейсами, определяющими несколько функций, которые в некоторых случаях должны быть реализованы с использованием функций, в основном работающих как NOP -подобные операции, не полагающихся на какой-либо член * struct и являющиеся только используется в одной позиции в проекте (повторное использование не требуется).

Существует ли более простой (менее подробный) способ в Go для определения (предпочтительно) анонимной реализации интерфейса с использованием анонимных функций, например (псевдокод, я знаю, что он не работает таким образом):

RandomNumOp({
   Binary: func(a,b int) int { return a+b},
   Ternary: func(a,b,c int) int {return a+b+c},
})

1 Ответ

0 голосов
/ 10 января 2019

Если реализация должна работать

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

Объявления методов должны быть на верхнем уровне (уровень файла). И реализовать интерфейс с более чем 0 методами, для которых требуется где-то иметь объявления методов.

Конечно, вы можете использовать структуру и встраивать существующую реализацию, но опять же, она требует уже существующей реализации, методы которой уже должны быть определены «где-то»: на уровне файлов.

Если вам нужна «фиктивная», но работоспособная реализация, они используют / pass любую реализацию , например. значение вашего MyAdd типа. Если вы хотите подчеркнуть, что реализация не имеет значения, то создайте фиктивную реализацию, имя которой означает, что:

type DummyOp struct{}

func (DummyOp) Binary(_, _ int) int     { return 0 }
func (DummyOp) Ternary(_, _, _ int) int { return 0 }

Если вам нужно динамически предоставить реализацию для некоторых методов, вы можете создать тип структуры делегатора, который содержит функции для методов, и фактические методы проверяют, установлена ​​ли соответствующая функция, в которой если это называется, иначе ничего не будет сделано.

Вот как это может выглядеть:

type CustomOp struct {
    binary  func(int, int) int
    ternary func(int, int, int) int
}

func (cop CustomOp) Binary(a, b int) int {
    if cop.binary != nil {
        return cop.binary(a, b)
    }
    return 0
}

func (cop CustomOp) Ternary(a, b, c int) int {
    if cop.ternary != nil {
        return cop.ternary(a, b, c)
    }
    return 0
}

При его использовании вы можете предоставить только часть функций, остальные будут недоступны:

RandomNumOp(CustomOp{
    binary: func(a, b int) int { return a + b },
})

Если реализация не требуется для работы

Если вам нужно только значение, которое реализует интерфейс, но вам не требуется, чтобы его методы были «вызываемыми» (чтобы не вызывать панику при вызове), вы можете просто использовать анонимный литерал структуры, встраивая тип интерфейса:

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