Let’s assume you are working with shapes and that, thus far, you’ve implemented a Square and a Circle with a getArea()
and a getPerimeter()
function:
type Shape interface {
getArea() float32
getPerimeter() float32
}
type Square struct {
side float32
}
func (s *Square) getArea() float32 {
return s.side * s.side
}
func (s *Square) getPerimeter() float32 {
return s.side * 4
}
func NewSquare(side float32) *Square {
s := &Square{side: side}
return s
}
type Circle struct {
radius float32
}
func (c *Circle) getArea() float32 {
return math.Pi * c.radius * c.radius
}
func (c *Circle) getPerimeter() float32 {
return 2 * math.Pi * c.radius
}
func NewCircle(radius float32) *Circle {
c := &Circle{radius: radius}
return c
}
However, you want to add color to your shapes and print the color for each shape. You would then find yourself with a complicated, exponentially growing tree of colored shapes like this:
If you keep adding shapes and colors, you would get a lot of different objects that behave similarly and are tedious, awful, and inefficient to escalate.
So, we are going to use the bridge pattern for this issue.
Let’s define a new color interface that will enforce a printColour()
method on the implementing structs:
type Colour interface {
printColour()
}
We can then create a new color structure that will implement the printColour()
method in its own way:
type RedColour struct{}
func (r *RedColour) printColour() {
fmt.Println("I'm Red!")
}
type BlueColour struct {
}
func (b *BlueColour) printColour() {
fmt.Println("I'm Blue!")
}
So, now, we can actually add the color attribute to our shapes and the color methods we want to impose on our shape interface:
type Shape interface {
getArea() float32
getPerimeter() float32
getColour()
setColour(colour Colour)
}
Let’s go back to our shape’s structs and implement the methods:
func (s *Square) setColour(colour Colour) {
s.colour = colour
}
func (s *Square) getColour() {
fmt.Print("Square says: ")
s.colour.printColour()
}
func (c *Circle) setColour(colour Colour) {
c.colour = colour
}
func (c *Circle) getColour() {
fmt.Print("Circle says: ")
c.colour.printColour()
}
Right now, our program will look something like this:
package mainimport "fmt"import "math"/*Bridge:A mechanism that decouples an interface (hierarchy)from an implementation (hierarchy)*/type Shape interface {getArea() float32getPerimeter() float32getColour()setColour(colour Colour)}type Square struct {side float32colour Colour}func (s *Square) getArea() float32 {return s.side * s.side}func (s *Square) getPerimeter() float32 {return s.side * 4}func (s *Square) setColour(colour Colour) {s.colour = colour}func (s *Square) getColour() {fmt.Print("Square says: ")s.colour.printColour()}func NewSquare(side float32, colour Colour) *Square {s := &Square{side: side}s.setColour(colour)return s}type Circle struct {radius float32colour Colour}func (c *Circle) getArea() float32 {return math.Pi * c.radius * c.radius}func (c *Circle) getPerimeter() float32 {return 2 * math.Pi * c.radius}func (c *Circle) setColour(colour Colour) {c.colour = colour}func (c *Circle) getColour() {fmt.Print("Circle says: ")c.colour.printColour()}func NewCircle(radius float32, colour Colour) *Circle {c := &Circle{radius: radius}c.setColour(colour)return c}type Colour interface {printColour()}type RedColour struct{}func (r *RedColour) printColour() {fmt.Println("I'm Red!")}type BlueColour struct {}func (b *BlueColour) printColour() {fmt.Println("I'm Blue!")}func main() {r := &RedColour{}b := &BlueColour{}s := NewSquare(4.2, r)c := NewCircle(6.9, b)fmt.Println("Square's area is:", s.getArea())fmt.Println("Square's perimeter is:", s.getPerimeter())s.getColour()s.setColour(b)s.getColour()fmt.Println()fmt.Println("Circle's area is:", c.getArea())fmt.Println("Circle's perimeter is:", c.getPerimeter())c.getColour()c.setColour(r)c.getColour()}
And that’s it for today! I hope you liked it and that it proves useful for your projects.
Check out the four other design patterns in GO:
Happy coding!
Free Resources