Codementor Events

Design Patterns in Rust: Facade, hiding a complex world

Published Jun 03, 2023

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:
Facade.drawio.png

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.

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

cargo new rust_facade
cd rust_facade

Now open this directory in your favourite IDE and open the main.rs file in your src/ directory.

We will start by defining a Point struct:

struct Point {
    x: f32,
    y: f32,
}

impl Point {
    fn new(x: f32, y: f32) -> Point {
        Point { x: x, y: y }
    }
}

A Point simply gets two coordinates, X and Y. I also added a simple constructor.

The ShapeMaker struct will help us build a square:

struct ShapeMaker<'s> {
    p: &'s Point,
}

impl<'s> ShapeMaker<'s> {
    fn new(p: &'s Point) -> ShapeMaker<'s> {
        ShapeMaker { p: p }
    }

    fn create_square(&self,size: f32) -> Point {
        Point::new(self.p.x+size,self.p.y+size)
    }
}

The ShapeMaker struct just gets the starting point. It calculates the second point of the square in the create_square() method, by adding the size parameter, to both the X and Y coordinate of the point.

The ShapeMeasurer struct measures the distance between two points using the Pythogorean theorem:

struct ShapeMeasurer<'s> {
    first: &'s Point,
    second: &'s Point,
}

impl<'s> ShapeMeasurer<'s> {
    fn new(first: &'s Point, second: &'s Point) -> ShapeMeasurer<'s> {
        ShapeMeasurer { first: first, second: second }
    }

    fn get_distance(&self) -> f32 {
        let x = self.first.x - self.second.x;
        let y = self.first.y - self.second.y;
        (x*x + y*y).sqrt()
    }
}

Finally we have the SquareMeasurer which from a starting point and a given size, can measure the square’s diagonal:

struct SquareMeasurer<'s> {
    starting_point: &'s Point,
}

impl<'s> SquareMeasurer<'s> {
    fn new(starting_point: &'s Point) -> SquareMeasurer<'s> {
        SquareMeasurer { starting_point: starting_point }
    }

    fn measure_square_diagonal(&self, size: f32) -> f32 {
        let p = ShapeMaker::new(self.starting_point).create_square(size);
        ShapeMeasurer::new(self.starting_point, &p).get_distance()
    }
}

Now we can test it:

fn main() {
    let p = Point::new(3.0, 4.0);
    let sm = SquareMeasurer::new(&p);
    println!("Diagonal of a square with size 3 is {}", sm.measure_square_diagonal(3.0));
}

Line by line:

  1. We create a new point, and pass it on to the SquareMeasurer.
  2. By calling the measure_square_diagonal() method, the SquareMeasurer calls on the underlying classes to find the size of the diagonal.
  3. We print out the result. In our testcase it should be about 4.2

This is not the most difficult pattern to implement. As with most design patterns it just looks like common sense to hide complexity. In a later article I hope to provide you with a more complex example for this pattern.

Discover and read more posts from Iede Snoek
get started