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()
}
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)
}
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)
}
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()
}
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")
}
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()
}
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()
Top comments (1)
Does this work as a proxy?