web-dev-qa-db-ja.com

メソッドチェーンとエラー処理を実行します

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がエラーを報告し、そのチェーンを停止できるようにするための良い方法はありますか?

20
johannes

FuncA、funcB、funcCがエラーを報告し、そのチェーンを停止できるようにするための良い方法はありますか?

残念ながら、いいえ、あなたの問題に対する良い解決策はありません。回避策は十分に複雑であるため(エラーチャネルの追加など)、コストがゲインを上回ります。

メソッドチェーンはGoのイディオムではありません(少なくとも、エラーが発生する可能性のあるメソッドではそうではありません)。これは、メソッドチェーンに特に問題があるためではなく、パニックではなくエラーを返すというイディオムの結果です。他の答えは回避策ですが、慣用的なものはありません。

Goの場合のようにエラーを返す結果のために、Goでメソッドをチェーンするのは慣用的ではありませんか、それとも、より一般的には、複数のメソッドを返すことの結果ですか?

良い質問ですが、Goが複数の返品をサポートしているからではありません。 Pythonは複数の戻り値をサポートし、JavaはTuple<T1, T2>クラスを介しても可能です。メソッドチェーンは両方の言語で共通です。これらの言語が取得できる理由例外を介してエラーを慣例的に伝達するためです。例外はメソッドチェーンをすぐに停止し、関連する例外ハンドラーにジャンプします。これは、Go開発者が代わりにエラーを返すことを選択することで、特に回避しようとした動作です。

35
weberc2

次のように試すことができます: 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)
}
4
Kiura

コードを制御でき、関数のシグネチャが同一である場合は、次のように記述できます。

func ChainCall(fns ...func() (*Chain, error)) (err error) {
    for _, fn := range fns {
        if _, err = fn(); err != nil {
            break
        }
    }
    return
}

playground

2
OneOfOne

ファンションのスライスを集めることでチェーンを怠惰にすることができます

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)
}
1

このアプローチはどうですか:Chainerrorを委任する構造体を作成し、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)
}
0
Not_a_Golfer