web-dev-qa-db-fra.com

Fonctions de décoration dans Go

Le modèle de décorateur (fonctions) a de nombreux avantages :

Il est très utile lorsqu'une méthode a de nombreuses préoccupations orthogonales ... C'est-à-dire qu'aucune de ces préoccupations n'est liée, à part que nous voulons toutes (ou certaines) toutes les fois que nous appelons notre méthode. C'est là que le motif décorateur aide vraiment.

En mettant en œuvre le modèle de décorateur, nous souscrivons au principe ouvert-fermé. Notre méthode est ouverte à de futures extensions mais fermée à de futures modifications. L'obéissance au principe ouvert-fermé présente de nombreux avantages groovy.

Cependant, tous les exemples que j'ai trouvés sont vraiment compliqués (par exemple, écrire des serveurs HTTP avec de nombreux middlewares). Cela rend difficile pour moi d'appliquer le principe ailleurs. J'ai besoin de quelque chose que je peux facilement essayer pour en faire le tour.

Quelqu'un peut-il me donner un exemple plus simple qui peut mieux illustrer comment faire un motif de décorateur (fonctions) dans Go s'il vous plaît?

Cet exemple d'Alex Alehano , est trop simple pour être mis en pratique. J'ai besoin de quelque chose qui puisse illustrer cela:

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

Une manipulation de chaîne selon différentes options/instructions, par exemple, en haut, en bas, en base64, etc., serait le meilleur exemple IMO, et en ajoutant également un préfixe/suffixe, comme " Cette technique s'avère particulièrement utile si les décorateurs eux-mêmes sont paramétrés ".

12
xpt

Tout d'abord, un décorateur est fondamentalement une fonction qui prend comme argument une autre fonction d'un type spécifique et renvoie une fonction du même type. Cela vous permet essentiellement de créer une chaîne de fonctions. Donc, dans Go, cela ressemblerait à ceci:

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

voici un exemple plus complet

11
mkopriva

Je connais un très bon exemple de décorateurs/middlewares qui utilise la même technique que vous avez démontrée. Ce n'est pas spécifique à la manipulation de chaînes, mais je le trouve vraiment magnifiquement conçu, le voici (parcourez le code):

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

Jetez un oeil à la type PublisherOption func(*publisherConfig) error

Vous remarquerez que certaines fonctions renvoient le type PublisherOption. Ce sont les décorateurs/middleware.

L'action se produit sur la fonction 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
     }
 }

Notez qu'il parcourt le option et appelle chaque décorateur/middleware.

Un exemple supplémentaire est ici: https://Gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9

Code exécutable: https://play.golang.org/p/ZXixnyTHXH

J'espère que cela a aidé :)

4
Steven Ferrer

Une manipulation de chaîne selon différentes options/instructions, par exemple, en haut, en bas, en ajoutant un préfixe/suffixe, à base64, etc., serait le meilleur exemple IMO.

Voici l'exemple que vous avez demandé:

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

Notez que pour des raisons de simplicité, il utilise le type aliases de Golang, qui a été introduit dans Go 1.9. Ce sont les deux alias que nous utilisons:

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

afin que votre fonction Decorate() puisse être appliquée ultérieurement. Ensuite, nous créons simplement quelques décorateurs (fonctions) qui correspondent à la signature de type que nous avons définie:

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

et appliquez-les:

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

MODIFIER:

Un décorateur paramétré serait simplement une fonction qui renvoie une autre fonction, par exemple:

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

Il pourrait ensuite être appliqué de la manière suivante:

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