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 Plant
s, 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;
}
}
Plant
s are then eaten by Herbivore
s, 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 Herbivore
s are eaten by Carnivore
s:
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);
}
}
Eventually the Carnivore
s 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.
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#!