9

Here's the the general problem I am trying to solve:

  1. One set of packages is collecting data from one source and sending it to many channels.
  2. A second set of packages is collecting data from many channels and writing it to one source. This set of packages would need to translate data from multiple formats.

This seems like a perfect case for the Strategy Pattern but am not sure how best to make that work in Go.

KevDog
  • 5,763
  • 9
  • 42
  • 73

3 Answers3

22

In general; don't get lost in patterns and protocols to build Go applications. The language makes it easy to be expressive and straightforward. Most of the time defining solid interfaces makes everything flexible enough.

Still, here's an example of the strategy pattern in Go:

Define an interface for the behavior of the strategies:

type PackageHandlingStrategy interface {
    DoThis()
    DoThat()
}

Implement that strategy:

type SomePackageHandlingStrategy struct {
    // ...
}

func (s *SomePackageHandlingStrategy) DoThis() {
    // ...
}

func (s *SomePackageHandlingStrategy) DoThat() {
    // ...
}

And then, either embed…

type PackageWorker struct {
    SomePackageHandlingStrategy
}

func (w *PackageWorker) Work() {
    w.DoThis()
    w.DoThat()
}

…or pass the strategy…

type PackageWorker struct {}

func (w *PackageWorker) Work(s PackageHandlingStrategy) {
    s.DoThis()
    s.DoThat()
}

… to your worker.

thwd
  • 23,956
  • 8
  • 74
  • 108
  • Excellent answer, thanks! Wasn't so much getting lost in patterns as I knew this was a solved problem in some fashion. Thanks again. – KevDog Jul 22 '13 at 14:59
  • 1
    But how to do you load the strategy dynamically? If I need to load a strategy based on configuration in the site, how is that done in go? From what I see there is no way to load a package dynamically in golang. Or is there another way? – Madhan Ganesh Apr 08 '16 at 16:40
  • 2
    @Madhan using a switch statement. – thwd Apr 13 '16 at 15:59
  • @madhan You can make a factory of strategies. Then pass the strategy name from the config, the factory will create the instance of the strategy you need. Factory can be implemented with a switch or map[string]Strateger. – Dr.eel Oct 31 '19 at 18:19
1

@madhan You can make a factory of strategies. Then pass the strategy name from the config, the factory will create the instance of the strategy you need. Factory can be implemented with switch or map[string]Strateger.

Dr.eel
  • 1,837
  • 3
  • 18
  • 28
0

Please see another example below.

// Duck embeds IQuack interface
// Client for IQuack behavior
type Duck struct {
    IQuack
}

// IQuack is interface for quacking behavior (Behavior)
type IQuack interface {
    Quack() string
}

// NoQuacking implements IQuack interface and does not quack.
// Concrete Behavior
type NoQuacking struct{}

// Quack returns "I cannot Quack"
func (i NoQuacking) Quack() string {
    return "I cannot Quack :("
}

// LoudQuacking implements IQuack interface and is for ducks that quack loudly.
// Concrete Behavior
type LoudQuacking struct{}

// Quack returns "I Quack loudly"
func (i LoudQuacking) Quack() string {
    return "I Quack loudly!!"
}

// ShyQuacking implements IQuack interface and is for ducks that are shy.
// Concrete Behavior
type ShyQuacking struct{}

// Quack returns "I do a shy quack"
func (i ShyQuacking) Quack() string {
    return "I do a shy quack :)"
}

func main() {
    rubberDuck := Duck{NoQuacking{}}
    fmt.Println(rubberDuck.Quack)

    wildDuck := Duck{LoudQuacking{}}
    fmt.Println(wildDuck.Quack)
}

In the example, the Duck is the client.

It has an IQuack behavior (interface). NoQuacking, LoudQuacking, ShyQuacking are the algorithms/concrete-behavior that implement the IQuack interface.

A Duck can thus quack() using the NoQuacking / loudQuacking / ShyQuacking algorithm interchangeably.

Aaditi Jain
  • 6,969
  • 2
  • 24
  • 26