DEV Community

Shubham Sethi
Shubham Sethi

Posted on

Strategy Pattern in Go

Imagine you have to build a program that simulates birds.

There can be different type of birds with different flying behaviours.

A parrot that flies low , an eagle that flies high and ostrich which doesn’t fly at all

We will start with Bird interface.

// Interface
type Bird interface {
    Fly()
    Display()
}
Enter fullscreen mode Exit fullscreen mode

So anything that implements Fly() and Display() is a Bird.

A parrot

type Parrot struct {
    color string
}

func (p *Parrot) Fly() {
    fmt.Println("I can fly")
}

func (p *Parrot) Display() {
    fmt.Printf("I am a Parrot . I have color =%s", p.color)
}
Enter fullscreen mode Exit fullscreen mode

or an Eagle

type Eagle struct {
    color string
}

func (p *Eagle) Fly() {
    fmt.Println("I can fly high")
}

func (p *Eagle) Display() {
    fmt.Printf("I am a Eagle . I have color =%s", p.color)
}
Enter fullscreen mode Exit fullscreen mode

As you can see the Fly() is common mechanism for lot of bids, so we can move this into a common construct.

In Java, we would create an abstract class, which has some common method implemented and later extend the abstract class to implement varying behaviour.

In Go we can do so be creating a type that partially implements Fly() method

//abstracting out just flying behaviour

type FlyStrategy struct {
    doFly FlyBehaviour
}

func (f *FlyStrategy) Fly() {
    f.doFly()
}

Enter fullscreen mode Exit fullscreen mode

We extract out how a bird flies as FlyBehaviour

type FlyBehaviour func()

func FlywithWings() {
   fmt.Println("I am  flying with my wings")
}

func FlyNoWay() {
   fmt.Println("I can't fly")
}

func FlyHigh(){
   fmt.Println("I am  flying high")
}
Enter fullscreen mode Exit fullscreen mode

Now to extend this behaviour in Java we would use extend but Go doesn't support inheritance in the classical sense; instead, in encourages composition as a way to extend the functionality of types. Go supports embedding of structs and interfaces to express a more seamless composition of types.

So to extend FlyStrategy we will simply embed it inside Parrot type

// Implementation of Parrot with FlyStrategy embed
type Parrot struct {
    color string
    FlyStrategy
}

func (p *Parrot) Display() {
    fmt.Printf("I am a Parrot . I have color = %s", p.color)
}

func main() {

    var greenParrot Bird

    greenParrot = &Parrot{
        color:       "green",
        FlyStrategy: FlyStrategy{doFly: FlywithWings},
    }

    greenParrot.Fly()
    greenParrot.Display()

}
Enter fullscreen mode Exit fullscreen mode

Advantage of this pattern is that we can dynamically update flying strategy .

// Interface
type Bird interface {
    Fly()
    Display()
//new
    UpdateFlyStrategy(flyb FlyBehaviour)
}

....
....

type FlyStrategy struct {
    doFly FlyBehaviour
}

func (f *FlyStrategy) Fly() {
    f.doFly()
}

//new
func (f *FlyStrategy) UpdateFlyStrategy(flyb FlyBehaviour) {
    f.doFly = flyb
}

....
....

greenParrot.UpdateFlyStrategy(FlyHigh)
greenParrot.Fly()
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
parmcoder profile image
Possawat Sanorkam

Does this work as a proxy?