web-dev-qa-db-ja.com

Goのデコレーター関数

デコレータパターン(関数)には 多くの利点

メソッドに多くの直交する懸念がある場合に非常に役立ちます。つまり、メソッドを呼び出すときは常にそれらのすべて(または一部)を実行したいということを除いて、これらの懸念のどれも関連していません。これは、デコレータパターンが本当に役立つ場所です。

デコレータパターンを実装することにより、open-closedプリンシパルにサブスクライブします。私たちの方法は、将来の拡張に対してオープンですが、将来の変更に対してクローズされています。開閉の原則に従うことには、多くのグルーヴィーなメリットがあります。

ただし、私が見つけたすべての例は非常に複雑です(たとえば、多くのミドルウェアを使用してHTTPサーバーを作成するなど)。これは私が他の場所で原則を適用することを困難にします。頭に巻けるように簡単に着れるものが必要です。

Goでデコレーターパターン(関数)を実行する方法を最もよく説明できる簡単な例を誰かに教えてもらえますか?

Alex Alehanoによるこの例 は単純すぎて実用化できません。これを説明できるものが必要です:

func Decorate(c Decorated, ds ...Decorator) Decorated {
    decorated := c
    for _, decorate := range ds {
        decorated = decorate(decorated)
    }
    return decorated
}

さまざまなオプション/命令に応じた文字列操作(たとえば、上、下、base64など)は、IMOの最良の例であり、プレフィックス/サフィックスも " この手法は、デコレータ自体がパラメータ化されている場合に特に価値があります "。

12
xpt

まず、デコレータは基本的に、特定の型の別の関数を引数として取り、同じ型の関数を返す関数です。これにより、基本的に関数のチェーンを作成できます。したがって、Goでは次のようになります。

// this is the type of functions you want to decorate
type StringManipulator func(string) string

// this is your decorator.
func ToLower(m StringManipulator) StringManipulator {
    return func(s string) string {
        lower := strings.ToLower(s)
        return m(lower)
    }
}

これはより完全な例です

11
mkopriva

あなたがデモンストレーションしたのと同じテクニックを利用するデコレータ/ミドルウェアの非常に良い例を知っています。これは文字列操作に固有のものではありませんが、本当に美しく作成されていることがわかります。

https://github.com/basvanbeek/pubsub/blob/master/kafka/publisher.go

type PublisherOption func(*publisherConfig) errorをご覧ください

一部の関数がPublisherOptionタイプを返すことに気づくでしょう。これらはデコレータ/ミドルウェアです。

アクションはNewKafkaPublisher関数で発生します:

func NewKafkaPublisher(
  broker, topic string,
  options ...PublisherOption,
  ) (pubsub.Publisher, error) {
  if len(strings.Trim(broker, " \t")) == 0 {
      return nil, ErrNoBrokers
  }
  brokerHosts := strings.Split(broker, ",")

  if len(topic) == 0 {
      return nil, ErrNoTopic
  }
  // set sensible defaults
  pc := &publisherConfig{
    syncPublisher: defaultSyncPublisher,
    ackMode:       defaultRequiredAcks,
    successes:     nil,
    logger:        log.NewNopLogger(),
    topic:         topic,
    partitioner:   sarama.NewManualPartitioner(topic),
    partition:     0,
  }

 // parse optional parameters
 for _, option := range options {
     if err := option(pc); err != nil {
        return nil, err
     }
 }

optionをループして、各デコレータ/ミドルウェアを呼び出すことに注意してください。

補足的な例はここにあります: https://Gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9

実行可能なコード: https://play.golang.org/p/ZXixnyTHXH

それが役に立てば幸い:)

4
Steven Ferrer

異なるオプション/命令による文字列操作、たとえば、上へ、下へ、接頭辞/接尾辞の追加、base64への追加などは、IMOの最良の例です。

これはあなたが尋ねた例です:

_package main

import (
    "fmt"
    "strings"
)

const (
    prefix = "PREFIX"
    suffix = "SUFFIX"
)

type Decorated=string

func addConstPrefix(s string) string {
    return prefix + s
}

func addConstSuffix(s string) string {
    return s + suffix
}

type Decorator=func(string) string

func Decorate(c Decorated, ds ...Decorator) Decorated {
    decorated := c
    for _, decorator := range ds {
        decorated = decorator(decorated)
    }
    return decorated
}

func main() {

    var toLower Decorator = strings.ToLower
    var toUpper Decorator = strings.ToUpper
    var dec3 Decorator = addConstPrefix
    var dec4 Decorator = addConstSuffix

    input := "Let's decorate me!"
    inputUppercase := Decorate(input, []Decorator{toUpper}...)
    fmt.Println(inputUppercase)

    allDecorators := []Decorator{toUpper, toLower, dec3, dec4}
    output := Decorate(input, allDecorators...)
    fmt.Println(output)
}
_

簡単にするために、Go 1.9で導入されたGolangのtype aliasesを使用していることに注意してください。これらは、使用している2つのエイリアスです。

_type Decorated=string
type Decorator=func(string)string
_

Decorate()関数を後で適用できるようにします。次に、定義した型シグネチャに一致するいくつかのデコレータ(関数)を作成します。

_var toLower Decorator = strings.ToLower
var toUpper Decorator = strings.ToUpper
var dec3 Decorator = addConstPrefix
var dec4 Decorator = addConstSuffix
_

そしてそれらを適用します:

_allDecorators := []Decorator{toUpper, toLower, dec3, dec4}
output := Decorate(input, allDecorators...)
_

編集:

parametrizedデコレータは、単に別の関数を返す関数です。たとえば、次のようになります。

_func addPrefix(prefix string) func(string) string {
    return func(s string) string {
        return prefix + s
    }
}
_

その後、次の方法で適用できます。

_input2 := "Let's decorate me!"
prefixed := Decorate(input2, []Decorator{addPrefix("Well, ")}...)
_
4
syntagma