Design Patterns in Go: Facade, hiding a complex world

Introduction

The facade pattern is used as a way to hide more complex logic. A facade can do the following:

  • Improve usability of a library or API by masking interaction with the more complex components of the underlying code.
  • Provide interfaces which are context-specific. That means that each client of an API could get its own facade, depening on use-cases.

A facade pattern is typically used in the following cases:

  • A subsystems, or more subsystems have tightly coupled elements.
  • In layered software, you might need an entry point for each layer.
  • In the case of very complex systems, a facade can help

What does it look like? Well, it is quite simple:

The Façade class has a simple interface hiding the fact that it might have more complex interaction with for example classA and classB. This example is simple, but you can imagine that in complex systems, there are tens or hundreds of classes with which the Façade has to interact.

Implementation in Go

In an empty directory open your terminal or commandline, and type:

go mod init github.com/facade_pattern

Now open this directory in your favourite IDE and add a main.go. Add this to the main.go file:

package main

import (
	"fmt"
	"math"
)

In this example we will create a struct which measure the diagonal of a square. I must admit this use case is rather contrived, but it works to make my point.

We will start by defining a Point struct:

type Point struct {
	X, Y int
}

Nothing out of the ordinary, just a point with an X and Y coordinate.

Now we to be able to construct a square on that:

type ShapeMaker struct {
	p Point
}

func (sm *ShapeMaker) CreateSquare(size int) Point {
	return Point{sm.p.X + size, sm.p.Y + size}
}

The ShapeMaker struct gets a point, and the CreateSquare() method, constructs a square, simple by adding the given size to both the X and Y coordinates and returning the resulting Point.

Now we need to be able to measure the distance between two points:

type ShapeMeasurer struct {
	first  Point
	second Point
}

func (sm *ShapeMeasurer) MeasureDistance() float64 {
	xDistance := math.Pow(float64(sm.first.X-sm.second.X), 2)
	yDistance := math.Pow(float64(sm.first.Y-sm.second.Y), 2)
	return math.Sqrt(xDistance + yDistance)
}

The ShapeMeasurer class gets two points, and the MeasureDistance() method calculates the Pythogarean distance between the two points.

Now we come to the facade bit of this code, the SquareMeasurer struct:

type SquareMeasurer struct {
	startingPoint Point
}

func (sm *SquareMeasurer) MeasureSquareDiagonal(size int) float64 {
	shapeMaker := ShapeMaker{sm.startingPoint}
	secondPoint := shapeMaker.CreateSquare(size)
	shapeMeasurer := ShapeMeasurer{sm.startingPoint, secondPoint}
	return shapeMeasurer.MeasureDistance()
}

The SquareMeasurer just gets the starting point of the square. In the MeasureSquareDiagonal() method, which has a size as a parameter, the secondpoint is calculated, after which the distance between the two is calculated.

This example is rather contrived, but I hope it gets my point across.

Now it is time to test this:

func main() {
	squareMeasurer := SquareMeasurer{Point{3, 4}}
	fmt.Println(squareMeasurer.MeasureSquareDiagonal(3))
}

Line by line:

  1. A SquareMeasurer struct is constructed with starting point (3,4)
  2. Then the MeasureSquareDiagonal is calculated and printed. Since the size is 3, it should print 4.24….

Conclusion

This is not the most difficult pattern to implement. As with most Design Patterns, it is just common sense. In a later article I hope to provide you with a more complex example for this pattern.

Leave a Reply

Your email address will not be published. Required fields are marked *