2017-08-29 5 views
3

Dekorierermuster (Funktionen) hat many benefits:Decorator Funktionen in Go

Es ist sehr nützlich, wenn ein Verfahren viele orthogonale Bedenken hat ... Das heißt, keiner dieser Bedenken beziehen sich, anders als das wir tun wollen alle (oder einige) von ihnen, wann immer wir unsere Methode nennen. Dies ist, wo das Decorator-Muster wirklich hilft.

Durch die Implementierung des Decorator-Musters haben wir den Open-Closed-Principal. Unsere Methode ist offen für zukünftige Erweiterungen, aber für zukünftige Änderungen geschlossen. Es gibt viele groovige Vorteile, um dem Prinzip "offen-geschlossen" zu folgen.

Alle Beispiele, die ich gefunden habe, sind jedoch sehr kompliziert (z. B. HTTP-Server mit vielen Middlewares schreiben). Dies macht es mir schwer, das Prinzip woanders anzuwenden. Ich brauche etwas, das ich leicht anprobieren kann, um meinen Kopf darum zu wickeln.

Kann jemand mir ein einfacheres Beispiel geben, das am besten veranschaulichen kann, wie man Dekoratormuster (Funktionen) in Go bitte tut?

This example by Alex Alehano, ist zu einfach, um praktisch verwendet zu werden. Ich brauche etwas, das dies illustriert:

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

Eine String-Manipulation nach verschiedener Option/Anweisung, zum Beispiel an den oberen, zu niedrigeren, zu base64, etc, würde IMO das beste Beispiel sein, und das Hinzufügen von Präfix/Suffix auch als "This technique proves especially valuable if the decorators themselves are parameterized".

Antwort

4

Zunächst einmal ein Dekorateur ist im Grunde eine Funktion, die eine andere Funktion eines bestimmten Typs als Argument und gibt eine Funktion der gleichen Art. Dies ermöglicht Ihnen im Wesentlichen, eine Kette von Funktionen zu erstellen. Also gehen würde wie folgt aussehen:

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

here's a more complete example

+0

Dies ist genau das Dekorator-Funktionen-Muster in Go, das ich sehr gesucht habe! – xpt

+0

Wenn ich richtig verstehe, 'AppendDecorator', wie es definiert ist, kann nur als erster Vorgang zum Anhängen verwendet werden? – RayfenWindspear

+0

https://play.golang.org/p/rrBnAJOIc2 nein, 'AppendDecorator' ist ein wenig anders als die anderen, in denen Sie es aufrufen müssen, um den tatsächlichen Dekorator zurückzugeben, der dann mit der gewünschten Funktion aufgerufen wird schmücken. Im Grunde erstellt es nur einen Abschluss für den String, den Sie an "AppendDecorator" übergeben und gibt einen neuen Decorator zurück, der dann Zugriff auf diesen String hat ... – mkopriva

2

Ich kenne ein sehr gutes Beispiel von Dekorateuren/Middlewares, die die gleiche Technik verwenden, die Sie demonstriert haben. Dies ist allerdings auf String-Manipulation nicht spezifisch, aber ich finde es wirklich schön gestaltete, hier ist es (durch den Code scannen):

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

Werfen Sie einen Blick auf die type PublisherOption func(*publisherConfig) error

Sie feststellen, dass Einige der Funktionen geben PublisherOption Typ zurück. Dies sind die Dekoratoren/Middleware.

Die Aktion geschieht auf NewKafkaPublisher Funktion:

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

Beachten Sie, dass es die option und ruft jeden Dekorateur/Middleware-Schleifen durch.

Ergänzungsbeispiel ist hier: https://gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9

Runnable Code: https://play.golang.org/p/ZXixnyTHXH

Hope that :) half

+0

Die schwer für mich ist das Prinzip anderswo anzuwenden. Wenn ich den von Ihnen zitierten Code betrachte, kann ich ihn leider nicht besser verstehen und ihn für meine eigenen Probleme anwenden. Es ist immer noch zu kompliziert für mich, meinen Kopf darum zu wickeln. – xpt

+0

Ich habe ein einfaches Beispiel erstellt, siehe bitte: https://gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9 – srf

+0

Danke für Ihre Hilfe. Upvoiert! – xpt

3

Eine String-Manipulation nach verschiedener Option/Anweisung, zum Beispiel an den oberen, zu senken Das Hinzufügen von Präfix/Suffix zu Base64 usw. wäre das beste Beispiel für IMO.

Dies ist das Beispiel, das Sie gefragt:

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

Beachten Sie, dass aus Gründen der Einfachheit, es Golang die verwendet Typ Aliase, die in Go 1.9 eingeführt wurden. Dies sind die beiden Aliase wir verwenden:

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

, so dass Ihre Decorate() Funktion später angewendet werden könnte. Dann schaffen wir nur wenige Dekorateure (Funktionen), die die Art Signatur entsprechen wir definiert:

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

und wenden sie:

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

EDIT:

A parametrisiert Dekorateur würde einfach eine Funktion sein, die eine andere Funktion zurückgibt, zum Beispiel:

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

Es könnte dann in der folgenden Art und Weise angewendet werden:

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

wow, schön, wusste nicht, es gab Typ Aliase :) – srf

+0

@srxf Typ Aliase wurden nur in Go 1.9 hinzugefügt, letzte Woche veröffentlicht. – Adrian

+0

genial, ich muss mein Selbst aktualisieren. Danke @Adrian! – srf