1
\$\begingroup\$

I have this interface for all strategies:

type PrintStrategy interface {
    Print() error
}

of two types:

type ConsoleStrategy struct{}

type FileStrategy struct {
    DestinationFilePath string
}

with two concrete implementations of strategy:

func (c *ConsoleStrategy) Print() error {
    fmt.Println("ConsoleStrategy")
    lister, _ := template.New("foo").Parse(tplTemplate())
    lister.Execute(os.Stdout, tplParams())
    return nil
}

func (c *FileStrategy) Print() error {
    fmt.Println("FileStrategy")
    var t bytes.Buffer
    foo, _ := template.New("bar").Parse(tplTemplate())
    foo.Execute(&t, tplParams())

    f, err := os.Create(c.DestinationFilePath)
    if err != nil {
        panic(err)
    }
    defer f.Close()
    f.Write(t.Bytes())
    return nil
}

and finally a main function that gets the strategy from the console and prints the content built inside tplTemplate() to the console or a file.

func main() {
    strategy := flag.String("strategy", "console", "selected strategy")
    flag.Parse()

    var printStrategy PrintStrategy

    switch *strategy {
    case "console":
        printStrategy = &ConsoleStrategy{}
    case "file":
        printStrategy = &FileStrategy{"bigciao"}
    default:
        printStrategy = &ConsoleStrategy{}
    }

    printStrategy.Print()
}

Is this a good example of using the pattern?

\$\endgroup\$
4
\$\begingroup\$

According to the GoF, Strategy pattern:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.

See that the objective is disacouplished client (context object) of your possibles behaviours. So there is two basics elements in this pattern: context object and strategies.

The contexts objects have domain problems that must be separated from each other, so different algorithms. See in image(source in: https://www.tutorialspoint.com/design_pattern/strategy_pattern.htm):

UML diagram for Strategy Pattern

All this allow that you can work to an interface without worrying implementation strategy.

In your code there is no context object, so this is wrong. If there is no context object, the problem does not exist either.

Refactoring your code, we will have the following strategies:

// ----------------------------------------------
// in strategies.go files
// ----------------------------------------------

type PrintStrategy interface {
    Print() error
}

type ConsoleStrategy struct{}
func (c ConsoleStrategy) Print() error {
  fmt.Println("Hello, playground by console strategy")
  return nil
}

type FileStrategy struct {DestinationFilePath string}
func (c FileStrategy) Print() error {
  //print in file c.DestinationFilePath
  fmt.Println("Hello, playground by file strategy")
  return nil
}

type DefaultStrategy struct{}
func (c DefaultStrategy) Print() error {
  //print in printer device
  fmt.Println("Hello, playground by default strategy")
  return nil
}

And the following context object:

// ----------------------------------------------
// in context.go files
// ----------------------------------------------
type MyContextObject struct {
  ps PrintStrategy
}

func (m MyContextObject) Print(){
  //check if PrintingStrategy is not nil
  err := m.ps.Print()
  if err != nil {
    panic("Fail on PrintMyDatas")
  }
}

func NewMyContextObject(p PrintStrategy) MyContextObject{
  if (p != nil){
    return MyContextObject{p}
  } else {
    return MyContextObject{DefaultStrategy{}}
  }
}

See that on instancing of MyContextObject an behaviour is passed. In your case the behaviour for this context is provided by argument passing on executing. In another case you might like change the behaviour in according with the content size. These are different context using same strategy.

func main() {
    //read args
    strategy := flag.String("strategy", "console", "selected strategy")
    flag.Parse()

    //context object that needs to print your datas in according with
    //an user arg.
    var m MyContextObject

    //this statments can be replaced by factory method
    switch *strategy {
    case "console":
        m = NewMyContextObject(ConsoleStrategy{})
    case "file":
        m = NewMyContextObject(FileStrategy{"bigciao"})
    default:
        m = NewMyContextObject(nil)
    }
    //
    m.Print()
}

Now you can does more strategies or share this behaviour in other contexts. This is a good practical of reuse!!

This code is in https://play.golang.org/p/P-kGGOwDDRt.

I hope I helped you!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.