Цепочка метода и обработка ошибок
Я хочу создать API цепочки методов в Go. Во всех примерах я могу найти, что прикованные операции кажутся всегда успешными, что я не могу гарантировать. Поэтому я стараюсь расширить их, чтобы добавить возвращаемое значение ошибки.
Если я сделаю это так,
package main
import "fmt"
type Chain struct {
}
func (v *Chain)funA() (*Chain, error ) {
fmt.Println("A")
return v, nil
}
func (v *Chain)funB() (*Chain, error) {
fmt.Println("B")
return v, nil
}
func (v *Chain)funC() (*Chain, error) {
fmt.Println("C")
return v, nil
}
func main() {
fmt.Println("Hello, playground")
c := Chain{}
d, err := c.funA().funB().funC() // line 24
}
Компилятор сообщает мне chain-err-test.go:24: multiple-value c.funA() in single-value context
и не будет компилироваться. Есть ли хороший способ, чтобы funcA, funcB и funcC могли сообщать об ошибке и останавливать эту цепочку?
Ответы
Ответ 1
Есть ли хороший способ, чтобы funcA, funcB и funcC могли сообщать об ошибке и останавливать эту цепочку?
К сожалению, нет, нет хорошего решения вашей проблемы. Временные решения достаточно сложны (добавление каналов ошибок и т.д.), Что стоимость превышает коэффициент усиления.
Цепочка метода не является идиомой в Go (по крайней мере, не для методов, которые могут привести к ошибке). Это происходит не потому, что в цепочках методов есть что-то особенно неправильное, а следствие идиомы возврата ошибок вместо паники. Другие ответы являются обходными решениями, но ни один из них не является идиоматическим.
Могу ли я спросить, не так ли идиоматично цеплять методы в Go из-за следствия возврата ошибки, как и в Go, или это в целом следствие того, что у вас есть несколько методов?
Хороший вопрос, но это не потому, что Go поддерживает несколько возвратов. Python поддерживает несколько возвратов, а Java тоже может быть через класс Tuple<T1, T2>
; Цепочки методов распространены на обоих языках. Причина, по которой эти языки могут уйти, заключается в том, что они идиоматически сообщают об ошибках через исключения. Исключения немедленно останавливают цепочку методов и переходят к соответствующему обработчику исключений. Это поведение, которое разработчики Go пытались избежать, выбрав вместо этого ошибки.
Ответ 2
Если у вас есть контроль над кодом, и подпись функции идентична, вы можете написать что-то вроде:
func ChainCall(fns ...func() (*Chain, error)) (err error) {
for _, fn := range fns {
if _, err = fn(); err != nil {
break
}
}
return
}
игровая площадка
Ответ 3
Как насчет этого подхода: создайте структуру, которая делегирует Chain
и error
, и вернет ее вместо двух значений. например:.
package main
import "fmt"
type Chain struct {
}
type ChainAndError struct {
*Chain
error
}
func (v *Chain)funA() ChainAndError {
fmt.Println("A")
return ChainAndError{v, nil}
}
func (v *Chain)funB() ChainAndError {
fmt.Println("B")
return ChainAndError{v, nil}
}
func (v *Chain)funC() ChainAndError {
fmt.Println("C")
return ChainAndError{v, nil}
}
func main() {
fmt.Println("Hello, playground")
c := Chain{}
result := c.funA().funB().funC() // line 24
fmt.Println(result.error)
}