Skip to content
Carmel Eve By Carmel Eve Software Engineer I
Design patterns in C# - The Facade Pattern

Continuing my series on design patterns, this week we're focusing on the facade pattern!

The facade pattern is a useful for providing a simple interface into a complex system. It doesn't provide access to all parts of the system, it provides a limited set of functionality and hides any underlying complexity.

The facade may orchestrate a host of underlying operations, but present to the outside world a simple singular method.

As always, I think this will be simpler if we look at an example:

Facade Example

So, say we have a complex(ish) ecosystem, everything starts with the nutrients in the soil:

public class Soil
{
    public int NutrientCount;

    public Soil(int nutrientCount)
    {
        Console.WriteLine($"The soil has {nutrientCount} nutrient(s)");
        this.NutrientCount = nutrientCount;
    }

    public void AddNutrient(Edible edible)
    {
        Console.WriteLine("The soil gains nutrients");

        edible.Eat();
        NutrientCount++;

        Console.WriteLine($"Nutrient count: {NutrientCount}");
    }
    
    public void UseNutrient()
    {
        if (NutrientCount <= 0)
        {
            throw new Exception("No nutrients left!");
        }

        NutrientCount--;

        Console.WriteLine($"Nutrient count: {NutrientCount}");
    }
}

Nutrients can be added / removed from the soil by other things in the system.

We then have Plants, which use the nutrients in order to grow:

public class Plant : Edible
{
    public Plant(Soil soil)
    {
        Console.WriteLine("A new plant grows");
        soil.UseNutrient();
        this.IsEaten = false;
    }
}

Where plants implement the Edible abstract base class:

public abstract class Edible
{
    public bool IsEaten;

    public void Eat()
    {
        if (IsEaten)
        {
            throw new Exception("Already eaten!");
        }
        IsEaten = true;
    }
}

Plants are then eaten by Herbivores, which themselves also implement the Edible base class:

public class Herbivore : Edible
{
    public Herbivore(Plant plant)
    {
        Console.WriteLine("The herbivore eats a plant.");
        plant.Eat();
        IsEaten = false;
    }
}

And in turn these Herbivores are eaten by Carnivores:

public class Carnivore : Edible
{
    public Carnivore(Herbivore herbivore)
    {
        Console.WriteLine("The carnivore eats the herbivore");
        herbivore.Eat();
        IsEaten = false;
    }

    public void Die(Soil soil)
    {
        Console.WriteLine("The carnivore dies");
        soil.AddNutrient(this);
    }
}
The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

Eventually the Carnivores Die, and return the nutrients to the soil.

Now, our caller wants to run this ecosystem (starting with soil that is enriched with a single nutrient), up until the point of worldwide extinction, like so:

public static void FacadeExample()
{
    bool meteor = false;
    var soil = new Soil(1);

    while (!meteor)
    {
        var plant = new Plant(soil);
        var herbivore = new Herbivore(plant);
        var carnivore = new Carnivore(herbivore);
        carnivore.Die(soil);

        Console.WriteLine("Has a meteor hit? (Y/N)");
        var response = Console.ReadLine();
        if (response == "Y")
        {
            meteor = true; 
        }
    }
    Console.WriteLine("A meteor has hit and destroyed the ecosystem!");
}

But it doesn't really compare about the complexities of running the ecosystem itself, it just wants to handle the higher level, catastrophic, extinction events. Therefore we can hide this complexity behind a facade:

public class DinoEcosystemFacade
{
    private readonly Soil soil;

    public DinoEcosystemFacade(Soil soil)
    {
        this.soil = soil;
    }

    public void RunAGeneration()
    {
        var plant = new Plant(this.soil);
        var herbivore = new Herbivore(plant);
        var carnivore = new Carnivore(herbivore);
        carnivore.Die(this.soil);
    }
}

The calling code then just has to run a generation, before checking for those inconvienient mass extinctions:

public static void FacadeExample()
{
    bool meteor = false;
    var dinoEcosystemFacade = new DinoEcosystemFacade(new Soil(1));

    while (!meteor)
    {
        dinoEcosystemFacade.RunAGeneration();

        Console.WriteLine("Has a meteor hit? (Y/N)");
        var response = Console.ReadLine();
        if(response == "Y")
        {
            meteor = true;
        }
    }
    Console.WriteLine("A meteor has hit and destroyed the ecosystem!");
}

If we run the program, then the output of the above program will be as follows:

The soil has 1 nutrient(s)
A new plant grows
Nutrient count: 0
The herbivore eats a plant.
The carnivore eats the herbivore
The carnivore dies
The soil gains nutrients
Nutrient count: 1
Has a meteor hit? (Y/N)
N
A new plant grows
Nutrient count: 0
The herbivore eats a plant.
The carnivore eats the herbivore
The carnivore dies
The soil gains nutrients
Nutrient count: 1
Has a meteor hit? (Y/N)
N
A new plant grows
Nutrient count: 0
The herbivore eats a plant.
The carnivore eats the herbivore
The carnivore dies
The soil gains nutrients
Nutrient count: 1
Has a meteor hit? (Y/N)
Y
A meteor has hit and destroyed the ecosystem!

Without the calling code needing to know about how the ecosystem is run under the covers!

In practice this pattern in generally used when there are a large number of classes or subsystems involved in a process, or when the source code for the process isn't available.

Programming C# 12 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

Thanks for reading! Here's a link to the GitHub repository which contains all the code for this blog series. And watch of for my next blog on design patterns in C#!

Carmel Eve

Software Engineer I

Carmel Eve

Carmel is a software engineer and LinkedIn Learning instructor. She worked at endjin from 2016 to 2021, focused on delivering cloud-first solutions to a variety of problems. These included highly performant serverless architectures, web applications, reporting and insight pipelines, and data analytics engines. After a three-year career break spent travelling around the world, she rejoined endjin in 2024.

Carmel has written many blog posts covering a huge range of topics, including deconstructing Rx operators, agile estimation and planning and mental well-being and managing remote working.

Carmel has released two courses on LinkedIn Learning - one on the Az-204 exam (developing solutions for Microsoft Azure) and one on Azure Data Lake. She has also spoken at NDC, APISpecs, and SQLBits, covering a range of topics from reactive big-data processing to secure Azure architectures.

She is passionate about diversity and inclusivity in tech. She spent two years as a STEM ambassador in her local community and taking part in a local mentorship scheme. Through this work she hopes to be a part of positive change in the industry.

Carmel won "Apprentice Engineer of the Year" at the Computing Rising Star Awards 2019.