GoでメソッドチェーンAPIを作成したいと思います。すべての例で、連鎖操作は常に成功しているように見えますが、これは保証できません。したがって、これらを拡張してエラーの戻り値を追加しようとしています。
こんな風にやれば
_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がエラーを報告し、そのチェーンを停止できるようにするための良い方法はありますか?
FuncA、funcB、funcCがエラーを報告し、そのチェーンを停止できるようにするための良い方法はありますか?
残念ながら、いいえ、あなたの問題に対する良い解決策はありません。回避策は十分に複雑であるため(エラーチャネルの追加など)、コストがゲインを上回ります。
メソッドチェーンはGoのイディオムではありません(少なくとも、エラーが発生する可能性のあるメソッドではそうではありません)。これは、メソッドチェーンに特に問題があるためではなく、パニックではなくエラーを返すというイディオムの結果です。他の答えは回避策ですが、慣用的なものはありません。
Goの場合のようにエラーを返す結果のために、Goでメソッドをチェーンするのは慣用的ではありませんか、それとも、より一般的には、複数のメソッドを返すことの結果ですか?
良い質問ですが、Goが複数の返品をサポートしているからではありません。 Pythonは複数の戻り値をサポートし、JavaはTuple<T1, T2>
クラスを介しても可能です。メソッドチェーンは両方の言語で共通です。これらの言語が取得できる理由例外を介してエラーを慣例的に伝達するためです。例外はメソッドチェーンをすぐに停止し、関連する例外ハンドラーにジャンプします。これは、Go開発者が代わりにエラーを返すことを選択することで、特に回避しようとした動作です。
次のように試すことができます: https://play.golang.org/p/dVn_DGWt1p_H
package main
import (
"errors"
"fmt"
)
type Chain struct {
err error
}
func (v *Chain) funA() *Chain {
if v.err != nil {
return v
}
fmt.Println("A")
return v
}
func (v *Chain) funB() *Chain {
if v.err != nil {
return v
}
v.err = errors.New("error at funB")
fmt.Println("B")
return v
}
func (v *Chain) funC() *Chain {
if v.err != nil {
return v
}
fmt.Println("C")
return v
}
func main() {
c := Chain{}
d := c.funA().funB().funC()
fmt.Println(d.err)
}
コードを制御でき、関数のシグネチャが同一である場合は、次のように記述できます。
func ChainCall(fns ...func() (*Chain, error)) (err error) {
for _, fn := range fns {
if _, err = fn(); err != nil {
break
}
}
return
}
ファンションのスライスを集めることでチェーンを怠惰にすることができます
package main
import (
"fmt"
)
type (
chainFunc func() error
funcsChain struct {
funcs []chainFunc
}
)
func Chain() funcsChain {
return funcsChain{}
}
func (chain funcsChain) Say(s string) funcsChain {
f := func() error {
fmt.Println(s)
return nil
}
return funcsChain{append(chain.funcs, f)}
}
func (chain funcsChain) TryToSay(s string) funcsChain {
f := func() error {
return fmt.Errorf("don't speek golish")
}
return funcsChain{append(chain.funcs, f)}
}
func (chain funcsChain) Execute() (i int, err error) {
for i, f := range chain.funcs {
if err := f(); err != nil {
return i, err
}
}
return -1, nil
}
func main() {
i, err := Chain().
Say("Hello, playground").
TryToSay("go cannot into chains").
Execute()
fmt.Printf("i: %d, err: %s", i, err)
}
このアプローチはどうですか:Chain
とerror
を委任する構造体を作成し、2つの値の代わりにそれを返します。例えば。:
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)
}